The background,

In today’s distributed, microservice prevailing, most projects have adopted the microservice framework, front and back end separation mode. The front end and the back end interact. The front end requests the URL path according to the convention and passes in relevant parameters. The back end server receives the request, processes services and returns data to the front end.

Therefore, it is important to unify the return value of the interface and ensure the idempotency of the return value of the interface. This paper mainly introduces the result set currently used by the blogger.

Second, unified format design

2.1 General form of unified results

  • Example:
Data: Object # return error message: "",}Copy the code

2.2 Enumeration of result classes

public enum ResultCodeEnum {
    /*** generic part 100-599 ***/
    // Successful request
    SUCCESS(200."successful"),
    / / redirection
    REDIRECT(301."redirect"),
    // The resource was not found
    NOT_FOUND(404."not found"),
    // Server error
    SERVER_ERROR(500."server error"),

    /*** The error code can be separated by different modules, for example: ***/

    // The range from 1000 to 1999 indicates an error in the user module
    // The range from 2000 to 2999 indicates an order module error
    // The range from 3000 to 3999 indicates that the product module is faulty
    / /...

    ;
    /** * Response status code */
    private Integer code;
    /** * Response message */
    private String message;

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

    public Integer getCode(a) {
        return code;
    }

    public String getMessage(a) {
        returnmessage; }}Copy the code
  • code: Response status code

Most people add whatever they need during development. However, for the sake of specification, we should refer to the status code returned by the HTTP request.

Code range type meaning
1 * * 100-199. information The server receives the request and requires the requester to continue with the operation
2 * * 200-299. successful The request was received and processed successfully
3 * * 300-399. redirect Further action is required to complete the request
4 * * 400-499. Client error The request contained a syntax error or could not complete the request
5 * * 500-599. Server error The server encountered an error while processing

Common HTTP status codes:

  1. 200– Request success;
  2. 301– Resources (web pages, etc.) are permanently transferred to anotherURL;
  3. 404– Requested resource (web page, etc.) does not exist;
  4. 500– Internal server error.
  • message: Error message

How do you kindly inform when an error occurs?

  1. According to thecodeGive the corresponding error code location;
  2. Record the misdescriptionsmessageFor interface callers to understand errors in more detail.

2.3 Unified result classes

public class HttpResult <T> implements Serializable {

    /** * Whether the response is successful */
    private Boolean success;
    /** * Response status code */
    private Integer code;
    /** * Response data */
    private T data;
    /** * error message */
    private String message;

    // The constructor starts
    /** * No parameter constructor (the constructor is private and cannot be created directly externally) */
    private HttpResult(a) {
        this.code = 200;
        this.success = true;
    }
    /** * parameter constructor *@param obj
     */
    private HttpResult(T obj) {
        this.code = 200;
        this.data = obj;
        this.success = true;
    }

    /** * parameter constructor *@param resultCode
     */
    private HttpResult(ResultCodeEnum resultCode) {
        this.success = false;
        this.code = resultCode.getCode();
        this.message = resultCode.getMessage();
    }
    // The constructor ends

    /** * General return success (no result returned) *@param <T>
     * @return* /
    public static<T> HttpResult<T> success(a){
        return new HttpResult();
    }

    /** * returns success *@param data
     * @param <T>
     * @return* /
    public static<T> HttpResult<T> success(T data){
        return new HttpResult<T>(data);
    }

    /** * General return failed *@param resultCode
     * @param <T>
     * @return* /
    public static<T> HttpResult<T> failure(ResultCodeEnum resultCode){
        return  new HttpResult<T>(resultCode);
    }

    public Boolean getSuccess(a) {
        return success;
    }

    public void setSuccess(Boolean success) {
        this.success = success;
    }

    public Integer getCode(a) {
        return code;
    }

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

    public T getData(a) {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String getMessage(a) {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public String toString(a) {
        return "HttpResult{" +
                "success=" + success +
                ", code=" + code +
                ", data=" + data +
                ", message='" + message + '\' ' +
                '} '; }}Copy the code

Description:

  1. The constructor is private and cannot be created externally.
  2. Only static methods of the uniform return class can be called to return objects;
  3. successIs aBooleanValue, through which you can directly observe whether the request is successful;
  4. dataIndicates the response data, which is used to return the data required by the client after a successful request.

Iii. Test and summary

3.1 Simple interface test

@RestController
@RequestMapping("/httpRest")
@Api(tags = "Unified Results Test")
public class HttpRestController {

    @ApiOperation(value = "General return successful (no result returned)", httpMethod = "GET")
    @GetMapping("/success")
    public HttpResult success(a){
        return HttpResult.success();
    }

    @ApiOperation(value = "Return success (with result returned)", httpMethod = "GET")
    @GetMapping("/successWithData")
    public HttpResult successWithData(a){
        return HttpResult.success("The Dust Blog");
    }

    @ApiOperation(value = "General Motors return failure.", httpMethod = "GET")
    @GetMapping("/failure")
    public HttpResult failure(a){
        returnHttpResult.failure(ResultCodeEnum.NOT_FOUND); }}Copy the code

The configuration of Swagger and SpringMVC is not posted here, see the Github sample code.

3.2 Return Result

http://localhost:8080/swagger-ui.html#/

{
  "code": 200,
  "success": true
}
Copy the code
{"code": 200, "data": "blog ", "success": true}Copy the code
{
  "code": 404,
  "message": "not found",
  "success": false
}
Copy the code

Global exception handling

When using uniform return results, there is also a case where an error is reported by a program as a result of runtime exceptions, some of which we throw in our business and some of which we cannot predict in advance.

Therefore, we need to define a unified global exception, catch all exceptions in the Controller, handle them appropriately, and return them as a result.

4.1 Design Idea:

  1. Customize an exception class (e.g.TokenVerificationException), catch exceptions for projects or businesses;
  2. use@ExceptionHandlerAnnotations capture both custom and generic exceptions;
  3. use@ControllerAdviceintegration@ExceptionHandlerMethod into a class;
  4. The exception object information is added to the unified result enumeration;

4.2 Custom Exceptions

public class TokenVerificationException extends RuntimeException {

    /** * error code */
    protected Integer code;

    protected String msg;

    public Integer getCode(a) {
        return code;
    }

    public String getMsg(a) {
        return msg;
    }

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

    /** * has a parameter constructor, return code in the enumeration class, where error message * can be specified@param msg
     */
    public TokenVerificationException(String msg) {
        super(msg); }}Copy the code

4.3 Unified Exception Handler

The @ControllerAdvice annotation is a control-level Advice that aggregates the common @ExceptionHandler, @initBinder, and @ModelAttributes methods into a type and applies them to all controllers.

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /** * exception catch *@paramE Caught exceptions *@returnThe encapsulated return object **/
    @ExceptionHandler(Exception.class)
    public HttpResult handlerException(Exception e) {
        ResultCodeEnum resultCodeEnum;
        // Custom exception
        if (e instanceof TokenVerificationException) {
            resultCodeEnum = ResultCodeEnum.TOKEN_VERIFICATION_ERROR;
            resultCodeEnum.setMessage(getConstraintViolationErrMsg(e));
            log.error("TokenVerificationException: {}", resultCodeEnum.getMessage());
        }else {
            // Other exceptions, where we can add judgments and records when we define multiple exceptions
            resultCodeEnum = ResultCodeEnum.SERVER_ERROR;
            resultCodeEnum.setMessage(e.getMessage());
            log.error("common exception:{}", JSON.toJSONString(e));
        }
        return HttpResult.failure(resultCodeEnum);
    }

    /** * Get error message *@param ex
     * @return* /
    private String getConstraintViolationErrMsg(Exception ex) {
        // VALIDTest1. id: THE ID must be positive
        // VALIDTest1. id: The ID must be positive. Validtest1. name: The id must be within the valid range
        String message = ex.getMessage();
        try {
            int startIdx = message.indexOf(":");
            if (startIdx < 0) {
                startIdx = 0;
            }
            int endIdx = message.indexOf(",");
            if (endIdx < 0) {
                endIdx = message.length();
            }
            message = message.substring(startIdx, endIdx);
            return message;
        } catch (Throwable throwable) {
            log.info("ex caught", throwable);
            returnmessage; }}}Copy the code
  • instructions
  1. I’m using@RestControllerAdviceIs the same as@ControllerAdvice + @ResponseBody
  2. The error enumeration class is omitted here, as shown in the Github code.

5. Test and summary

5.1 Testing Interfaces

@RestController
@RequestMapping("/exception")
@Api(tags = "Exception test interface")
public class ExceptionRestController {

    @ApiOperation(value = "Service Exception (Token exception)", httpMethod = "GET")
    @GetMapping("/token")
    public HttpResult token(a) {
        // Simulate the business layer to throw a token exception
        throw new TokenVerificationException("Token has expired");
    }


    @ApiOperation(value = "Other exceptions", httpMethod = "GET")
    @GetMapping("/errorException")
    public HttpResult errorException(a) {
        // An additional exception is intentionally created and not handled
        Integer.parseInt("abc123");
        returnHttpResult.success(); }}Copy the code

5.2 Result

http://localhost:8080/swagger-ui.html#/

{
  "code": 500,
  "message": "For input string: \"abc123\"",
  "success": false
}
Copy the code
{"code": 4000, "message": "token has expired ", "success": false}Copy the code

5.3 summary

@RestControllerAdvice and @ExceptionHandler catch all Rest interface exceptions and return them as the result set of HttpResult we defined, but cannot handle exceptions in the interceptor

Six, summarized

There is no single solution that is suitable for all situations, such as paging, but you can also add a static solution that returns paging results, the implementation of which is not shown here. Therefore, suitable for their own, with a certain readability is very good, welcome to hold different opinions of the big guy to give advice.

6.1 Sample Code

Github sample code

6.2 Technical Exchange

  1. Over the blog
  2. Dust blog – Gold digging
  3. Travelogue Blog – Blog Garden
  4. Github
  5. The public,