1. The format of return values is uniform

1.1 Introduction to Return Values

  1. When using controller to provide services externally, it is often necessary to have a uniform return value format, for example
{
	"status": true."message": null."code": "200"."data": {
		"name": "json"."desc": "Json return value"}}Copy the code
  1. If you don’t use a global uniform return, you need to write a utility class, and then the controller returns the corresponding object
@Data
public class ResponseData {
    private boolean status;
    private String message;
    private String code;
    private Object data;
}
Copy the code
@RequestMapping("/foo")
public ResponseData foo(a) {
    // Or use the tool class to return, setting the value according to the business
    return new ResponseData();
}
Copy the code
  1. In addition to the above methods, the return value can be handled uniformly. Instead of using a single return value for all controllers, the controller simply returns the original value, and the processor wraps the return value
  2. You can also add a custom annotation that ignores the return value encapsulation and returns the original controller value

1.2 Basic Functions

  1. org.springframework.web.method.support.HandlerMethodReturnValueHandler
    • Return values from call handler methods are handled using different policies
    • The policy handles the top-level interface that needs to be implemented for custom return value formats
    • SupportsReturnType: Indicates the type of returned values supported
    • HandleReturnValue: Handles the return value base parameter

  1. org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
    • The request mapping process ADAPTS, including parameters, return value handlers, and so on
    • To maintain the list HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlerComposite inside

  1. You can customize annotations to ignore return value encapsulation at the class or method level
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IgnoreResponseWrapper {

}
Copy the code
  1. org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
    • Belong to HandlerMethodReturnValueHandler subclass
    • The main function is the request and response body to do processing methods
    • All belong to RequestResponseBodyMethodProcessor subclasses need to be replaced by a custom return values
  2. Principle is that at the time of bean initialization, access to all processors array, and then will all be RequestResponseBodyMethodProcessor processor subclass replacement for the processing of the return value for custom processing
  3. In this way, when the corresponding return value handler is called, a custom return value handler will be used, which means that all returned values will be processed as specified

1.3 Basic Implementation

  1. Create a normal SpringBoot project. Project creation is not described here
  2. Create a class implements HandlerMethodReturnValueHandler interface, is mainly used to implement a custom return value content, don’t need injection container
import com.codecoord.unifyreturn.annotation.IgnoreResponseWrapper;
import com.codecoord.unifyreturn.domain.ResponseBase;
import org.springframework.core.MethodParameter;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;

public class ResponseBodyWrapHandler implements HandlerMethodReturnValueHandler {
    private final HandlerMethodReturnValueHandler delegate;

    public ResponseBodyWrapHandler(HandlerMethodReturnValueHandler delegate) {
        this.delegate = delegate;
    }

    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return delegate.supportsReturnType(returnType);
    }

    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        // Ignore the wrapper if the class or method contains unwrapped annotations
        IgnoreResponseWrapper wrapper = returnType.getDeclaringClass().getAnnotation(IgnoreResponseWrapper.class);
        if(wrapper ! =null) {
            delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
            return;
        }
        wrapper = returnType.getMethodAnnotation(IgnoreResponseWrapper.class);
        if(wrapper ! =null) {
            delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
            return;
        }
        
        // Customize the return format
        ResponseBase responseBase = new ResponseBase();
        responseBase.setStatus(true);
        responseBase.setCode("200"); responseBase.setData(returnValue); delegate.handleReturnValue(responseBase, returnType, mavContainer, webRequest); }}Copy the code
  1. Create a class to implement the InitializingBean, which is called at initialization time and needs to be injected into the container, otherwise Spring cannot manage it
import java.util.ArrayList;
import java.util.List;

@Component
public class ResponseBodyWrapFactoryBean implements InitializingBean {
    private final RequestMappingHandlerAdapter adapter;

    @Autowired
    public ResponseBodyWrapFactoryBean(RequestMappingHandlerAdapter adapter) {
        this.adapter = adapter;
    }

    @Override
    public void afterPropertiesSet(a) throws Exception {
        List<HandlerMethodReturnValueHandler> returnValueHandlers = adapter.getReturnValueHandlers();
        if (returnValueHandlers.size() > 0) {
        	// Replace the built-in return value handler
            List<HandlerMethodReturnValueHandler> handlers = newArrayList<>(returnValueHandlers); decorateHandlers(handlers); adapter.setReturnValueHandlers(handlers); }}/ * * * all RequestResponseBodyMethodProcessor return value processor is replaced by the return value of a custom processor * *@author [email protected]
     * @since2020/10/12 * /
    private void decorateHandlers(List<HandlerMethodReturnValueHandler> handlers) {
        for (HandlerMethodReturnValueHandler handler : handlers) {
            if (handler instanceof RequestResponseBodyMethodProcessor) {
            	// Replace with a custom return value handler
                ResponseBodyWrapHandler decorator = new ResponseBodyWrapHandler(handler);
                int index = handlers.indexOf(handler);
                handlers.set(index, decorator);
                break; }}}}Copy the code
  1. Create the controller information. For example, here the map does not need to be encapsulated and responds in the original format
@RestController
@RequestMapping("/unify")
public class UnifyReturnValueController {

    @RequestMapping("string")
    public String stringHandler(a){
        return "Received successfully.";
    }

    @RequestMapping("/json")
    public JSONObject jsonHandler(a){
        JSONObject object = new JSONObject();
        object.put("name"."json");
        object.put("desc"."Json return value");
        return object;
    }

    @RequestMapping("/map")
    @IgnoreResponseWrapper
    public Map<String, Object> mapHandler(a){
        Map<String, Object> map = new HashMap<>(10);
        map.put("name"."map");
        map.put("desc"."Map return value");
        return map;
    }

    @RequestMapping("/list")
    public List<Object> listHandler(a){
        List<Object> data = new ArrayList<>();
        data.add(100);
        data.add(95);
        data.add(99);
        returndata; }}Copy the code

1.4 Test Information

  1. Testing JSON (with encapsulation)
{
	"status": true."message": null."code": "200"."data": {
		"name": "json"."desc": "Json return value"}}Copy the code
  1. Test map(no encapsulation)
{
	"name": "map"."desc": "Map return value"
}
Copy the code
  1. The other test is the same

Ii. Appendix Description

  1. Refer to the red box for the project structure and ignore everything else

2. In addition to globally unifying return values, you can also globally handle exceptions and return them in a unified format