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 unify the format of backend return by SpringBoot

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)05/31 Report--

This article mainly introduces the relevant knowledge of "how SpringBoot unifies the back-end return format". The editor shows you the operation process through an actual case. The operation method is simple, fast and practical. I hope this article "how to unify the back-end return format of SpringBoot" can help you solve the problem.

Why return a uniform standard format for SpringBoot

By default, there are three common return formats for SpringBoot:

First: return String@GetMapping ("/ hello") public String getStr () {return "hello,javadaily";}

At this point, the returned value obtained by calling the API is as follows:

Hello,javadaily

Second: return the custom object @ GetMapping ("/ aniaml") public Aniaml getAniaml () {Aniaml aniaml = new Aniaml (1, "pig"); return aniaml;}

At this point, the returned value obtained by calling the API is as follows:

{"id": 1, "name": "pig"} third: interface exception @ GetMapping ("/ error") public int error () {int I = 9apper0; return I;}

At this point, the returned value obtained by calling the API is as follows:

{"timestamp": "2021-07-08T08:05:15.423+00:00", "status": 500,500, "error": "Internal Server Error", "path": "/ wrong"}

Based on the above, they will be confused if you co-tune the interface with the front-end developer, because we did not give him a uniform format, the front-end staff do not know how to deal with the return value.

What's more, some students, such as Xiao Zhang, like to encapsulate the results. He uses the Result object, and Xiao Wang also likes to wrap the results, but he uses the Response object. When this happens, I believe the front-end staff will freak out.

So we need to define a unified standard return format in our project.

Define the return standard format

A standard return format consists of at least three parts:

Status status value: the status codes of various returned results are uniformly defined by the backend.

Message description: description of the result of this API call

Data data: the data returned this time

{"status": "100,100", "message": "Operation succeeded", "data": "hello,javadaily"}

Of course, you can also add other extension values as needed. For example, we have added the API call time to the returned object.

Timestamp: API call time

Define the return object

@ Datapublic class ResultData {/ * * result status. For more information, please see ResultData.java*/ private int status; private String message; private T data; private long timest public ResultData () {this.timestamp = System.currentTimeMillis ();} public static ResultData success (T data) {ResultData resultData = new ResultData (); resultData.setStatus (ReturnCode.RC100.getCode ()); resultData.setMessage (ReturnCode.RC100.getMessage ()); resultData.setData (data) Return resultData;} public static ResultData fail (int code, String message) {ResultData resultData = new ResultData (); resultData.setStatus (code); resultData.setMessage (message); return resultData;}}

Define status code

Public enum ReturnCode {/ * * operation succeeded * * / RC100 (100, "operation succeeded"), / * * operation failed * * / RC999 (999, "operation failure"), / * * service current limit * * / RC200 (200, "service current limit protection is enabled, please try again later!"), / * * service degradation * * / RC201 (201," Service degradation protection is enabled, please try again later! ") / * * hotspot parameter current limit * * / RC202 (202," hotspot parameter current limit, please try again later! "), / * * system rule does not meet * * / RC203 (203," system rule does not meet the requirements, please try again later! "), / * * authorization rule does not pass * / RC204 (204," authorization rule fails, please try again later!") / * * access_denied**/ RC403 (403, "No access, please contact the administrator to grant permission"), / * * access_denied**/ RC401 (401, "exception when anonymous users access unprivileged resources"), / * * Service exception * / RC500 (500, "system exception" Please try again later), INVALID_TOKEN (2001, "invalid access token"), ACCESS_DENIED (2003, "do not have permission to access this resource"), CLIENT_AUTHENTICATION_FAILED (1001, "client authentication failed"), USERNAME_OR_PASSWORD_ERROR (1002, "wrong username or password"), UNSUPPORTED_GRANT_TYPE (1003, "unsupported authentication mode") / * * Custom status code * * / private final int code; / * * Custom description * * / private final String message; ReturnCode (int code, String message) {this.code = code; this.message = message;} public int getCode () {return code;} public String getMessage () {return message;}}

Unified return format

@ GetMapping ("/ hello") public ResultData getStr () {return ResultData.success ("hello,javadaily");}

At this point, the returned value obtained by calling the API is as follows:

{"status": 100, "message": "hello,javadaily", "data": null, "timestamp": 1625736481648, "httpStatus": 0}

This has indeed achieved the desired results, as I have seen in many projects, when the Controller layer wrappers the returned results through ResultData.success () and returns them to the front end.

Seeing here, we might as well stop and think, what are the disadvantages of doing so?

The biggest drawback is that we need to call ResultData.success () to wrap the results every time we write an interface, which is repetitive and a waste of energy, and it is easy to be laughed at by other old birds.

So we need to optimize the code, and the goal is not to manually define the ResultData return value for every interface.

Advanced implementation mode

To optimize this code is simple, we only need to use the ResponseBodyAdvice provided by SpringBoot.

The role of ResponseBodyAdvice: intercept the return value of the Controller method, uniformly deal with the return value / response body, generally used to unify the return format, encryption and decryption, signature and so on.

Let's first take a look at the source code of ResponseBodyAdvice:

Public interface ResponseBodyAdvice {/ * whether support advice function * true support, false does not support * / boolean supports (MethodParameter var1, Class > var2) / * process the returned data * / @ Nullable T beforeBodyWrite (@ Nullable T var1, MethodParameter var2, MediaType var3, Class > var4, ServerHttpRequest var5, ServerHttpResponse var6);}

We just need to write a concrete implementation class.

/ * @ author jam * @ date 10:10 on 2021-7-8 * / @ RestControllerAdvicepublic class ResponseAdvice implements ResponseBodyAdvice {@ Autowired private ObjectMapper objectMapper; @ Override public boolean supports (MethodParameter methodParameter, Class > aClass) {return true;} @ SneakyThrows @ Override public Object beforeBodyWrite (Object o, MethodParameter methodParameter, MediaType mediaType, Class > aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {if (o instanceof String) {return objectMapper.writeValueAsString (ResultData.success (o)) } return ResultData.success (o);}}

There are two things to pay attention to:

@ RestControllerAdvice comment

@ RestControllerAdvice is an enhancement of the @ RestController annotation and can implement three functions:

Global exception handling

Global data binding is complete

Local data preprocessing

String type judgment

If (o instanceof String) {return objectMapper.writeValueAsString (ResultData.success (o));}

This code must be added. If Controller returns String directly, SpringBoot returns directly, so we need to convert it to json manually.

After the above processing, we no longer need to convert through ResultData.success (), directly return to the original data format, SpringBoot automatically helps us achieve the wrapper class encapsulation.

@ GetMapping ("/ hello") public String getStr () {return "hello,javadaily";}

At this point, the data returned by calling the API is:

@ GetMapping ("/ hello") public String getStr () {return "hello,javadaily";}

Does it feel perfect? don't worry, there's a question waiting for you.

Interface exception problem

There is a problem at this time, because we do not handle the exception of Controller, when the method we call has an exception, there will be a problem, such as the following interface

@ GetMapping ("/ wrong") public int error () {int I = 9 + 0; return I;}

The returned result is:

This is obviously not the result we want. The interface has been reported incorrectly and the response code for successful operation has been returned. The front end will hit people if you read it.

Don't worry, let's move on to the second issue, how to handle global exceptions gracefully.

Why does SpringBoot need global exception handlers?

Try...catch is captured uniformly by global exception handlers without handwriting.

The biggest advantage of using global exception handlers is that programmers no longer need to write try...catch when writing code. As we mentioned earlier, the result returned when an exception occurs in SpringBoot by default is like this:

{"timestamp": "2021-07-08T08:05:15.423+00:00", "status": 500,500, "error": "Internal Server Error", "path": "/ wrong"}

This data format is returned to the front end, which is incomprehensible to the front end, so at this time we usually handle exceptions through try...catch.

@ GetMapping ("/ wrong") public int error () {int i; try {I = 9 + 0;} catch (Exception e) {log.error ("error: {}", e); I = 0;} return I;}

Our goal is certainly not to write try...catch manually, but to be handled by global exception handlers.

Custom exceptions can only be handled through a global exception handler

@ GetMapping ("error1") public void empty () {throw new RuntimeException ("custom exception");}

When we introduce the Validator parameter validator, if the parameter verification fails, an exception will be thrown, which cannot be caught by try...catch, and can only be used as a global exception handler.

SpringBoot integrated parameter verification please refer to this article SpringBoot development secret book-integrated parameter verification and high-level skills

How to implement a global exception handler

@ Slf4j@RestControllerAdvicepublic class RestExceptionHandler {/ * default global exception handling. * @ param e the e * @ return ResultData * / @ ExceptionHandler (Exception.class) @ ResponseStatus (HttpStatus.INTERNAL_SERVER_ERROR) public ResultData exception (Exception e) {log.error ("Global exception Information ex= {}", e.getMessage (), e); return ResultData.fail (ReturnCode.RC500.getCode (), e.getMessage ());}}

There are three details to explain:

Enhanced class of @ RestControllerAdvice,RestController, which can be used to implement global exception handlers

@ ExceptionHandler, which uniformly handles a certain type of exception, thus reducing the repetition rate and complexity of the code. For example, to get a custom exception, @ ExceptionHandler (BusinessException.class)

@ ResponseStatus specifies the http status code received by the client

Experience effect

At this point, we call the following API:

@ GetMapping ("error1") public void empty () {throw new RuntimeException ("custom exception");}

The result returned is as follows:

{"status": 500, "message": "Custom exception", "data": null, "timestamp": 1625795902556}

It basically meets our needs.

However, a new problem arises when we enable both the uniform standard format encapsulation feature ResponseAdvice and the RestExceptionHandler global exception handler:

{"status": 100, "message": "Operation successful", "data": {"status": 500, "message": "Custom exception", "data": null, "timestamp": 1625796167986}, "timestamp": 1625796168008}

The result returned at this time is like this, and the uniform format enhancement will encapsulate the returned exception result again, so we need to solve this problem next.

Standard format returned by global exception access

It is easy to connect the global exception to the standard format, because the global exception handler has encapsulated the standard format for us, and we just need to return it directly to the client.

@ SneakyThrows@Overridepublic Object beforeBodyWrite (Object o, MethodParameter methodParameter, MediaType mediaType, Class > aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {if (o instanceof String) {return objectMapper.writeValueAsString (ResultData.success (o));} if (o instanceof ResultData) {return o;} return ResultData.success (o);}

Key code:

If (o instanceof ResultData) {return o;}

If the returned result is a ResultData object, you can return it directly.

At this time, we call the above error method, and the returned result meets our requirements.

{"status": 500, "message": "Custom exception", "data": null, "timestamp": 1625796580778} this is the end of the introduction on "how SpringBoot unifies the back-end return format". Thank you for reading. If you want to know more about the industry, you can follow the industry information channel. The editor will update different knowledge points for you every day.

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: 0

*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