Spring Boot 处理 REST API 错误的正确姿势
- 摘要:如何正确的处理API的返回信息,让返回的错误信息提供更多的含义是一个非常值得做的功能。默认一般返回的都是难以理解的堆栈信息,然而这些信息也许对于API的客户端来说有可能并没有多大用途,并没有多大意义。如果我们把错误的信息分成多个字段,这样api客户端就可以解析这些信息,然后给用户反馈更好的错误message。在本文中,我们就来介绍在我们使用springboot来构建RESTAPI时如何更好的更恰当的处理错误信息。使用Spring来构建RESTAPI现在基本上已经变成了java
如何正确的处理API的返回信息,让返回的错误信息提供更多的含义是一个非常值得做的功能。
默认一般返回的都是难以理解的堆栈信息,然而这些信息也许对于API的客户端来说有可能并没有多大用途,并没有多大意义。
如果我们把错误的信息分成多个字段,这样api客户端就可以解析这些信息,然后给用户反馈更好的错误message。
在本文中,我们就来介绍在我们使用spring boot来构建REST API时如何更好的更恰当的处理错误信息。
使用Spring来构建REST API现在基本上已经变成了java开发者事实上的标准。
如果你使用Spring Boot的话,就更方便了,因为它帮你搞了很多的样板代码,而且通过auto-configuration可以集成各种组件。
我们将假设你在应用此处所述的知识之前已经比较熟悉使用这些技术的API开发的基础知识。
如果你仍然不确定如何开发基本的REST API,那么你应该先去了解下有关Spring MVC的文章,或者关于构建Spring REST服务的文章。
让Error响应更清晰
在本文中,我们将使用托管在GitHub(源码spring-boot-exception-handling在文末的阅读原文里,链接:https://github.com/importsource/spring-boot-exception-handling) 上的spring-boot-exception-handling应用程序上的源代码来通过REST API来查询“鸟”这个对象。 代码里有本文中描述的功能和更多的错误处理方案的示例。 以下是该应用程序中实现的几个endpoint:
Spring框架的MVC模块提供了一些很好的功能来帮助处理错误。 但是, 它把处理异常的事情扔给了开发人员,需要开发人员自己来处理异常,然后向API客户端返回返回有意义的响应。
我们来看一下Spring Boot的默认做法。 当我们把下面的一个对象通过 HTTP POST 发送到 /bird 端点时,我们故意给“mass”字段传递一个字符串“aaa”,其实这个字段类型是一个整数:
然后我 们来看一下 Spring Boot的默认应答。没有任何额外的错误处理:
嗯。。。这个响应信息确实提供了一些不错的字段,但是它更侧重的时候抛出一个底层的变成异常。顺便说一句,这是Spring Boot中的DefaultErrorAttributes类。 时间戳字段是一个整数,甚至不携带时间戳所在的度量单位的信息。异常字段的话,可能只有Java开发人员看到这个比较开心,这些信息让API消费者也陷入了思索API服务端究竟发生了什么内部的编程错误。 如果我们从这些编程异常的内容中抽象出更多的细节是不是更好一点?那么就让我们学习下如何正确地处理这些异常,并将它们转成更好的JSON表示形式,使我们的API客户端理解起来更加的轻松。
因为我们接下来要使用到Java 8日期和时间类,我们首先就加个Jackson JSR310转换器的maven依赖。 这样我们就可以使用@JsonFormat这个注解来将Java 8的日期和时间类转换为JSON来表示:
好的,来定义一个表示API错误的类。 我们将创建一个名为ApiError的类,其具有足够的字段来保存REST调用期间发生的错误的相关信息。
status 属性:保存操作调用状态。 比如4xx客户端错误或5xx服务端错误。 一个常见的情况是比如http代码400,表示BAD_REQUEST,这种情况是当客户端例如发送了格式不正确的字段,比如一个无效的电子邮件地址。
timestamp 属性:保存发生错误的日期时间。
message 属性:保存有关错误的对用户友好的消息。
debugMessage 属性:是更详细的描述错误的系统消息。
subErrors 属性:包含发生的一系列子错误。 这用于在单个调用中出现多个错误。 比如多个字段验证失败的验证错误。ApiSubError类就是用来封装这一系列子错误的。
ApiValidationError扩展了ApiSubError。它表示在REST调用期间遇到的验证问题的类。
下面,你将看到在我们实现了这里所做的改进之后生成的JSON响应的例子,仅仅是为了了解本文接下来的内容。
下面的例子就是当一个实体没有找到后返回的样子(端点:GET /birds/2):
下面是当我们发送一个POST /birds JSON串后,里边包含了非法的值赋值给了鸟的mass字段,然后返回了如下错误信息:
SpringBoot 方式的错误处理
接下来我们介绍一下将要用于处理异常的Spring注解。
RestController
RestController是处理REST操作放置在类上的基础注解。
ExceptionHandler
ExceptionHandler 是一个Spring注解,它提供了一种机制来处理在处理程序执行过程中抛出的异常(比如Controller操作)。 这个注解(如果用于Controller类的话)将用作处理仅在此Controller中抛出的验证yi'cyi异常。 总而言之,最常用的方法是在@ControllerAdvice类的方法上使用@ExceptionHandler,以便将异常处理应用到所有的Controller或指定的Controller子集上。
ControllerAdvice
ControllerAdvice 是Spring 3.2中引入的一个注解,顾名思义,是“Advice”多个Controller。 你可以启用一个ExceptionHandler,然后让多个Controller都遵循它所定义的异常处理。 这样的话,我们就只需要在一个地方定义好如何处理某一个异常,并且当这个ControllerAdvice涵盖的类抛出该异常时,这个处理程序就将会被调用。 如果你只是希望某一些Controller受影响,那么你可以通过在@ControllerAdvice上加这几个选择器属性来限制:annotations(),basePackageClasses()和basePackages())。 如果没有添加这些选择器,则ControllerAdvice将应用于所有Controller。
所以通过使用@ExceptionHandler和@ControllerAdvice,我们可以定义一个中心点来处理异常,并将它们包装在一个比Default错误处理机制组织错误信息更好的ApiError对象中。
处理异常
下一步是创建处理异常的类。 简单点说,我们称之为RestExceptionHandler,它继承自Spring Boot的ResponseEntityExceptionHandler。 然后我们扩展ResponseEntityExceptionHandler,因为它已经提供了Spring MVC异常的一些基本处理,接下来我们就针对一些新的异常添加一些新的handler,算是对现有的handler的一个扩展和改进。
如果你看看ResponseEntityExceptionHandler的源代码,你会看到很多方法叫做handle ******(),如handleHttpMessageNotReadable()或handleHttpMessageNotWritable()。 我们先来看看,我们是如何通过扩展handleHttpMessageNotReadable()来处理HttpMessageNotReadableException异常们的。 其实只需要在RestExceptionHandler类中覆盖方法handleHttpMessageNotReadable()如下:
之前已说过,如果HttpMessageNotReadableException被抛出,错误的message将是“ Malformed JSON request( 格式错误的JSON请求)”,并且该错误将被封装在ApiError对象内。
下面就是我们扩展后的REST调用的响应json:
处理自定义异常
接下来介绍如何创建一个方法来处理在Spring Boot的ResponseEntityExceptionHandler中没有被声明处理的异常。
一个用于处理数据库调用的Spring应用程序的常见场景就是使用存储库类通过其ID来查找一条或多条记录。 但是,我们发现CrudRepository.findOne()方法中,如果找不到数据,那么就返回null。 这意味着如果我们的服务只是调用该方法并直接返回给Controller,即使没有找到资源,我们也会得到HTTP代码 200(OK)。 事实上,正确的方法应该是返回HTTP / 1.1规范中指定的HTTP code 404(NOT FOUND)。
为了处理这种情况,我们可以创建一个名为EntityNotFoundException的自定义异常。 这是一个自定义创建的异常,与javax.persistence.EntityNotFoundException不同 ,因为它提供了一些缓解对象创建的构造函数,并且可以选择以 不同方式处理javax.persistence异常。
然后,我们在RestExceptionHandler类中为这个新创建的EntityNotFoundException创建一个ExceptionHandler。 其实就是创建一个名为handleEntityNotFound()的方法,并使用@ExceptionHandler对其进行注解,将类对象EntityNotFoundException.class传递给它。 这表明Spring每次抛出EntityNotFoundException时,Spring应该调用此方法来处理它。 当使用@ExceptionHandler注解方法时,它将接受多种自动注入的参数,如WebRequest,Locale和其他。 下面我们就把异常EntityNotFoundException本身作为下面这个handleEntityNotFound方法的参数。
好! 在handleEntityNotFound()方法中,我们将HTTP状态代码设置为NOT_FOUND并使用了新的异常消息。 以下是 GET /birds/2 终端的响应:
总结
控制异常处理非常重要,所以我们可以将这些异常映射到ApiError对象,然后向API客户端提供了更有意义的信息,让客户端知道发生了什么。 然后就是如何为应用程序代码中抛出的异常创建更多的手工方法(带有@ExceptionHandler的方法)。 还有一些其他常见异常的例子,例如MethodArgumentTypeMismatchException,ConstraintViolationException等等。
https://cn.aliyun.com/jiaocheng/778288.html
Spring Boot 处理 REST API 错误的正确姿势的更多相关文章
- spring boot ---web应用开发-错误处理
一.错误的处理 方法一:Spring Boot 将所有的错误默认映射到/error, 实现ErrorController @Controller @RequestMapping(value = &qu ...
- .NET CORE与Spring Boot编写控制台程序应有的优雅姿势
本文分别说明.NET CORE与Spring Boot 编写控制台程序应有的“正确”方法,以便.NET程序员.JAVA程序员可以相互学习与加深了解,注意本文只介绍用法,不会刻意强调哪种语言或哪种框架写 ...
- 基于Spring Boot的RESTful API实践(一)
1. RESTful简述 REST是一种设计风格,是一组约束条件及原则,而遵循REST风格的架构就称为RESTful架构,资源是RESTful的核心,一个好的RESTful架构,通过URL就能很 ...
- swagger ui和spring boot集成生成api文档
作者:小莫链接:https://www.zhihu.com/question/28119576/answer/134580038来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明 ...
- Spring Boot中Restful Api的异常统一处理
我们在用Spring Boot去向前端提供Restful Api接口时,经常会遇到接口处理异常的情况,产生异常的可能原因是参数错误,空指针异常,SQL执行错误等等. 当发生这些异常时,Spring B ...
- Spring Boot:自定义 Whitelabel 错误页面
一.概述在本文中,我们将研究如何禁用和自定义 Spring Boot 应用程序的默认错误页面,因为正确的错误处理描述了专业性和质量工作. 2.禁用白标错误页面 首先,让我们看看如何通过将server. ...
- Spring Boot 之 RESRful API 权限控制
摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢! “简单,踏实~ 读书写字放屁” 一.为何用RESTful API 1.1 RESTful是什么? ...
- spring boot 下 500 404 错误页面处理
spring boot 作为微服务的便捷框架,在错误页面处理上也有一些新的处理,不同于之前的spring mvc 500的页面处理是比较简单的,用java config或者xml的形式,定义如下的be ...
- 解决spring boot JavaMailSender部分收件人错误导致发送失败的问题
使用spring boot通常使用spring-boot-starter-mail进行邮件的发送.当进行邮件群发的话,如果一个收件人的地址错误,会导致所有邮件都发送失败.因此我们需要在邮件发送失败的时 ...
随机推荐
- java面向对象(1)
一.方法传不固定值的用法 public void Test(int a,Person ...Persons) { for(Person p:Persons){ ...
- Creating a Physical Standby Database 11g
1.Environment Item Primary database standby database Platform Redhat 5.4 Redhat 5.4 Hostname gc1 gc2 ...
- python 3 操作mysql数据库的方法
参考:http://www.cnblogs.com/txw1958/archive/2012/07/22/python3-mysql.html http://www.jb51.net/article/ ...
- Free-form语言
在计算机编程领域,程序指令文本中的字符在『纸面』上所处的位置无关紧要 - 不像老式的穿孔卡片系统(punched card system)程序指令文本需要放置在指定列,这种编程语言就可算是自由形式语言 ...
- Bash Shell (十一)
[教程主题]:Bash Shell [课程录制]: 创E [主要内容] [1] Hello World! 几乎所有的讲解编程的书给读者的第一个例子都是 Hello World 程序,那么我们今天也就从 ...
- HTML5数据推送SSE原理及应用开发
JavaScript表达行为,CSS表达外观,注意HTML既表达结构(逻辑结构),又表达内容(数据本身)通常需要更新数据时,并不需要更新结构,正是这种不改变组织结构仅改变数据的诉求,推动了数据拉取和数 ...
- Java设计模式(2)单态模式(Singleton模式)
定义:Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在. 在很多操作中,比如建立目录 数据库连接都需要这样的单线程操作. 还有,singleton能够被状态化 ...
- C#注册表读写完整操作类
1.注册表基项静态域 /// <summary> /// 注册表基项静态域 ///1.Registry.ClassesRoot 对应于HKEY_CLASSES_ROOT 主键 ///2.R ...
- vmstat和iostat命令进行Linux性能监控
这是我们正在进行的Linux命令和性能监控系列的一部分.vmstat和iostat两个命令都适用于所有主要的类unix系统(Linux/unix/FreeBSD/Solaris). 如果vmstat和 ...
- 在C++中调用DLL中的函数(2)
本文转自:http://blog.sina.com.cn/s/blog_53004b4901009h3b.html 应用程序使用DLL可以采用两种方式: 一种是隐式链接,另一种是显式链接.在使用DLL ...