8. Unified return R class and unified exception handling in SpringBoot 02

01. Why did you learn to return?

In fact, most program development or interface development in enterprises actually return a so-called JSON format. The JSON format is encapsulated by java object-oriented method.

  • When unifying exception handling, we use @ RestControllerAdvice, which is controller post enhanced processing. Post processing with @ RestControllerAdvice can achieve
  • In the front end and back end separated project architecture, all of them return @ RestController asynchronously and all of them are @ ResponseBody methods. Think you don't have a page or a freemaker. Therefore, during development, we certainly hope to return the specific error information - intercept - transform (Unified return) - transformed information to the user (the caller of the interface)
  • The standard of transformation is very important. The general practice is to use java Object-oriented development. Class oriented return instead of Map and Object
  • Because the key user of Map is defined casually, you can't achieve a high degree of unity and standardization.

Common naming:

  • R
  • Result
  • ResultResponse
  • ServerResponse
  • ApiResponse
  • ApiResult

02. Process of class R packaging

Unified return format:

There is only one case of success and N cases of failure.

# Successful return
{
  status:200,
  data:{id:1,name:"zhangsan"},
  msg:""
}


#  Failure Return
{
  status:601,
  msg:"user does not exist!!",
  data:"" 
}

{
  status:602,
  msg:"Payment user prohibited!!",
  data:"" 
}

Understanding json format

{}----Map perhaps Bean
[{Map},{Map},{Map}] List<Map>
[{Bean},{Bean},{Bean}] List<Bean>

Prototype of R:

@Data
public class R {

    // Status code
    private Integer status;
    // Return information
    private String msg;
    // Return data. Because the return data is of uncertain type, only Object or generic type can be considered
    private Object data;
}

03. What is the reason for using R?

package com.kuangstudy.web;

import com.kuangstudy.bean.User;
import com.kuangstudy.config.R;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Description:
 * Author: yandi Administrator
 * Version: 1.0
 * Create Date Time: 2021/12/16 20:33.
 * Update Date Time:
 *
 * @see
 */
@RestController
public class UserController {

    @GetMapping("/saveuser")
    public R saveUser(){
        // 1: Save user object
        User user = new User();
        user.setUserid(1L);
        user.setUsername("yandi");
        user.setPassword("24534534");
        user.setCreateTime(new Date());
        // 2: Return to R
        R r = new R();
        r.setStatus(200);
        r.setMsg("");
        r.setData(user);
        return r;
    }

    @GetMapping("/saveuser2")
    public Map<String,Object> saveUser2(){
        // 1: Save user object
        User user = new User();
        user.setUserid(1L);
        user.setUsername("yandi");
        user.setPassword("24534534");
        user.setCreateTime(new Date());
        // 2: Return to R
        Map<String,Object> map = new HashMap<>();
        map.put("status",200);
        map.put("msg","");
        map.put("data",user);
        return map;
    }
}

http://localhost:8085/saveuser Return to get the following effect:

var res = {
  "status": 200,
  "msg": "",
  "data": {
    "userid": 1,
    "username": "yandi",
    "createTime": "2021-12-16T12:39:10.037+00:00",
    "password": "24534534"
  }
}

// If value
console.log(res.status)
console.log(res.data.userid)
console.log(res.data.username)
console.log(res.data.createTime)
console.log(res.data.password)
// Structure value
var {userid,username,...yandi} = res.data;
console.log(userid)
console.log(username)
console.log(yandi.createTime)
console.log(yandi.password)
  • The above code R and Map can actually achieve the desired effect. However, Map is problematic and inflexible, because a team may be developed by many developers. If everyone has a set of standards and returns, there will be no constraints and norms. At this time, the interface caller becomes confused

  • Use R to achieve a unified return.

    • There is only one standard. This scheme is recommended
  • Problem: return writing is inconvenient and cumbersome. You can consider using methods for encapsulation to achieve the purpose of reuse and convenient call

04. Transformation of class R

package com.kuangstudy.config;

import lombok.Data;

/**
 * Description:
 * Author: yandi Administrator
 * Version: 1.0
 * Create Date Time: 2021/12/15 23:20.
 * Update Date Time:
 *
 * @see
 */
@Data
public class R {

    // Status code
    private Integer status;
    // Return information
    private String msg;
    // Return data. Because the return data is of uncertain type, only Object or generic type can be considered
    private Object data;

    // All constraints are executed and returned using the method area, and new is not allowed to go outside
    private R() {

    }

    /**
     * Method encapsulation:
     * - R Massive invasion
     * - Solve the problem of convenient call
     *
     * @param obj
     * @return
     */
    public static R success(Object obj) {
        // The reason why 200 is dead is very simple: only one sound is allowed after successful development, and multiple sounds are not allowed
        return restResult(obj, 200, "success");
    }

    public static R success(Object obj, String msg) {
        return restResult(obj, 200, msg);
    }

    // Error why pass status. There is only one success, but the error has N status
    public static R error(Integer status, String msg) {
        return restResult(null, status, msg);
    }

    public static R error(Integer status, String msg, Object obj) {
        return restResult(obj, status, msg);
    }

    private static R restResult(Object data, Integer status, String msg) {
        R r = new R();
        r.setStatus(status);
        r.setMsg(msg);
        r.setData(data);
        return r;
    }
}

05. Why should the constructor of R be privatized?

In fact, no, but from the perspective of normative constraints, the suggestion is Privatization: there is only one way to call.

  • Returns are processed only through the method area
  • Return by creating an object is not allowed
  • So as to obtain a high degree of unity and standards

06. R-type and enumeration relationships

Unified return and enumeration: golden partner

In the unified return, in fact, many of our error messages are defined by our program developers, whether the status or the return information. For example:

R.error(601,"User belongs to error");
R.error(602,"Payment failed");
R.error(10001,"Wrong password");
R.error(500,"Server busy");

There will be a problem above. As the project becomes larger and larger, there are more and more developers. At this time, we must highly focus on prompt and state control for centralized management. It is convenient for modification and unified adjustment.

Centralized management

Using constant classes & using interfaces

package com.kuangstudy.consts;

/**
 * Description:
 * Author: yandi Administrator
 * Version: 1.0
 * Create Date Time: 2021/12/16 20:57.
 * Update Date Time:
 *
 * @see
 */
public interface ResultConstants {
    // return R.error(601, "user password is incorrect");
    Integer USER_PWD_STATUS = 601;
    String USER_PWD_MESSAGE = "Incorrect user password!";
    
    
    //R.error(500, "server busy");
    Integer SERVER_ERROR_STATUS = 500;
    String SERVER_ERROR_MESSAGE = "Server busy!";

}

There is no problem with centralized management of information in the above static constant area, but there are several problems:

  • State management will be very. There will be many interface definition states. If the development is not standard and non-standard, it will cause who and who.

  • Naming is a disaster

enumeration

Enumeration in learning: it is a class, class inheritance interface, inheritance enumeration.

Enumeration in the program: it is an object-oriented alternative when a constant class = = multi value matching (pairs, twos and threes) = =.

Definition of enumeration

  • You can define properties and methods
  • Constructor - Private
    • What to say: its objects can only be exposed through methods. Enumerate and select objects exposed through the method area
    • It is exposed through a custom constructor name.

When the definition of enumeration is single column:

When there is only one object in the enumeration, it is single column.

package com.kuangstudy.enums;

/**
 * Description:
 * Author: yandi Administrator
 * Version: 1.0
 * Create Date Time: 2021/12/16 21:05.
 * Update Date Time:
 *
 * @see
 */
public enum ResultStatusEnum {
    INSTANCE();
    ResultStatusEnum() {
    }
    public Integer age;
}

ResultStatusEnum.INSTANCE.age = 10;
ResultStatusEnum.INSTANCE2.age = 20;

07. Class R and class C

R.java

package com.kuangstudy.config;

import com.kuangstudy.enums.ResultStatusEnum;
import lombok.Data;

/**
 * Description:
 * Author: yandi Administrator
 * Version: 1.0
 * Create Date Time: 2021/12/15 23:20.
 * Update Date Time:
 *
 * @see
 */
@Data
public class R {

    // Status code
    private Integer status;
    // Return information
    private String msg;
    // Return data. Because the return data is of uncertain type, only Object or generic type can be considered
    private Object data;

    // All constraints are executed and returned using the method area, and new is not allowed to go outside
    private R() {

    }

    /**
     * Method encapsulation:
     * - R Massive invasion
     * - Solve the problem of convenient call
     *
     * @param obj
     * @return
     */
    public static R success(Object obj) {
        // The reason why 200 is dead is very simple: only one sound is allowed after successful development, and multiple sounds are not allowed
        return restResult(obj, ResultStatusEnum.SUCCESS_STATUS.getStatus(), ResultStatusEnum.SUCCESS_STATUS.getMessage());
    }

    public static R success(Object obj, String msg) {
        return restResult(obj, ResultStatusEnum.SUCCESS_STATUS.getStatus(), ResultStatusEnum.SUCCESS_STATUS.getMessage());
    }

    // Error why pass status. There is only one success, but the error has N status
    @Deprecated
    public static R error(Integer status, String msg) {
        return restResult(null, status, msg);
    }

    @Deprecated    //This annotation indicates that the method is obsolete
    public static R error(Integer status, String msg, Object obj) {
        return restResult(obj, status, msg);
    }

    public static R error(ResultStatusEnum resultStatusEnum) {
        return restResult(null, resultStatusEnum.getStatus(),resultStatusEnum.getMessage());
    }

    private static R restResult(Object data, Integer status, String msg) {
        R r = new R();
        r.setStatus(status);
        r.setMsg(msg);
        r.setData(data);
        return r;
    }
}

ResultStatusEnum.java

package com.kuangstudy.enums;

/**
 * Description:
 * Author: yandi Administrator
 * Version: 1.0
 * Create Date Time: 2021/12/16 21:05.
 * Update Date Time:
 *
 * @see
 */
public enum ResultStatusEnum {

    SUCCESS_STATUS(200,"SUCCESS"),
    USER_PWR_STATUS(601,"Incorrect user password"),
    ORDER_ERROR_STATUS(602,"Wrong order");

    ResultStatusEnum(Integer status,String message) {
        this.status = status;
        this.message = message;
    }

    private Integer status;
    private String message;

    public Integer getStatus() {
        return status;
    }

    public String getMessage() {
        return message;
    }

}

08. Is the set method still necessary for enumeration?

It returns the enumeration uniformly and manages the error information centrally. Now you provide a set to expose it for programmers to modify. Isn't that self contradictory? Therefore, in this scenario, the enumerated set should be removed.

09. Why not use the ResponseEntity provided by the spring MVC framework?

In some open source projects, when learning a project course in the future, you may see that some projects do not return R. It's ResponseEntity

ResponseEntity is officially provided by spring MVC. It is a mechanism for unified management based on the method of sprignmvc. But this has great limitations. It is inconvenient to extend and provide HttpStatus enumeration class internally. Can't meet our business needs. Because it will be out of business and inconvenient to control. Therefore, it is generally not used.

In fact, the reference of R class + custom enumeration: ResponseEntity + HttpStatus

@GetMapping("/saveuser3")
public ResponseEntity<User> saveuser3(){
    // 1: Save user object
    User user = new User();
    user.setUserid(1L);
    user.setUsername("yandi");
    user.setPassword("24534534");
    user.setCreateTime(new Date());
    // 2: Return to R
    return ResponseEntity.status(HttpStatus.OK).body(user);
}

010. Problems with R and enumeration?

When an enterprise develops a multi person collaborative development model, but if the team suddenly increases by dozens or hundreds, there will be a problem. At this time, there was a voice in the team, and many felt that this unified return was a little bit:

  • Too restrictive.
  • A new person came and communicated with me again. Communication costs will be higher and higher.

Solution

Spring MVC uses the aop idea to do a lot of enhancement when the controller calls the method. aop: object execution method

  • If an error occurs: sprignmvc unified exception handling - AOP. ---@ Afterthrowing - Section
  • springmvc also provides unified return processing - AOP - @ afterreturning - aspect
  • springmvc also provides unified parameter processing - AOP - @ before - aspect

Implementation steps

Define a uniformly returned class: ResultResponseHandler implements the ResponseBodyAdvice interface as follows:

package com.kuangstudy.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xq.common.result.handler.ErrorHandler;
import com.xq.controller.login.WeixinLoginController;
import com.xq.controller.login.WeixinOpenReplyController;
import com.xq.controller.pay.alipay.AliPayController;
import com.xq.controller.pay.weixin.WeixinNavtiveController;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * <p>
 * bug: (basePackages = "com.kuangstudy")It is recommended to sweep the package
 * Why?
 * If you don't use Swagger in your project, you can scan the package or not. It's all normal.
 * But if your project uses Swagger, because Swagger itself is also a spring MVC project, and it also has http requests
 * During this request, if you configure interceptors or some notification classes xxadvice in your project, Swagger will be intercepted.
 * Will cause Swagger to fail.
 */
@RestControllerAdvice
public class ResultResponseHandler implements ResponseBodyAdvice<Object> {
    /**
     * Whether the advice function is supported. true means yes, false means No
     *
     * @param methodParameter
     * @return
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }
    

    // The parameter o represents the result of the method requested by spring MVC, and O is an object
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        // The results of the request are returned and processed uniformly here. If an error is reported during the operation of the method, an exception will be thrown, and the judgment result is true
        if (o instanceof ErrorHandler) {
            // 1. If the returned result is an exception result, just dump the structure data returned by the exception into R.fail
            ErrorHandler errorHandler = (ErrorHandler) o;
            return R.error(errorHandler.getStatus(), errorHandler.getMsg());
        } else if (o instanceof String) {
            try {
                // 2. Because the spring MVC data converter has special processing for strings, StringHttpMessageConverter
                //3. ObjectMapper is a class in jsckson
                ObjectMapper objectMapper = new ObjectMapper();
                R r = R.success(o);
                //4. Convert object to String and return
                return objectMapper.writeValueAsString(r);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return R.success(o);
    }
}

Normal route: - Request ----- controller object ----- saveuser-----return user -----supports(true)-- beforeBodyWrite - R.success(user)

Exception route: - Request ----- controller object ----- saveuser ----- exception ex - GlobalExceptionControllerHandler - errorhandler ------ supports (true) -- beforebodywrite - R.error(ex)

10. Why learn custom exceptions

package com.kuangstudy.config.exresult.ex;//package com.kuangstudy.config;

import com.kuangstudy.config.exresult.ResultStatusEnum;

/**
 * Description: Custom exception
 * Author: yandi Administrator
 * Version: 1.0
 * Create Date Time: 2021/12/15 21:48.
 * Update Date Time:
 *
 * @see
 */
public class BussinessException extends RuntimeException {

    private Integer status;
    private String msg;

    public BussinessException(Integer status, String msg) {
        super(msg);
        this.msg = msg;
        this.status = status;
    }

    public BussinessException(ResultStatusEnum  resultStatusEnum) {
        super(resultStatusEnum.getMessage());
        this.status = resultStatusEnum.getStatus();
        this.msg = resultStatusEnum.getMessage();
    }

    public BussinessException(String msg) {
        super(msg);
        this.status = 500;
        this.msg = msg;
    }

    public Integer getStatus() {
        return status;
    }

    public String getMsg() {
        return msg;
    }

}

package com.kuangstudy.config.exresult.ex;//package com.kuangstudy.config;

import com.kuangstudy.config.exresult.ResultStatusEnum;

/**
 * Description: Custom exception
 * Author: yandi Administrator
 * Version: 1.0
 * Create Date Time: 2021/12/15 21:48.
 * Update Date Time:
 *
 * @see
 */
public class OrderException extends RuntimeException {

    private Integer status;
    private String msg;

    public OrderException(Integer status, String msg) {
        super(msg);
        this.msg = msg;
        this.status = status;
    }

    public OrderException(String msg) {
        super(msg);
        this.status = 500;
        this.msg = msg;
    }

    public OrderException(ResultStatusEnum resultStatusEnum) {
        super(resultStatusEnum.getMessage());
        this.status = resultStatusEnum.getStatus();
        this.msg = resultStatusEnum.getMessage();
    }


    public Integer getStatus() {
        return status;
    }

    public String getMsg() {
        return msg;
    }

}

  • User defined exceptions can facilitate subsequent business analysis and positioning
  • Custom does not recommend inheritance: Exception and Throwable if your integration is a top-level Exception. You must throw

Keywords: Java R Language Spring Boot

Added by mhewall on Fri, 21 Jan 2022 23:02:35 +0200