改进 Spring Boot REST API 错误处理

栏目: IT技术 · 发布时间: 4年前

内容简介:来自作者投稿 作者:覃佑桦dzone.com/articles/rest-api-error-handling-with-spring-boot

(给 ImportNew 加星标,提高 Java 技能)

来自作者投稿 作者:覃佑桦

dzone.com/articles/rest-api-error-handling-with-spring-boot

引言

Spring Boot提供了优秀的异常处理机制。ErrorController的默认实现能够很好地捕获和处理异常。此外,还可以自己实现@ExceptionHandler捕获和处理特定异常。然而,这里还有可以改进的空间:

·  即使采用了自定义@ExceptionHandler实现某些异常还是会漏网,这时ErrorController会进行处理。@ExceptionHandler与ErrorController的方案可以改进。

·   默认错误有时候看起来比较混乱:   

{
  "timestamp": "2018-09-23T15:05:32.681+0000",
  "status": 400,
  "error": "Bad Request",
  "errors": [
    {
      "codes": [
        "NotBlank.dto.name",
        "NotBlank.name",
        "NotBlank.java.lang.String",
        "NotBlank"
      ],
      "arguments": [
        {
          "codes": [
            "dto.name",
            "name"
          ],
          "arguments": null,
          "defaultMessage": "name",
          "code": "name"
        }
      ],
      "defaultMessage": "{name.not_blank}",
      "objectName": "dto",
      "field": "name",
      "rejectedValue": null,
      "bindingFailure": false,
      "code": "NotBlank"
    }
  ],
  "message": "Validation failed for object='dto'. Error count: 1",
  "path": "/"
}

当然,我们可以注册自定义ErrorAttributes达成这一点,但是如果默认错误实现改进一下岂不更好。

·   对于验证产生的错误,可以把约束条件中的某些参数开放给信息显示。比如可以使用{}占位符把int最小值传给消息显示。但是这种方式并不适用其它异常处理:   

public class UserAlreadyExistsException extends RuntimeException {

    // How can I expose this value to the interpolated message?
    private final String username;

    // constructors and getters and setters
}		

·   如果所有异常都支持应用级的内置错误码当然是极好的。有时候,只有HTTP状态码根本无法定位问题。比如同一个EndPoint发生了不同的错误,报告了同一个状态码该如何分析?

改进方案

errors-spring-boot-starter为各种异常提供了统一处理。errors-spring-boot-starter在Spring Boot异常处理机制基础上提供了:

·   所有异常处理实现一致:不论是验证或绑定错误、domain错误、甚至Spring相关的错误都没有问题。所有异常都交由WebErrorHandler实现处理(不再需要ErrorController与@ExceptionHandler)

·   内建支持应用级错误码。

·   使用MessageSource进行简单的信息插入。

·   支持HTTP错误码自定义显示。

·   允许异常参数暴露给错误消息使用。

默认错误显示

错误的默认格式为JSON,schema定义如下:    

// errors数组为每个错误都定义了code/messsge组合
{
  "errors": [
    {
      "code": "first_error_code",
      "message": "1st error message"
    }
  ]
}

自定义显示只要注册HttpErrorAttributesAdapter实现一个Spring Bean。

统一错误处理

所有异常都交给WebErrorHandler处理。Starter 默认会查询内建WebErrorHandler处理以下异常:

·   所有验证或绑定异常。

·   所有带@ExceptionMapping注解的自定义异常。

·  Spring MVC异常。

·   Spring Security如果在classpath中也会处理。

也可以实现WebErrorHandler注册一个自定义异常处理Spring Bean。

内建错误码支持

尽管RESTful API推荐使用HTTP状态代码,但有时我们需要更多信息定位问题。这时候就需要使用错误码。可以把错误代码看成可读性更好的错误描述机器码。每个异常可以映射至少一个错误码。

exception-to-error-code根据不同的异常类型有所差别:

·   验证产生的错误码从注解中message属性提取,例如@NotBlank(message = "name.required")。

·   @ExceptionMapping注解中的errorCode属性看起来像下面这样:    

@ExceptionMapping(statusCode = BAD_REQUEST, errorCode = "user.already_exists")
public class UserAlreadyExistsException extends RuntimeException {}

·   下面是WebErrorHandler自定义实现:    

public class ExistedUserHandler implements WebErrorHandler {

    @Override
    public boolean canHandle(Throwable exception) {
        return exception instanceof UserAlreadyExistsException;
    }

    @Override
    public HandledException handle(Throwable exception) {
        return new HandledException("user.already_exists", BAD_REQUEST, null);
    }
}

暴露参数

与Spring Boot一样,你也可以通过注解传递验证参数,例如用@Min(value = 18, message = "age.min")把参数内插到显示的消息中:

age.min = The minimum age is {0}!

此外,还可以使用@ExposeAsArg注解自定义异常。例如,如果在下面的消息中报告username已被占用:

user.already_exists=Another user with the '{0}' username already exists

实现代码:    

@ExceptionMapping(statusCode = BAD_REQUEST, errorCode = "user.already_exists")
public class UserAlreadyExistsException extends RuntimeException {
    @ExposeAsArg(0) private final String username;

    // constructor
}

总结

本文介绍了Errors-spring-boot-starter对Spring Boot异常处理的一些改进。了解更多信息请移步GitHub。

看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

改进 Spring Boot REST API 错误处理

好文章,我 在看 :heart:


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

暗时间

暗时间

刘未鹏 / 电子工业出版社 / 2011-7 / 35.00元

2003年,刘未鹏在杂志上发表了自己的第一篇文章,并开始写博客。最初的博客较短,也较琐碎,并夹杂着一些翻译的文章。后来渐渐开始有了一些自己的心得和看法。总体上在这8年里,作者平均每个月写1篇博客或更少,但从未停止。 刘未鹏说—— 写博客这件事情给我最大的体会就是,一件事情如果你能够坚持做8年,那么不管效率和频率多低,最终总能取得一些很可观的收益。而另一个体会就是,一件事情只要你坚持得足......一起来看看 《暗时间》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

MD5 加密
MD5 加密

MD5 加密工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具