为什么?

前后端拆散的时代,如果没有对立的返回格局,给前端的后果各式各样,预计前端的小伙伴就要骂娘了。
咱们想对自定义异样抛出指定的状态码排查谬误,对系统的不可预知的异样抛出敌对一点的异样信息。
咱们想让接口对立返回一些额定的数据,例如接口执行的工夫等等。
...
所以嘛,不论是日常和前端小伙伴对接,还是和其余部门进行接口对接,都应该返回固定的格局。
本文给出一个简略通用的返回格局,小伙伴们有需要,能够基于此版本依据本人的业务需要丰盛返回格局。

返回数据格式如下,有趣味的小伙伴们能够持续往下看SpringBoot是怎么来实现的。

{  "status": true,  "code": "0000",  "msg": "",  "data": {    "id": 1,    "deptId": 103,    "userName": "admin",    "nickName": "笑小枫",    "userType": "00",    "email": "xxf@163.com",    "phone": "15888888888",    "status": "0",    "remark": "管理员"  }}

怎么做?

首先创立一个测试的Controller,代码如下

package com.maple.demo.controller;import com.maple.demo.util.ResultJson;import lombok.Data;import lombok.RequiredArgsConstructor;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/** * @author 笑小枫 * @date 2022/07/15 */@RestController@RequiredArgsConstructor@RequestMapping("/example")public class TestResultController {    @GetMapping("/testResult")    public Test testResult() {        Test test = new Test();        test.setName("笑小枫");        test.setAge(18);        test.setRemark("大家好,我是笑小枫,喜爱我的小伙伴点个赞呗");        return test;    }    @GetMapping("/testResultJson")    public ResultJson testResultJson() {        Test test = new Test();        test.setName("笑小枫");        test.setAge(18);        test.setRemark("大家好,我是笑小枫,喜爱我的小伙伴点个赞呗");        return new ResultJson(test);    }    @Data    static class Test {        private String name;        private Integer age;        private String remark;    }}

咱们先看看这是调用返回的后果

{  "name": "笑小枫",  "age": 18,  "remark": "大家好,我是笑小枫,喜爱我的小伙伴点个赞"}

而后在util包下定义一个对立的返回格局类,代码如下

package com.maple.demo.util;import lombok.Data;/** * 对立返回信息包装类 * * @author 笑小枫 * @date 2022/07/15 */@Datapublic class ResultJson {    private static final String SUCCESS_CODE = "0000";    /**     * 成功失败的状态值,true:胜利;false:失败     * 这里返回编码为:“0000”,零碎就认为接口胜利;其余值,代表失败     */    private Boolean status;    /**     * 状态码 正确为0000     */    private String code;    /**     * 返回提示信息     */    private String msg;    /**     * 返回数据     */    private Object data;    public ResultJson() {        this.status = true;        this.code = SUCCESS_CODE;        this.msg = "";    }    public ResultJson(Object data) {        this.status = true;        this.code = SUCCESS_CODE;        this.msg = "";        this.data = data;    }    public ResultJson(String code, String msg) {        this.status = SUCCESS_CODE.equals(code);        this.code = code;        this.msg = msg;    }    /**     * 如果返回状态码非0000,且接口状态为胜利,请应用这个     * @param status 接口申请状态     * @param code 返回code值     * @param msg 返回音讯     * @param data 返回数据     */    public ResultJson(Boolean status, String code, String msg, Object data) {        this.status = status;        this.code = code;        this.msg = msg;        this.data = data;    }}

一般版,代码侵入性强

这样咱们能够把后果对立放在ResultJson外面返回,代码如下

package com.maple.demo.controller;import com.maple.demo.util.ResultJson;import lombok.Data;import lombok.RequiredArgsConstructor;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/** * @author 笑小枫 * @date 2022/07/15 */@RestController@RequiredArgsConstructor@RequestMapping("/example")public class TestResultController {    @GetMapping("/testResult")    public Test testResult() {        Test test = new Test();        test.setName("笑小枫");        test.setAge(18);        test.setRemark("大家好,我是笑小枫,喜爱我的小伙伴点个赞呗");        return test;    }    /**     * 一般的返回     */    @GetMapping("/testResultJson")    public ResultJson testResultJson() {        Test test = new Test();        test.setName("笑小枫");        test.setAge(18);        test.setRemark("大家好,我是笑小枫,喜爱我的小伙伴点个赞呗");        return new ResultJson(test);    }    @Data    static class Test {        private String name;        private Integer age;        private String remark;    }}

咱们看一下返回的后果

{  "status": true,  "code": "0000",  "msg": "",  "data": {    "name": "笑小枫",    "age": 18,    "remark": "大家好,我是笑小枫,喜爱我的小伙伴点个赞呗"  }}

切面解决,代码无侵入

上述代码尽管实现了性能,但所有的返回后果都要解决,对代码有比拟强的侵入,Spring领有各种切面的反对,让咱们看看如何代码无侵入的实现这个性能。

最初创立一个配置类,增加@RestControllerAdvice注解,代码如下

package com.maple.demo.config;import com.alibaba.fastjson.JSON;import com.maple.demo.util.ResultJson;import lombok.extern.slf4j.Slf4j;import org.jetbrains.annotations.NotNull;import org.springframework.core.MethodParameter;import org.springframework.http.MediaType;import org.springframework.http.converter.HttpMessageConverter;import org.springframework.http.server.ServerHttpRequest;import org.springframework.http.server.ServerHttpResponse;import org.springframework.web.bind.annotation.RestControllerAdvice;import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;/** * 对返回后果对立进行解决,包含返回后果格局对立包装,返回异样对立解决 * * @author 笑小枫 * @date 2022/07/15 */@Slf4j@RestControllerAdvicepublic class RestResponseAdvice implements ResponseBodyAdvice<Object> {    @Override    public boolean supports(@NotNull MethodParameter returnType,                            @NotNull Class<? extends HttpMessageConverter<?>> converterType) {        return true;    }    /**     * 返回后果包装对立返回格局     *     * @return 包装后的返回后果     */    @Override    public Object beforeBodyWrite(Object body,                                  @NotNull MethodParameter returnType,                                  @NotNull MediaType selectedContentType,                                  @NotNull Class<? extends HttpMessageConverter<?>> selectedConverterType,                                  @NotNull ServerHttpRequest request,                                  ServerHttpResponse response) {        // 指定返回的后果为application/json格局        // 不指定,String类型转json后返回Content-Type是text/plain;charset=UTF-8        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);        ResultJson result = new ResultJson(body);        // 若返回类型是ResultJson,则不进行批改        if (body == null) {            if (returnType.getParameterType().isAssignableFrom(String.class)) {                return JSON.toJSONString(result);            }        } else if (body instanceof ResultJson) {            return body;        } else if (body instanceof String) {            return JSON.toJSONString(result);        }        return result;    }}

这样就实现了对立后果的返回✌

咱们再申请文章最开始写的测试方法http://127.0.0.1:6666/testResult看一下返回的后果

{  "status": true,  "code": "0000",  "msg": "",  "data": {    "name": "笑小枫",    "age": 18,    "remark": "大家好,我是笑小枫,喜爱我的小伙伴点个赞呗"  }}

这样对立返回格局就实现了,对代码没有的侵入,原来的代码该怎么写还是怎么写,是不是很nice。

@RestControllerAdvice介绍

首先理解一下SpringBoot的@RestControllerAdvice注解,它是Spring框架3.2新增的的注解
点进去这个注解源码,能够看到它是由@ControllerAdvice@ResponseBody的组合注解
它通常用来定义@ExceptionHandler@InitBinder以及@ModelAttribute实用于所有办法@RequestMapping的办法。
直白点说,这就是一个加强Controller的注解。次要实现以下三个方面的性能
  1. 全局异样解决
  2. 全局数据预处理
  3. 全局数据绑定

咱们看一下@RestControllerAdvice注解的源码

@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@ControllerAdvice@ResponseBodypublic @interface RestControllerAdvice {    @AliasFor(            annotation = ControllerAdvice.class    )    String[] value() default {};    @AliasFor(            annotation = ControllerAdvice.class    )    String[] basePackages() default {};    @AliasFor(            annotation = ControllerAdvice.class    )    Class<?>[] basePackageClasses() default {};    @AliasFor(            annotation = ControllerAdvice.class    )    Class<?>[] assignableTypes() default {};    @AliasFor(            annotation = ControllerAdvice.class    )    Class<? extends Annotation>[] annotations() default {};}

应用@RestControllerAdvice常做的两个性能的实现

  1. 返回对立格局的后果
  2. 异样对立解决

小结

好啦,本文就到这里了,咱们简略的总结一下,次要介绍了以下内容

  • 为什么要返回对立的后果信息
  • 手动封装对立的返回信息
  • 应用@RestControllerAdvice主动封装返回信息
  • 简略介绍@RestControllerAdvice注解
本文源码:https://github.com/hack-feng/maple-demo