The return value format of springboot global controller is uniform

Time:2021-11-23

1、 Uniform return value format

1. Introduction to return value

When using controller to provide external services, it is often necessary to unify the return value format, such as

{
	"status": true,
	"message": null,
	"code": "200",
	"data": {
		"name": "json",
		"Desc": "JSON return value"
	}
}

If you do not use global uniform return, you need to write a tool 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;
}
@RequestMapping("/foo")
public ResponseData foo() {
    //Or use the tool class to return, and set the value according to the business
    return new ResponseData();
}

In addition to the above methods, the return value can be processed uniformly. There is no need to use a return value for all controllers. The controller only needs to return the original value, and the processor will encapsulate the return value

At the same time, you can also add a custom annotation, which is used to ignore the return value encapsulation and return according to the original value of the controller

2. Basic functions

org.springframework.web.method.support.HandlerMethodReturnValueHandler

  • Use different policies to handle return values from calling handler methods
  • Policy processing top-level interface, which needs to be implemented for custom return value format
  • Supportsreturnantype: sets the supported return value type
  • Handlereturnvalue: basic parameter for processing return value

在这里插入图片描述

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter

  • Request mapping processing adaptation, including parameters, return value processor and other information
  • The handlermethodreturnvaluehandlercomposite internally maintains the handlermethodreturnvaluehandler list

在这里插入图片描述在这里插入图片描述

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 {

}

org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor

  • Subclass of handlermethodreturnvaluehandler
  • The main function is to process the request and response body
  • All subclasses belonging to requestresponsebodymethodprocessor need to be replaced with custom return value processing

The implementation principle is to obtain all processor arrays during bean initialization, and then replace all the processes of processing the return value by the requestresponsebodymethodprocessor processor subclass with custom processors

In this way, when calling the corresponding return value processor, the user-defined return value processor will be used, that is, all return values will be processed according to the specified

3. Basic realization

Create a normal springboot project, which is not described here

Create a class to implement the handlermethodreturnvaluehandler interface, which is mainly used to implement the custom return value content without injecting the 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 {
        //If the class or method contains non wrapper annotations, the wrapper is ignored
        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;
        }
        
        //Custom return format
        ResponseBase responseBase = new ResponseBase();
        responseBase.setStatus(true);
        responseBase.setCode("200");
        responseBase.setData(returnValue);
        delegate.handleReturnValue(responseBase, returnType, mavContainer, webRequest);
    }
}

Create a class to implement initializingbean. It is called during initialization 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() throws Exception {
        List<HandlerMethodReturnValueHandler> returnValueHandlers = adapter.getReturnValueHandlers();
        if (returnValueHandlers.size() > 0) {
        	//Replace the built-in return value processor
            List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(returnValueHandlers);
            decorateHandlers(handlers);
            adapter.setReturnValueHandlers(handlers);
        }
    }

    /** 
     *Replace all requestresponsebodymethodprocessor return value processors with custom return value processors
     *
     * @author [email protected]
     * @since 2020/10/12
     */
    private void decorateHandlers(List<HandlerMethodReturnValueHandler> handlers) {
        for (HandlerMethodReturnValueHandler handler : handlers) {
            if (handler instanceof RequestResponseBodyMethodProcessor) {
            	//Replace with custom return value processor
                ResponseBodyWrapHandler decorator = new ResponseBodyWrapHandler(handler);
                int index = handlers.indexOf(handler);
                handlers.set(index, decorator);
                break;
            }
        }
    }
}

Create controller information. For example, the map here does not need to be encapsulated and responds in the original format

@RestController
@RequestMapping("/unify")
public class UnifyReturnValueController {

    @RequestMapping("string")
    public String stringHandler(){
        Return "received successfully";
    }

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

    @RequestMapping("/map")
    @IgnoreResponseWrapper
    public Map<String, Object> mapHandler(){
        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(){
        List<Object> data = new ArrayList<>();
        data.add(100);
        data.add(95);
        data.add(99);
        return data;
    }
}

4. Test information test JSON (encapsulated)

{
	"status": true,
	"message": null,
	"code": "200",
	"data": {
		"name": "json",
		"Desc": "JSON return value"
	}
}

Test map (no encapsulation)

{
	"name": "map",
	"Desc": "map return value"
}

The other methods are the same

2、 Appendix description

Refer to the red box for the project structure, and ignore others

在这里插入图片描述

In addition to globally unifying the return value, exceptions can also be processed globally and returned in a unified format

This is the end of this article about the unified return value format of springboot global controller. For more information about the unified return value format of springboot, please search the previous articles of developeppaer or continue to browse the relevant articles below. I hope you will support developeppaer in the future!

Recommended Today

Apache sqoop

Source: dark horse big data 1.png From the standpoint of Apache, data flow can be divided into data import and export: Import: data import. RDBMS—–>Hadoop Export: data export. Hadoop—->RDBMS 1.2 sqoop installation The prerequisite for installing sqoop is that you already have a Java and Hadoop environment. Latest stable version: 1.4.6 Download the sqoop installation […]