Message service imitating Taobao open platform -- server message verification

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.

Keywords: Java server

Added by tryin_to_learn on Fri, 14 Jan 2022 19:36:55 +0200