In order to ensure the stable and reliable operation of the system, the input data must be strictly verified to prevent some illegal abnormal data from causing errors or even crashes in the subsequent processing process of the system. At the same time, in case of verification failure, clear and friendly error information needs to be output for the development and debugging of the docking party and the troubleshooting of abnormal operation after going online.
The verification work mainly includes the following contents:
1. Verifying the properties of the message object, such as whether it is empty and the format is correct, belongs to basic verification
2. Verify whether the message has been received. If so, no subsequent processing will be performed. This is the key to ensure idempotency and de duplication of the message
3. Verify that the message subject code exists and is available
4. Verify whether the application code exists and is available
5. Verify the permission to ensure that the application is controlled by the function permission of the message subject
6. Verify the timeliness. The agreed message receiving time cannot differ from the request time by more than 10 minutes, otherwise the service will be rejected
In addition, there is a special point about the data verification of the request message that affects the process processing, that is, as mentioned above, when the receiver of the request message, whether the client or the server, receives a duplicate message, in addition to stopping the subsequent processing, it also needs to send a response to the requester. There are two schemes: one is to throw an exception, Inform the requester that the message has been received; The second is to inform the requester that the message has been processed successfully. The former scheme will cause the response result of the request message in the message log table to be abnormal, but the real situation is that the message has been processed successfully, so the latter scheme is better.
According to the principle of code reuse, the common verification code is placed in the abstract parent class MessageHandler of the message processor
/** * Verify message properties * * @param message news */ protected void validateProperty(BaseMessage message) { String errorCode; String errorMessage; // id if (com.baomidou.mybatisplus.core.toolkit.StringUtils.isBlank(message.getId())){ errorCode = "S001"; errorMessage = "Message ID cannot be empty"; throw new MessageException(errorCode, errorMessage); } // theme if (com.baomidou.mybatisplus.core.toolkit.StringUtils.isBlank(message.getTopic())) { errorCode = "S002"; errorMessage = "Message subject cannot be empty"; throw new MessageException(errorCode, errorMessage); } // Publisher if (com.baomidou.mybatisplus.core.toolkit.StringUtils.isBlank(message.getPublishAppCode())) { errorCode = "S003"; errorMessage = "The message publisher cannot be empty"; throw new MessageException(errorCode, errorMessage); } // Release time String publishTimeString = message.getPublishTime(); if (com.baomidou.mybatisplus.core.toolkit.StringUtils.isBlank(publishTimeString)) { errorCode = "S004"; errorMessage = "Publishing time cannot be empty"; throw new MessageException(errorCode, errorMessage); } else if (ValidateUtil.dateIsNotFormat(publishTimeString)) { errorCode = "S005"; errorMessage = "Incorrect publishing time format"; throw new MessageException(errorCode, errorMessage); } } /** * Verify permissions * * @param publishAppCode Application coding * @param topicCode Subject code */ protected void validatePermission(String publishAppCode, String topicCode) { boolean hasPermission = apiMessagePermissionService.checkPermission(publishAppCode, topicCode); if(hasPermission==false){ throw new MessageException("301", "Application has no permission"); } } /** * Verify timeliness * * @param publishTimeString Publishing time string */ protected void validatePublishTimeValid(String publishTimeString) { // The data validation phase has verified that it can be converted. The conversion exception will not be processed here SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date publishTime = null; try { publishTime = dateFormat.parse(publishTimeString); } catch (ParseException e) { // The pre sequence phase has verified the date format. No exception will be thrown here. It is only for compilation } // Get the current system time Date currentTime = new Date(); // Comparison time long diff = Math.abs(currentTime.getTime() - publishTime.getTime()); //Maximum allowable time difference in milliseconds int maxTimeSpan = 10*60*1000; if (diff > maxTimeSpan) { // Request time out of reasonable range (10 minutes) throw new MessageException("S401", "The release time is beyond a reasonable range"); } } /** * Verification application * * @param publishAppCode Application coding */ protected void validateAppCode(String publishAppCode) { try { ApiApp app =apiAppService.getByCode(publishAppCode); if(app.getStatus().equals(StatusEnum.DEAD.name())){ throw new MessageException("S202", "App disabled"); } }catch (Exception ex){ throw new MessageException("S201", "Invalid app ID"); } } /** * Verify subject code * * @param topicCode Subject code */ protected void validateTopic(String topicCode) { try { ApiMessageTopic messageTopic = apiMessageTopicService.getByCode(topicCode); if(messageTopic.getStatus().equals(StatusEnum.DEAD.name())){ throw new MessageException("S102", "Message subject not available"); } }catch (Exception ex){ throw new MessageException("S101", "Message subject does not exist"); } } /** * Check whether the message has been received (for de duplication). false is returned by default and overridden by subclasses * @param id Message ID * @return true Yes false no */ protected boolean checkIfReceived(String id) { return false; }
The request message processor, RequestMessageHandler, inherits from the MessageHandler and is mainly used to call processing processes and methods
/** * Message processing * * @param message news * @param channel passageway */ public void handleMessage(RequestMessage requestMessage, Channel channel) { //Verify message properties validateProperty(requestMessage); //Verify whether the message has been received. If yes, send a successful response without subsequent processing boolean hasReceived = checkIfReceived(requestMessage.getId()); if(hasReceived){ //Send response sendResponse(channel, MessageResponseResultEnum.SUCCESS.name(), "", requestMessage.getId(), requestMessage.getTopic()); return; } // Message subject verification (whether it exists and is available) validateTopic(requestMessage.getTopic()); // Application verification (whether it exists and is available) validateAppCode(requestMessage.getPublishAppCode()); // Permission verification validatePermission(requestMessage.getPublishAppCode(),requestMessage.getTopic()); // Timeliness verification validatePublishTimeValid(requestMessage.getPublishTime()); //The request message status is set to no need to send by default requestMessage.setStatus(MessageStatus.NOT_TO_REQUEST.name()); // Log message requests saveLog(requestMessage); //Special treatment messageOperation(requestMessage, channel); //Send response sendResponse(channel, MessageResponseResultEnum.SUCCESS.name(), "", requestMessage.getId(), requestMessage.getTopic()); //Message processing (copy and forward) repostMessage(requestMessage); } /** * Verify that the request message has been received */ @Override protected boolean checkIfReceived(String id) { return apiMessageLogService.checkRequestMessageExist(id); }
The response message processor ResponseMessageHandler inherits from the MessageHandler, and the logic is simpler
/** * Message processing * * @param message news * @param channel passageway */ public void handleMessage(ResponseMessage responseMessage, Channel channel) { //Verify message properties validateProperty(responseMessage); //Verify whether the message has been received. If so, no subsequent processing will be carried out boolean hasReceived = checkIfReceived(responseMessage.getId()); if(hasReceived){ return; } // Message subject verification (whether it exists and is available) validateTopic(responseMessage.getTopic()); // Application verification (whether it exists and is available) validateAppCode(responseMessage.getPublishAppCode()); // Permission verification validatePermission(responseMessage.getPublishAppCode(),responseMessage.getTopic()); // Timeliness verification validatePublishTimeValid(responseMessage.getPublishTime()); // Update message log updateLog(responseMessage); //Special treatment messageOperation(responseMessage, channel); } /** * Verify that the response message has been received */ @Override protected boolean checkIfReceived(String id) { return apiMessageLogService.checkResponseMessageExist(id); }
Note that the implementation of the method of verifying whether the message has been received by the above two processors is different, and the properties of the message properties are not the same.