依据网络请求 - Ant Design Pro中提出的后端接口规范,后端返回的数据至少应遵循以下模板
{"success": true,"data": {},"errorMessage": "error message"}
为了帮助前端更好地完成工作,后端应强化接口规范。
首先创建一个工具包,再设想中,只要导入工具包,就会自动完成接口的转化。
pom中我们只引入spring-boot-starter-web:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency>
然后我们定义要统一返回的Message类,这里使用泛型:
public class Message<T> implements Serializable { boolean success; String errorMessage; T data; Timestamp timestamp; public Message(boolean success, String errorMessage, T data read more
SpringBoot 全局化消息返回与错误处理,兼容swagger(一)
依据网络请求 - Ant Design Pro中提出的后端接口规范,后端返回的数据至少应遵循以下模板
{
"success": true,
"data": {},
"errorMessage": "error message"
}
为了帮助前端更好地完成工作,后端应强化接口规范。
首先创建一个工具包,再设想中,只要导入工具包,就会自动完成接口的转化。
pom中我们只引入spring-boot-starter-web:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
然后我们定义要统一返回的Message类,这里使用泛型:
public class Message<T> implements Serializable {
boolean success;
String errorMessage;
T data;
Timestamp timestamp;
public Message(boolean success, String errorMessage, T data){
this.success = success;
this.errorMessage = errorMessage;
this.data = data;
this.timestamp = new Timestamp(System.currentTimeMillis());
}
public static Message failureMessage(String err){
return new Message(false, err, null);
}
public static <T> Message<T> successMessage(T data){
return new Message(true, null, data);
}
//省略getter, setter以及无参构造
}
可以看出,我们用方法failureMessage来返回一个失败信息,用方法successMessage来返回一个成功信息。
接下来我们我们分别构造错误处理与成功信息处理。
错误信息的处理是容易的,按照以上标准,只需要返回成功与否,一切错误都可视作失败,因此我们定义一个全局错误处理器,对所有错误进行统一的处理:
@ControllerAdvice
public class ExceptionAdviceConfig {
@ResponseBody
@ExceptionHandler(Throwable.class)//处理所有异常
public Message exceptionHanlder(Exception e){
return Message.failureMessage(e.getMessage());
}
}
之后我们去定义一个全局信息处理器吧,处理所有信息:
@RestControllerAdvice
public class ResponseBodyAdivceConfig implements ResponseBodyAdvice {
@Autowired
private ObjectMapper objectMapper;
/**
* 过滤器
* @param methodParameter
* @param aClass
* @return 避免重复处理Message
*/
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
return !methodParameter.getParameterType().getName().equals("org.example.utils.Message");
}
/**
*进行预处理
*/
@Override
public Object beforeBodyWrite(Object returnValue, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
String returnClassType = returnType.getParameterType().getName();
if ("java.lang.String".equals(returnClassType)){
try {
return objectMapper.writeValueAsString(Message.successMessage(returnValue));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
return Message.successMessage(returnValue);
}
}
第一个函数是过滤器,第二个是处理规则。在过滤器中,我们把自己写的Message泛型类给过滤掉了,这样能防止我们再次处理已经处理过的Message,因为我们之前已经设置了错误处理器,错误处理器会将封装的结果再次返回全局信息处理器。
这样我们用错误处理器处理失败信息,用全局信息处理器处理成功信息。
在全局信息处理器中,我们需要对String进行特殊处理,否则会报错:在Spring中,String对象不会被转换为json,所以我们需要手动转换。
当然,出于代码的美观性考虑,也可以将所有的输入信息在beforeBodyWrite中统一处理为json,这样就不需要对String单独处理了。
这样处理后,在项目中同时引入以上项目和swagger/openapi,会发现swagger/openapi无法发现接口。
我们以openapi为例,解决这个问题。
解决这个问题有两种思路,白名单与黑名单:
白名单,我们指定一个扫描范围@RestControllerAdvice(basePackages = “org.example”),这样处理器只会处理包名下面的包,但是缺少了普适性
黑名单,在过滤器中禁止掉openapi的输出类,这样openapi无法被输出,缺点是随着依赖的增加,维护成本会变大。
我们有一个更优的处理办法:
我们继承一个restcontroller注解类,让advice去扫描它,只处理用这个注解标记过的controller。
注解如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RestController
public @interface MessageController {
@AliasFor(
annotation = RestController.class
)
String value() default "";
}
用@MessageController取代@RestController,并修改advice标记:
@RestControllerAdvice(annotations = MessageController.class)//我们用自己的注释
我们这样处理controller:
@MessageController
@Tag(name = "demo", description = "测试")
public class DemoController {
...
}
大功告成!