The general system structure is as follows:
image
What needs to be explained is that some small partners will reply that this architecture is too simple and too low. There are no gateways, caches, or message middleware. Because this article mainly talks about the API interface, we focus on this point.
interface interaction
The front-end and the back-end interact. The front-end requests the URL path according to the agreement and passes in relevant parameters. The back-end server receives the request, performs business processing, and returns data to the front-end.
For the restful style of the URL path, and the requirements of the public request headers of the incoming parameters (such as: app_version, api_version, device, etc.), I will not introduce them here. Friends can learn by themselves, and it is relatively simple.
How does the backend server return data to the frontend?
return format
The backend returns to the frontend. We generally use the JSON body, which is defined as follows:
{
#返回状态码
code:integer,
#返回信息描述
message:string,
#返回值
data:object
}
CODE status code
code returns the status code. Generally, friends add whatever they need during development.
If the interface wants to return a user permission exception, let’s add a status code of 101. Next time we need to add a data parameter exception, we will add a status code of 102. Although this can satisfy the business as usual, the status code is too messy
We should be able to refer to the status code returned by the HTTP request
:下面是常见的HTTP状态码:
200 - 请求成功
301 - 资源(网页等)被永久转移到其它URL
404 - 请求的资源(网页等)不存在
500 - 内部服务器错误
image.gif
We can refer to such a design, which has the advantage of classifying the error types into a certain range. If the range is not enough, it can be designed as 4 digits.
#1000~1999 区间表示参数错误
#2000~2999 区间表示用户错误
#3000~3999 区间表示接口异常
In this way, after getting the return value, the front-end developer can know the approximate error according to the status code, and then quickly locate it according to the information related to the message.
Message
This field is relatively simple to understand, that is, how to give a friendly prompt when an error occurs. The general design is designed together with the code status code, such as
image.png
Then define in the enumeration, the status code
image
The status code and information will correspond one by one, which is easier to maintain.
Data
Return the data body in JSON format, and different JSON bodies according to different businesses.
We want to design a return body class Result
image
Control layer Controller
We will process business requests at the controller layer and return them to the front end, taking order as an example
image.png
We see that after obtaining the order object, we use the Result constructor to wrap and assign values, and then return. Friends, have you noticed that the packaging of the construction method is not very troublesome, we can optimize it.
Beautification
We can add static methods to the Result class to understand at a glance
image.png
Then let's transform the Controller
image.png
Is the code simpler and more beautiful?
elegant optimization
Above we saw that a static method has been added to the Result class, which makes the business processing code more concise. But friends, have you noticed that there are several problems:
1. The return of each method is a Result package object, which has no business meaning
2. In the business code, we call Result.success when it succeeds, and call Result.failure when there is an exception. Is it redundant?
3. The above code judges whether the id is null or not. In fact, we can use validate for verification, and there is no need to judge in the method body.
Our best way is to directly return the real business object, it is best not to change the previous business method, as shown in the figure below
image
This is the same as our usual code, it is very intuitive, and it returns the order object directly, isn't it perfect? What is the implementation plan?
Implementation plan
Friends, do you have some ideas on how to achieve it? In this process, we need to do a few things
1. Customize an annotation @ResponseResult, indicating that the value returned by this interface needs to be packaged
2. Intercept the request and judge whether the request needs to be annotated with @ResponseResult
3. The core step is to implement the interfaces ResponseBodyAdvice and @ControllerAdvice, determine whether the return value needs to be packaged, and if necessary, rewrite the return value of the Controller interface.
Annotation class
Used to mark the return value of the method, whether it needs to be wrapped
image
interceptor
Intercept the request, whether the value returned by this request needs to be packaged, in fact, it is to parse the @ResponseResult annotation when running
image
The core idea of this code is to get the request, whether it needs to wrap the return value, and set an attribute flag.
rewrite return body
image
The above code is to judge whether the return value packaging is needed, and if necessary, wrap it directly. Here we only deal with the normal successful packaging, what if the method body reports an exception? It is also relatively simple to handle exceptions, just judge whether the body is an exception class.
image
Rewrite Controller
image
Add a custom annotation @ResponseResult on the controller class or method body, so it's ok, easy. The design idea returned here is completed, is it simple and elegant?
Is there any other room for optimization in this solution? Of course there is. For example: each request needs to be reflected, whether the method of obtaining the request needs to be packaged, in fact, it can be cached, and it does not need to be parsed every time. Of course, if you understand the overall idea, you can expand on this basis.