Spring Boot Error Response

本文翻译自:Spring Boot Error Responses

在昨天的文章最后,我提到一个问题,就是我的例子对错误处理的设计不够。按照RESTful的设计,既然请求是借助HTTP的方法,那么返回信息也应该借助HTTP的状态码和其他信息。经过查找资料,决定将这篇文章中提到的例子实践一次,并用我的话总结下。

首先我们需要新建一个简单的Controller,代码如下:

@RestController
class GreetingController {

    @RequestMapping("/greet")
    String sayHello(@RequestParam("name") String name) {
        if (name == null || name.isEmpty()) {
            throw new IllegalArgumentException("The 'name' parameter must not be null or empty");
        }
        return String.format("Hello %s!", name);
    }
}

通过http请求客户端——httpie发送HTTP请求,这个工具比curl的好处是:返回值信息有语法高亮、对返回的JSON字符串自动格式化。启动服务器,使用命令http http://127.0.0.1:8080/greet?name=duqi发起请求,结果如下:

HTTP/1.1 200 OK
Content-Length: 11
Content-Type: text/plain;charset=UTF-8
Date: Sat, 05 Dec 2015 05:45:03 GMT
Server: Apache-Coyote/1.1
X-Application-Context: application

现在我们制造一个错误的请求,@RequestParam是获取URL中的参数,如果这个参数不提供则会出错。因此,我们发送一个命令http http://127.0.0.1:8080,看结果如何。

HTTP/1.1 400 Bad Request
Connection: close
Content-Type: application/json;charset=UTF-8
Date: Sat, 05 Dec 2015 05:54:06 GMT
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
X-Application-Context: application

{
    "error": "Bad Request",
    "exception": "org.springframework.web.bind.MissingServletRequestParameterException",
    "message": "Required String parameter 'name' is not present",
    "path": "/greet",
    "status": 400,
    "timestamp": 1449294846060
}

可以看到,由于没有提供name参数,服务器返回的状态码是400:错误的请求。在响应体中的内容依次如下:

  • error : 错误信息;
  • exception:异常的类型,MissingServletRequestParameterExeption,见名知意,说明是缺少了某个请求参数;
  • message:对异常的说明
  • path:显示请求的URL路径;
  • status:表示返回的错误码
  • timestamp:错误发生的时间戳,调用System.currentMills()

如果我们给定name参数,却不给它赋值,又会如何?发送命令http http:127.0.0.1:8080/greet?name,则服务器的返回值如下:

HTTP/1.1 500 Internal Server Error
Connection: close
Content-Type: application/json;charset=UTF-8
Date: Sat, 05 Dec 2015 06:01:24 GMT
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
X-Application-Context: application

{
    "error": "Internal Server Error",
    "exception": "java.lang.IllegalArgumentException",
    "message": "The 'name' parameter must not be null or empty",
    "path": "/greet",
    "status": 500,
    "timestamp": 1449295284160
}

对比上面,可以看出,这次返回的错误码是500,表示服务器内部错误;返回的异常类型是java.lang.IllegalArgumentException,表示参数不合法。服务器内部错误表示服务器抛出了异常缺没有处理,我们更愿意API返回400,告诉调用者自己哪里做错了。如何实现呢?利用@ExceptionHandler注解即可。

在GreetingController控制器中加入如下处理函数,用于捕获这个控制器的异常。

@ExceptionHandler
void handleIllegalArgumentException(IllegalArgumentException e,
                        HttpServletResponse response) throws IOException {
      response.sendError(HttpStatus.BAD_REQUEST.value());
}

再次发送命令http http:127.0.0.1:8080/greet?name,则返回下面的结果:

HTTP/1.1 400 Bad Request
Connection: close
Content-Type: application/json;charset=UTF-8
Date: Sat, 05 Dec 2015 06:08:50 GMT
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
X-Application-Context: application

{
    "error": "Bad Request",
    "exception": "java.lang.IllegalArgumentException",
    "message": "The 'name' parameter must not be null or empty",
    "path": "/greet",
    "status": 400,
    "timestamp": 1449295729978
}

说明我们在服务器端捕获了IllegalArgumentException这个异常,并设置response的返回码为400。如果你想对多个异常都进行一样的处理,则上述异常处理代码可以修改为下面这样(给@ExceptionHandler传入参数):

@ExceptionHandler({IllegalArgumentException.class, NullPointerException.class})
void handleIllegalArgumentException(HttpServletResponse response) throws IOException {
    response.sendError(HttpStatus.BAD_REQUEST.value());
}

现在这个异常处理代码是加在当前的这个控制器中,因此它只处理属于这个控制器的响应,如果我们新建一个类,并用注解@ControllerAdvice修饰,并在这个类中定义上述的异常处理代码,则它会负责处理所有的请求。

Spring Boot 1.2.0以后,还支持在response修改对应的message,只要将对应的message信息传入sendError函数即可,例如:

@ExceptionHandler({IllegalArgumentException.class, NullPointerException.class})
void handleIllegalArgumentException(HttpServletResponse response) throws IOException {
    response.sendError(HttpStatus.BAD_REQUEST.value(),
         "Please try again and with a non empty string as 'name'");
}

再次执行同样的命令,会收到下列反馈:

HTTP/1.1 400 Bad Request
Connection: close
Content-Type: application/json;charset=UTF-8
Date: Sat, 05 Dec 2015 06:21:05 GMT
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked
X-Application-Context: application

{
    "error": "Bad Request",
    "exception": "java.lang.IllegalArgumentException",
    "message": "Please try again and with a non empty string as 'name'",
    "path": "/greet",
    "status": 400,
    "timestamp": 1449296465060
}

如果希望验证请求的参数,可以使用JSR-303 Bean Validation API,并参考Spring Validation。在spring.io上还有一个验证表单输入的例子Validating Form Input

参考资料

  1. 模拟GET/POST请求的工具
  2. Spring Boot Error Response
时间: 2025-01-02 14:15:29

Spring Boot Error Response的相关文章

RESTful by Spring Boot with MySQL

现在的潮流是前端承担越来越多的责任:MVC中的V和C,后端只需要负责提供数据M,但是后端有更重要的任务:高并发.提供各个维度的扩展能力(负载均衡.数据表切分.服务分离).更清晰的API设计.Spring Boot框架提供的机制便于工程师实现标准的RESTful接口,本文主要讨论如何编写Controller代码,另外还涉及了MySQL的数据库操作,之前我也写过一篇关于Mysql的文章link,但是这篇文章加上了CRUD的操作. 先回顾下之前的文章中我们用到的例子:图书信息管理系统,主要的领域对象有

Spring Boot 之 RESRful API 权限控制

一.为何用RESTful API 1.1 RESTful是什么? RESTful(Representational State Transfer)架构风格,是一个Web自身的架构风格,底层主要基于HTTP协议(ps:提出者就是HTTP协议的作者),是分布式应用架构的伟大实践理论.RESTful架构是无状态的,表现为请求-响应的形式,有别于基于Bower的SessionId不同.   1.2理解REST有五点: 1.资源  2.资源的表述  3.状态的转移  4.统一接口  5.超文本驱动 需要理

一篇文章学会spring boot(包括jms和hessian的集成)

之前在学习spring cloud微服务的时候,由于spring cloud的基础是spring boot,因此曾简单地了解过spring boot,但也只是简单的了解过而已. 而现在,需要把struts2项目改为spring boot,一开始时以为是整个项目重构,不仅限于struts2部分,因此就相对更系统.更细致的学了一下spring boot. 整个过程由易到难,大概分成了这么些模块: 一.创建简单的spring boot web项目 很多时候学一个新的东西,都需要从最简单的地方开始,然后

Spring Boot中集成Spring Security 专题

if语句中条件判断就是检查当前的url请求是否是logout-url的配置值,接下来,获取用户的authentication,并循环调用处理器链中各个处理器的logout()函数,前面在parse阶段说过,处理器链中有两个实例,处理会话的SecurityContextLogoutHandler及remember-me服务,我们来一一看看它们的logout函数实现: 2.1.0 SecurityContextLogoutHandler public void logout(HttpServletR

Spring Boot 项目构建 之 使用 Spring Boot 构建应用(Building an Application with Spring Boot)

Table of contents What you'll build What you'll need How to complete this guide Build with Gradle Build with Maven Build with Spring Tool Suite Learn what you can do with Spring Boot Create a simple web application Create an Application class Run the

《Spring Boot官方指南》翻译邀请

学技术并发网始终建议同学们阅读原版官方文档,所以并发网每月都会组织大家翻译各种官方技术文档.9月份并发网组织大家翻译<Spring Boot官方指南>,欢迎有兴趣的同学参与. 如何领取  通过评论领取想要翻译的文章,每次领取一章,如Spring Boot Documentation,翻译完后再领取其他章节.领取完成之后,建议在一个星期内翻译完成,如果不能完成翻译,也欢迎你邀请其他同学和你一起完成翻译. 如何提交? 翻译完成之后请登录到并发网提交成待审核状态,会有专门的编辑校对后进行发布.校对完

spring boot actuator专题

spring-boot-starter-actuator模块的实现对于实施微服务的中小团队来说,可以有效地减少监控系统在采集应用指标时的开发量.当然,它也并不是万能的,有时候我们也需要对其做一些简单的扩展来帮助我们实现自身系统个性化的监控需求.下面,在本文中,我们将详解的介绍一些关于spring-boot-starter-actuator模块的内容,包括它的原生提供的端点以及一些常用的扩展和配置方式. /autoconfig:该端点用来获取应用的自动化配置报告,其中包括所有自动化配置的候选项.同

Spring Boot MyBatis 通用Mapper插件集成 good

看本文之前,请确保你已经在SpringBoot中集成MyBatis,并能正常使用.如果没有,那么请先移步 http://blog.csdn.net/catoop/article/details/50553714 做了解后,再按本文步骤操作. 使用MyBatis在我们通过xml集中配置SQL,并通过创建接口Mapper文件来完成持久化DAO层(mybatis内部使用的是动态代理,所以我们不需要自己编写实现类). 然而在实际开发中,单表操作非常多,如果你也想像JPA.JDBC那样做一个所谓的Base

5.28. Spring boot with Elasticsearch 5.5.x

Spring data 目前还不支持 Elasticsearch 5.5.x 所以需要通过注入 TransportClient 这就意味着使用 5.5.x 版本你无法使用 ElasticsearchRepository 这种特性,只能通过官方的 TransportClient 操作 Elasticsearch. 5.28.1. Maven Elasticsearch 依赖下来四个包 <dependency> <groupId>org.elasticsearch</groupI