SpringClould microservice Architecture Construction -- unified response, input parameter verification and exception handling

preface

For an interface provided by the back end, it is essential to have a unified response format to facilitate parameter verification and unified exception handling. Today, we integrate these three basic functions into the project to make the project closer to the actual development scenario.

Unified response

In project development, generally, what is returned to the front end will be a unified return response object. Therefore, the back end needs to encapsulate a generic class as the response object. The advantage of this is that the front and back ends can return through a unified interface and can do standardized response processing.
Implementation steps:

  1. Create mingx common microservices to handle public services, including unified response, unified exception interception, tool classes, etc.
  2. Create a new com.mingx.common package under src/mian/java, and create it under this package
    The functions of appresult.java, appresultbuilder.java and resultcode.java files are as follows:
  • AppResult.java: a class that specifies the format of the returned result
package com.mingx.common;

public class AppResult<T> {

    private int code;
    private String msg;
    private T data;//  data

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}
  • AppResultBuilder.java: a class that facilitates the return of format assembly
package com.mingx.common;

public class AppResultBuilder {

    private static Integer successCode = ResultCode.SUCCESS.getCode();
    private static String successMsg = ResultCode.SUCCESS.getMsg();

    //Success, no specific data returned
    public static AppResult<String> success(){
        AppResult<String> result = new AppResult<String>();
        result.setCode(successCode);
        result.setMsg(successMsg);
        result.setData("");
        return result;
    }
    //Success, return data
    public static <T> AppResult<T> success(T t){
        AppResult<T> result = new AppResult<T>();
        result.setCode(successCode);
        result.setMsg(successMsg);
        result.setData(t);
        return result;
    }

    //Failed, return failure information
    public static <T> AppResult<T> error(ResultCode code){
        AppResult<T> result = new AppResult<T>();
        result.setCode(code.getCode());
        result.setMsg(code.getMsg());
        return result;
    }

    //Failed, return failure information
    public static <T> AppResult<T> error(ResultCode code,String extraMsg){
        AppResult<T> result = new AppResult<T>();
        result.setCode(code.getCode());
        result.setMsg(code.getMsg() + "," + extraMsg);
        return result;
    }

    //Failed, return failure information
    public static <T> AppResult<T> error(Integer code,String extraMsg){
        AppResult<T> result = new AppResult<T>();
        result.setCode(code);
        result.setMsg(extraMsg);
        return result;
    }

}
  • ResultCode.java: enumeration class that defines the return information
package com.mingx.common;

public enum ResultCode {
    /* Success status code  */
    SUCCESS(10000, "success"),
    /* System error:  */
    SYSTEM_ERROR(10001, "The system is busy, please try again later"),
    /* Parameter error:  */
    PARAM_ERROR(10002, "Parameter error"),
    /* Illegal login:*/
    ILLEGAL_ERROR(10003, "Illegal user login"),

    /* User module: 20001-29999*/
    USER_NOT_LOGGED_IN(20001, "User not logged in"),
    USER_LOGIN_ERROR(20002, "The user name or password is incorrect. Please check and try again"),
    USER_ACCOUNT_FORBIDDEN(20003, "Account has been disabled"),
    USER_HAS_EXISTED(20004, "User already exists");

    private Integer code;
    private String msg;

    ResultCode(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

Input parameter verification

In normal projects, it is inevitable to check the correctness of some parameters. These checks appear in the business code, which makes our business code look bloated. Moreover, it is boring to write such parameter verification codes frequently. In view of this, I think the Hibernate Validator framework just solves these problems. It can implement parameter verification in an elegant way, separate business code from verification logic, and no longer write duplicate verification logic.

Hibernate Validator is the reference implementation of Bean Validation. Hibernate Validator provides the implementation of all built-in constraints in JSR 303 specification. In addition, there are some additional constraints.

Bean Validation defines the corresponding metadata model and API for JavaBean validation. The default metadata is Java Annotations. The original metadata information can be overwritten and extended by using XML. Bean Validation is a runtime data validation framework. After validation, the validation error information will be returned immediately.

Preparation steps:

  1. Add hibernate validator master version management in the parent pom
<hibernate-validator.version>6.0.14.Final</hibernate-validator.version>

<!-- hibernate validator Master version management -->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>${hibernate-validator.version}</version>
</dependency>

      2. Create a new com.mingx.common package under src/mian/java. Under this package, create regexpcontainers interface for common regular expression management, and add it according to the actual situation of the project.

package com.mingx.common;

/**
 * General regular expression
 *
 * @author Admin
 */
public interface RegexpContants {

    /**
     * Nullable tag
     */
    String NULLFLAG = "^$|";

    /**
     * Mobile phone regular expression
     */
    String MOBIL_EREGEXP = "^(13|14|15|16|17|18|19)\\d{9}$";

    /**
     * Mailbox regular expression
     */
    String EMAIL_EREGEXP = "^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$";

    /**
     * ID card regular expression
     */
    String ID_CARD_EREGEXP = "(^\\d{15}$)|(^\\d{18}$)|(^\\d{17}(\\d|X|x)$)";

    /**
     * Fixed line regular expression
     */
    String TELEPHONE_EREGEXP = "^(0\\d{2,3}-)?\\d{7,8}(-\\d{3,4})?$";

    /**
     * Web site regular expression
     */
    String URL_EREGEXP = "(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]";

    /**
     * 6 Bit SMS verification code
     */
    String SIXNUMBER_EREGEXP = "^\\d{6}$";

    /**
     * 4 Bit SMS verification code
     */
    String FOURNUMBER_EREGEXP = "^\\d{4}$";

    /**
     * Verification number
     */
    String NUMBER_EREGEXP = "^\\d{1,}$";

    /**
     * Age
     */
    String AGE_EREGEXP = "^[0-9]{1,2}$";

    /**
     * Password (6-12 letters or numbers) regular expression
     */
    String PASSWORD_OR_EREGEXP = "^[0-9A-Za-z]{6,12}$";

    /**
     * Password (6-12 letters and numbers) regular expression
     */
    String PASSWORD_AND_EREGEXP = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,12}$";

    /**
     * Chinese name regular expression
     */
    String CHINESE_NAME_EREGEXP = "^[\u4e00-\u9fa5]+(\\·[\u4e00-\u9fa5]+)*$";

    /**
     * Amount regular expression   Positive integer, cannot be decimal or negative
     */
    String MONEY_EREGEXP = "^([1-9]\\d*)*$";

    /**
     * Only numbers can be entered   0   or   one
     **/
    String ZERO_OR_ONE_EREGEXP = "^[0-1]{1}$";

    /**
     * positive integer
     */
    String POSITIVE_NUMBER = "^[0-9]*[1-9][0-9]*$";

    /**
     * Date mode
     */
    String SIMPLE_DATE_PATTERN = "^[1-9]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$";

    /**
     * Mm / DD / yyyy date mode none-
     */
    String SIMPLE_DATE_PATTERN_SIMPLE = "^[1-9]\\d{3}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])$";

    /**
     * Date format validation
     */
    String DATE_FORMATE_TEMPLATE1_EREGEXP = "^[1-2][0-9][0-9][0-9]-([1][0-2]|0?[1-9])-([12][0-9]|3[01]|0?[1-9]) ([01][0-9]|[2][0-3]):[0-5][0-9]$";

    /**
     * Zip code format verification
     */
    String POSTCODE_EREGEXP = "^[0-9]{6}$";
}

In the subsequent practical application, see how to use it.

Unified exception response

  1. Introducing spring boot starter web dependency into the pom of mingx common
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.mingx</groupId>
    <artifactId>mingx-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>

  <groupId>com.mingx.common</groupId>
  <artifactId>mingx-common</artifactId>
  <packaging>jar</packaging>


  <dependencies>

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>

   </dependencies>

</project>

      2. Create a com.mingx.common, handler package under src/mian/java, and create SysExceptionHandler.java under this package

The ControllerAdvice annotation is used to realize global Exception handling. First, the Exception handling for input parameter verification will throw an Exception of MethodArgumentNotValidException. First, it will be caught and return a unified parameter error response. Next, catch all exceptions. The exceptions here can be refined. Now, for simulation, catch exceptions uniformly and return a unified [system busy, please try again later] response.

package com.mingx.common.hander;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.mingx.common.AppResult;
import com.mingx.common.AppResultBuilder;
import com.mingx.common.ResultCode;

@ControllerAdvice
@Component
public class SysExceptionHandler {


    private static final Logger logger = LoggerFactory.getLogger(SysExceptionHandler.class);

    /**
     *  Input parameter verification
     * @param exception
     * @return
     */
    @ResponseBody
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public AppResult<String> handle(MethodArgumentNotValidException exception) {
        String message = exception.getBindingResult().getAllErrors().get(0).getDefaultMessage();        
        return AppResultBuilder.error(ResultCode.PARAM_ERROR,message);
    }

    /**
     *     Global exception capture processing
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public AppResult<String> errorHandler(Exception ex) {
        logger.error(ex.getMessage(),ex);
        return AppResultBuilder.error(ResultCode.SYSTEM_ERROR);
    }

}

Comprehensive practice

After preparing for the implementation of the above three basic functions, now simulate and realize a function of saving user information. The steps are as follows:

  1. Introduce mingx common and Hibernate validator dependencies into the project pom
 <dependency>
     <groupId>com.mingx.common</groupId>
     <artifactId>mingx-common</artifactId>
     <version>0.0.1-SNAPSHOT</version>
   </dependency>

   <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
   </dependency>

      2. In the mingx user pojo project, create the com.mingx.user.bo package, which is specially used to put the pojo class of verification input parameters and create the SaveUserBO.java class. It is recommended that the name of the BO class be: corresponding method name + BO, and the specific code is as follows:

package com.mingx.user.bo;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.Length;
import com.mingx.common.RegexpContants;
import lombok.Data;

@Data
public class SaveUserBO {

    @NotBlank(message = "User name cannot be empty")
    @Length(max = 10,message = "The length cannot exceed 10 characters")
    private String name;

    @NotBlank(message = "Gender cannot be empty")
    @Pattern(regexp = RegexpContants.ZERO_OR_ONE_EREGEXP,message = "Incorrect gender format")
    private String sex;

    @NotBlank(message = "Birth date cannot be blank")
    @Pattern(regexp = RegexpContants.SIMPLE_DATE_PATTERN_SIMPLE,message = "Birth date format is incorrect")
    private String birthday;

}

@NotBank annotation is a non empty annotation, indicating that this field cannot be empty

@The Length annotation sets its field Length

@The Pattern annotation can be used for custom regular expression verification, and the relevant regular expressions can be extracted separately. The regexpcontainers.java interface created earlier can be used to define common regular expressions

For more comments on verification, please refer to the reference document officially given by hibernate Validator. Here is the simplest use.

      3. Add the interface for saving user information in SysController.java of mingx user project:

Note: the interface input parameter needs to be annotated with @ Validated before parameter verification can be performed on the input parameter

@PostMapping("/saveUser")
public AppResult<String> saveUser(@Validated @RequestBody SaveUserBO bo){

    Integer count = sysUserService.count(new QueryWrapper<SysUser>().eq("name", bo.getName()));
    if(count > 0) {
        return AppResultBuilder.error(ResultCode.USER_HAS_EXISTED);
    }

    SysUser  user = new SysUser();
    user.setName(bo.getName()).setSex(Integer.valueOf(bo.getSex()))
        .setBirthday(LocalDate.parse(bo.getBirthday(),DateTimeFormatter.ofPattern("yyyy-MM-dd")))
        .setCreateTime(LocalDateTime.now());
    sysUserService.save(user);
    return AppResultBuilder.success(user.getId());

}

      4. Start nacos, start mingx user, open postman, send post request and access http://localhost:7001/user/saveUser
Add parameters in json format to the body, as follows:

If it is saved successfully, it will be displayed as follows:

If a field is empty or the format is incorrect, it will be displayed as follows:

Due to the addition of repeated logical judgment, if you save it again, you will return:

Conclusion

This chapter does not cover the knowledge points related to SpringClould, but as long as it is a qualified back-end interface, it must have the three most basic functions. It is simple to practice in the project and optimize and refine, which is helpful to improve the technical level. It can not be directly involved in business development and ignore the implementation process of these basic functions.

Keywords: Spring Cloud Microservices microservice

Added by eves on Thu, 18 Nov 2021 04:23:17 +0200