The problem

  • The front-end doesn’t agree with the HTTP status to indicate the success of the operation. It strongly requires all interfaces to return 200 and add code to the response body to indicate the success of the operation.

For example, when the POST request interface/API /user/2 is used, there are two response interfaces: / API /user/2 Request method: POST request body:…… (Parameter in JSON format)

Response format: HTTP Status: 403 Response body:

{MSG: 'no permission'}Copy the code

The front end wants the response format: HTTP Status: 200

{code: 403, MSG: 'no permission'}Copy the code

Way to

1. Modify the return value of each interface

The simplest way to define a class is as follows:


@Data
public class Res{
    private Integer code;
    private String msg;
    private Object content;
}
Copy the code

Set the return value of each service to the content, set the corresponding code and MSG, and the controller returns the object to the front end.

It is also possible for each interface to return an instance json of JSONObject

@GetMapping("/{id}") public JSONObject getOne((@PathVariable Integer id){ Person person = personService.findById(id); JSONObject json = new JSONObject(); json.put("code", 200); Json.put (" MSG ", "success "); json.put("content", person); return json; }Copy the code

The function is complete, but seeing a full screen of Res or JSONObject operations, so much repetitive code, really shouldn’t be.

2. Improve and modify the original SpringBoot process

SpringBoot requests are processed as follows:

The general flow of request processingRequest processing process

In the second picture you can see HandlerMethodReturnValueHandler in dealing with all the content of the response, as long as intervention he process can packing up the original data The following is the source code of the class

public interface HandlerMethodReturnValueHandler { /** * Whether the given {@linkplain MethodParameter method return type} is * supported by this handler. * @param returnType the method return type to check * @return {@code true} if this  handler supports the supplied return type; * {@code false} otherwise */ boolean supportsReturnType(MethodParameter returnType); /** * Handle the given return value by adding attributes to the model and * setting a view or setting the * {@link ModelAndViewContainer#setRequestHandled} flag to {@code true} * to indicate the response has been handled directly. * @param returnValue the value returned from the handler method * @param returnType the type of the return value. This type must have * previously been passed to {@link #supportsReturnType} which must * have returned {@code true}. * @param  mavContainer the ModelAndViewContainer for the current request * @param webRequest the current request * @throws Exception if the return value handling results in an error */ void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception; }Copy the code

Implementing the two methods of this class allows you to customize the processing.

  • First, we need to define a utility class:
Public class Res {// protected final static Res OK = new Res(httpstatus.ok.value ()," success ",""); private Integer code; private String msg; private String message; */ Private Object content; / / Private Object content; /** * use the constructor * @param data */ protected Res(Object data) {this.code = 200; This. MSG = "success "; this.message = "success"; this.content = data; } Res(Integer code,String msg, String message){ this.msg = msg; this.code = code; this.message = message; } public Integer getCode() { return code; } public String getMsg() { return msg; } public String getMessage() { return message; } public Object getContent() { return content; }}Copy the code
  • Then implement HandlerMethodReturnValueHandler
public class WrapReturnValueHandler implements HandlerMethodReturnValueHandler { private Res ok = new Res(null); private RequestResponseBodyMethodProcessor target; public WrapReturnValueHandler(RequestResponseBodyMethodProcessor target) { this.target = target; } @Override public boolean supportsReturnType(MethodParameter methodParameter) { return true; } @Override public void handleReturnValue(Object o, MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest) throws Exception { Executable executable = methodParameter.getExecutable(); Method method = (Method) executable; Class<? > returnType = method.getReturnType(); if (returnType.equals(Void.class)) { target.handleReturnValue(ok, methodParameter, modelAndViewContainer, nativeWebRequest); } else if ( returnType.equals( Res.class )) { target.handleReturnValue(o, methodParameter, modelAndViewContainer, nativeWebRequest); else { target.handleReturnValue(new Res(o), methodParameter, modelAndViewContainer, nativeWebRequest); }}}Copy the code
  • Finally, add this implementation class to the SpringBoot process
@Configuration public class InitializingAdvice implements InitializingBean { private final static Logger log = LoggerFactory.getLogger( InitializingAdvice.class ); @Resource private RequestMappingHandlerAdapter adapter; @Override public void afterPropertiesSet() { List<HandlerMethodReturnValueHandler> returnValueHandlers = adapter.getReturnValueHandlers(); List<HandlerMethodReturnValueHandler> handlers = new ArrayList(returnValueHandlers); this.decorateHandlers(handlers); adapter.setReturnValueHandlers(handlers); } private void decorateHandlers(List<HandlerMethodReturnValueHandler> handlers) { for (HandlerMethodReturnValueHandler handler : handlers) { if (handler instanceof RequestResponseBodyMethodProcessor) { WrapReturnValueHandler decorator = new WrapReturnValueHandler( (RequestResponseBodyMethodProcessor) handler); int index = handlers.indexOf(handler); handlers.set(index, decorator); break; }}}}Copy the code

This allows all responses to wrap the content in the expected format before being turned into a stream. Of course, exceptions can be thrown using the @ControllerAdvice annotation to capture control of the HTTP status, and then actively construct the response as an Res instance