Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to realize RESTful API return uniform data format by spring boot

2025-03-27 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/02 Report--

Today, I will talk to you about spring boot how to achieve RESTful API return unified data format, many people may not know much about it. In order to make you understand better, the editor has summarized the following content for you. I hope you can get something according to this article.

With regard to the overall handling of Spring, I have two aspects to say:

Unified data return format

Unified exception handling in order to explain the two problems clearly, it will be divided into two chapters. This chapter focuses on the first point.

Children's shoes said that our project has done this treatment, that is, each API tool class will be a separate tool class to encapsulate the return value, but this is not elegant; I want to write the least code to complete this thing, maybe some children's shoes said, add a few notes to solve the problem, said true, but the main purpose of this article is to explain why to add a few notes to solve the problem, the purpose is to hope that everyone knows why.

In order to better illustrate the problem, this article first explains how to implement it, and then analyzes the principle of implementation in detail (which is very important).

Why do you want to make a uniform data return format?

Front-end separation is the mainstream of today's services. How to design a good RESTful API and how to enable front-end partners to handle standard response JSON data structures are very important. In order to enable the front-end to have a better logical presentation and page interaction, each RESTful request should contain the following information:

The name describes the status status code, identifying whether the request was successful or not, such as [1: success;-1: failure] errorCode error code, giving a clear error code to better deal with business exceptions. If the request succeeds, the value can be an empty errorMsg error message. Corresponding to the error code, the more specific description of the exception information resultBody returns the result, which is usually the JSON data corresponding to the Bean object. Usually, in order to deal with different return value types, it is declared as a generic type to implement the general return value class definition.

According to the above description, using Java Bean to reflect this structure is like this:

@ Datapublic final class CommonResult {private int status = 1; private String errorCode = "; private String errorMsg ="; private T resultBody; public CommonResult () {} public CommonResult (T resultBody) {this.resultBody = resultBody;}} configuration

Yes, we need a few key annotations to complete the configuration:

@ EnableWebMvc@Configurationpublic class UnifiedReturnConfig {@ RestControllerAdvice ("com.example.unifiedreturn.api") static class CommonResultResponseAdvice implements ResponseBodyAdvice {@ Override public boolean supports (MethodParameter methodParameter, Class > aClass) {return true } @ Override public Object beforeBodyWrite (Object body, MethodParameter methodParameter, MediaType mediaType, Class > aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {if (body instanceof CommonResult) {return body;} return new CommonResult (body) }}}

This is the end, we can indulge in writing any RESTful API, all the return values will have a unified JSON structure

test

Create a new UserController and add a corresponding RESTful API. The test case is relatively simple to write, just to illustrate the processing of the returned value.

@ RestController@RequestMapping ("/ users") public class UserController {@ GetMapping (") public List getUserList () {List userVoList = Lists.newArrayListWithCapacity (2); userVoList.add (UserVo.builder () .id (1L) .name) .age (18) .build () UserVoList.add (UserVo.builder (). Id (2L) .name ("tan"). Age (19). Build (); return userVoList;}}

Open the browser to enter the address test: http://localhost:8080/users/, we can see that the List JSON data is returned

Continue to add RESTful API to query user information according to user ID

@ GetMapping ("/ {id}") public UserVo getUserByName (@ PathVariable Long id) {return UserVo.builder () .id (1L) .name ("Soldier") .age (18) .build ();}

Open the browser to enter the address test: http://localhost:8080/users/1, we can see that a single User JSON data is returned

Add an API with a return type of ResponseEntity

@ GetMapping ("/ testResponseEntity") public ResponseEntity getUserByAge () {return new ResponseEntity (UserVo.builder () .id (1L) .name) .age (18) .build (), HttpStatus.OK);}

Open the browser to enter the address test: http://localhost:8080/users/testResponseEntity, we can see that a single User JSON data is also returned

The realization process of anatomy

I will explain the key parts one by one. To solve a case, you still need to go to the crime scene by yourself (open your own IDE to check)

The story starts with the @ EnableWebMvc note. Open it and see:

@ Retention (RetentionPolicy.RUNTIME) @ Target (ElementType.TYPE) @ Documented@Import (DelegatingWebMvcConfiguration.class) public @ interface EnableWebMvc {}

DelegatingWebMvcConfiguration.class has been introduced through the @ Import annotation, so take a look at this class:

@ Configurationpublic class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {...}

You should be familiar with the @ Configuration annotation, but there is a key piece of code hidden in the parent class WebMvcConfigurationSupport of this class:

@ Beanpublic RequestMappingHandlerAdapter requestMappingHandlerAdapter () {RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter (); Return adapter;}

RequestMappingHandlerAdapter is the key to every request processing. Let's take a look at the definition of this class:

Public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean {...}

This class implements the InitializingBean interface, and I am in the "where do I come from" in the Spring Bean lifecycle? Several keys to Spring Bean initialization are clearly explained in this article, one of which is the afterPropertiesSet method of the InitializingBean interface, which is also overridden in the RequestMappingHandlerAdapter class:

@ Overridepublic void afterPropertiesSet () {/ / Do this first, it may add ResponseBody advice beans initControllerAdviceCache (); if (this.argumentResolvers = = null) {List resolvers = getDefaultArgumentResolvers (); this.argumentResolvers = new HandlerMethodArgumentResolverComposite () .addResolvers (resolvers);} if (this.initBinderArgumentResolvers = = null) {List resolvers = getDefaultInitBinderArgumentResolvers (); this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite (). AddResolvers (resolvers) } if (this.returnValueHandlers = = null) {List handlers = getDefaultReturnValueHandlers (); this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite () .addHandlers (handlers);}}

The content of this method is very critical, but let's take a look at the initControllerAdviceCache method first, and the rest will be explained separately later:

Private void initControllerAdviceCache () {... If (logger.isInfoEnabled ()) {logger.info ("Looking for @ ControllerAdvice:" + getApplicationContext ());} List beans = ControllerAdviceBean.findAnnotatedBeans (getApplicationContext ()); AnnotationAwareOrderComparator.sort (beans); List requestResponseBodyAdviceBeans = new ArrayList (); for (ControllerAdviceBean bean: beans) {. If (ResponseBodyAdvice.class.isAssignableFrom (bean.getBeanType () {requestResponseBodyAdviceBeans.add (bean);}

Scan the ControllerAdvice annotation through the ControllerAdviceBean static method, but we use the @ RestControllerAdvice annotation in our implementation. Open it and see it:

@ Target (ElementType.TYPE) @ Retention (RetentionPolicy.RUNTIME) @ Documented@ControllerAdvice@ResponseBodypublic @ interface RestControllerAdvice {

This comment is marked by @ ControllerAdvice and @ ResponseBody, just as the familiar @ RestController annotation is the same as the @ Controller and @ ResponseBody tags.

Now that you know how the Bean marked with @ RestControllerAdvice is loaded into the Spring context, you need to know how Spring uses our bean and handles the return body

How do you actually convert data in HttpMessageConverter? Part of it has been explained in this article. I hope my friends will read this article first and the following part will be understood in seconds. We will explain it further here.

In the writeWithMessageConverters method of AbstractMessageConverterMethodProcessor, there is a core code:

If (messageConverter instanceof GenericHttpMessageConverter) {if ((GenericHttpMessageConverter) messageConverter). CanWrite (declaredType, valueType, selectedMediaType) {outputValue = (T) getAdvice (). BeforeBodyWrite (outputValue, returnType, selectedMediaType, (Class >) messageConverter.getClass (), inputMessage, outputMessage);. Return;}}

You can see that we are close to the truth by calling the beforeBodyWrite method through getAdvice ()

Protected RequestResponseBodyAdviceChain getAdvice () {return this.advice;}

RequestResponseBodyAdviceChain, whose name comes with Chain, obviously uses the "chain of responsibility design pattern", which is clearly stated in the article on the chain of responsibility design pattern that you have to know, but it passes the chain of responsibility in a circular way:

Class RequestResponseBodyAdviceChain implements RequestBodyAdvice, ResponseBodyAdvice {@ Override public Object beforeBodyWrite (Object body, MethodParameter returnType, MediaType contentType, Class > converterType, ServerHttpRequest request, ServerHttpResponse response) {return processBody (body, returnType, contentType, converterType, request, response) } @ SuppressWarnings ("unchecked") private Object processBody (Object body, MethodParameter returnType, MediaType contentType, Class > converterType, ServerHttpRequest request, ServerHttpResponse response) {for (ResponseBodyAdvice advice: getMatchingAdvice (returnType, ResponseBodyAdvice.class)) {if (advice.supports (returnType) ConverterType) {body = ((ResponseBodyAdvice) advice) .beforeBodyWrite ((T) body, returnType, contentType, converterType, request, response) }} return body;}}

Our rewritten beforeBodyWrite method will eventually be called, and that's the truth!

Have you ever thought that if the return value of our API method is of type org.springframework.http.ResponseEntity, we can specify the HTTP return status code, but this return value will be put directly into the body parameter of our beforeBodyWrite method? If this is obviously wrong because ResponseEntity contains a lot of our non-business data, how does Spring handle it for us?

Before our method gets the return value and before calling the beforeBodyWrite method, we have to select HandlerMethodReturnValueHandler to handle different Handler to process the return value

In the handleReturnValue method in class HandlerMethodReturnValueHandlerComposite

@ Overridepublic void handleReturnValue (Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {HandlerMethodReturnValueHandler handler = selectHandler (returnValue, returnType); if (handler = = null) {throw new IllegalArgumentException ("Unknown return value type:" + returnType.getParameterType (). GetName ());} handler.handleReturnValue (returnValue, returnType, mavContainer, webRequest);}

There are many built-in Handler to select the appropriate handler,Spring by calling the selectHandler method. Let's look at the class diagram:

HttpEntityMethodProcessor is one of them. It overrides the supportsParameter method to support the HttpEntity type, that is, the ResponseEntity type:

@ Overridepublic boolean supportsParameter (MethodParameter parameter) {return (HttpEntity.class = = parameter.getParameterType () | | RequestEntity.class = = parameter.getParameterType ());}

So when we return type ResponseEntity, we use the handleReturnValue method of HttpEntityMethodProcessor to process our result:

Overridepublic void handleReturnValue (Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {... If (responseEntity instanceof ResponseEntity) {int returnStatus = ((ResponseEntity) responseEntity). GetStatusCodeValue (); outputMessage.getServletResponse (). SetStatus (returnStatus); if (returnStatus = = 200) {if (SAFE_METHODS.contains (inputMessage.getMethod ()) & & isResourceNotModified (inputMessage, outputMessage)) {/ / Ensure headers are flushed, no body should be written. OutputMessage.flush (); / / Skip call to converters, as they may update the body. Return;} / / Try even with null body. ResponseBodyAdvice could get involved. WriteWithMessageConverters (responseEntity.getBody (), returnType, inputMessage, outputMessage); / / Ensure headers are flushed even if no body was written. OutputMessage.flush ();}

This method extracts responseEntity.getBody (), passes a MessageConverter, and then continues to call the beforeBodyWrite method, which is the truth!

After reading the above, do you have any further understanding of how spring boot implements the uniform data format returned by RESTful API? If you want to know more knowledge or related content, please follow the industry information channel, thank you for your support.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 225

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report