本文首发于个人网站:http://www.javaadu.online/,如需转载,请注明出处

使用Spring Boot开发微服务的过程中,我们会使用别人提供的接口,也会设计接口给别人使用,这时候微服务应用之间的协作就需要有一定的规范。

  • 基于rpc协议,我们一般有两种思路:(1)提供服务的应用统一将异常包起来,然后用错误码交互;(2)提供服务的应用将运行时异常抛出,抛出自定义的业务异常,服务的调用者通过异常catch来处理异常情况。

  • 基于HTTP协议,那么最流行的就是RESTful协议,服务提供方会自己处理所有异常,并且返回的结果中会跟HTTP的状态码相结合,这篇文章我们就用一个例子来说明RESTful接口的错误处理如何做。

首先我们需要新建一个简单的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

Spring Boot 1.x系列

  1. Spring Boot的自动配置、Command-line-Runner
  2. 了解Spring Boot的自动配置
  3. Spring Boot的@PropertySource注解在整合Redis中的使用
  4. Spring Boot项目中如何定制HTTP消息转换器
  5. Spring Boot整合Mongodb提供Restful接口
  6. Spring中bean的scope
  7. Spring Boot项目中使用事件派发器模式

本号专注于后端技术、JVM问题排查和优化、Java面试题、个人成长和自我管理等主题,为读者提供一线开发者的工作和成长经验,期待你能在这里有所收获。

Spring Boot提供RESTful接口时的错误处理实践的更多相关文章

  1. spring boot https --restful接口篇

    我们写的接口默认都是http形式的,不过我们的接口很容易被人抓包,而且一抓全是明文的挺尴尬的 spring boot配置https生成证书大的方向有3种: 1.利用keytool自己生成证书 2.从免 ...

  2. 聊一聊 Spring Boot 中 RESTful 接口设计规范

    在设计接口时,有很多因素要考虑,如接口的业务定位,接口的安全性,接口的可扩展性.接口的稳定性.接口的跨域性.接口的协议规则.接口的路径规则.接口单一原则.接口过滤和接口组合等诸多因素,本篇文章将简要分 ...

  3. 无规矩不成方圆,聊一聊 Spring Boot 中 RESTful 接口设计规范

    在设计接口时,有很多因素要考虑,如接口的业务定位,接口的安全性,接口的可扩展性.接口的稳定性.接口的跨域性.接口的协议规则.接口的路径规则.接口单一原则.接口过滤和接口组合等诸多因素,本篇文章将简要分 ...

  4. 通过spring boot提供restful api

    1 将返回设置为produces = "application/json" 返回给客户端json格式的response. 2 对各种异常的处理 各种异常如何返回给客户端? 各种异常 ...

  5. 使用Spring Boot操作Hive JDBC时,启动时报出错误:NoSuchMethodError: org.eclipse.jetty.servlet.ServletMapping.setDef

    使用Spring Boot操作Hive JDBC时,启动时报出错误:NoSuchMethodError: org.eclipse.jetty.servlet.ServletMapping.setDef ...

  6. Spring Boot提供的特性

    一.导览 本文主要按以下模块介绍spring Boot(1.3.6.RELEASE)提供的特性. SpringApplication类 外部化配置 Profiles 日志 开发WEB应用 Securi ...

  7. Spring Boot2 系列教程(三十一)Spring Boot 构建 RESTful 风格应用

    RESTful ,到现在相信已经没人不知道这个东西了吧!关于 RESTful 的概念,我这里就不做过多介绍了,传统的 Struts 对 RESTful 支持不够友好 ,但是 SpringMVC 对于 ...

  8. Spring Boot构建 RESTful 风格应用

    Spring Boot构建 RESTful 风格应用 1.Spring Boot构建 RESTful 风格应用 1.1 实战 1.1.1 创建工程 1.1.2 构建实体类 1.1.4 查询定制 1.1 ...

  9. spring boot: GlobalDefaultExceptionHandler方法内的友好错误提示,全局异常捕获

    spring boot: GlobalDefaultExceptionHandler方法内的友好错误提示,全局异常捕获 当你的某个控制器内的某个方法报错,基本上回显示出java错误代码,非常不友好,这 ...

随机推荐

  1. Docker学习之docker常用命令

    docker ps -a 表示所有容器 docker pull 获取image docker build 创建image docker run 运行container docker images 列出 ...

  2. Java基本数据类型转换及运算符

    上次我们说到完了Java中的基本数据类型,今天我们来说说Java中的基本数据类型转换和Java中的运算符 基本数据类型转换 java中可以从任意基本数据类型转型到外的基本数据类型 注意:(boolea ...

  3. 在wxml中直接写js代码(wxs)

    我们在h5开发中,很多时候要在html中写到js代码,这个很容易实现.但是在微信小程序开发中,是不能直接在wxml中写js代码的,因此就有了wxs.在wxml中用wxs代码,有以下几种方式(在小程序文 ...

  4. BeanFactory not initialized or already closed

    产生这个错误有两个可能, 一.你没有配置初始化文件,在web.xml中配置如下 <context-param> <param-name>contextConfigLocatio ...

  5. Spring Boot (五): Redis缓存使用姿势盘点

    1. Redis 简介 Redis 是目前业界使用最广泛的内存数据存储.相比 Memcached,Redis 支持更丰富的数据结构,例如 hashes, lists, sets 等,同时支持数据持久化 ...

  6. Redis数据库之编程项目及练习资源

    实训项目 :   NOSQL数据库设计与应用实训         注释:   Redis数据库编程项目示例及练习资源 项目源码获取: https://pan.baidu.com/s/19f0F7cmx ...

  7. JVM 调优 - jhat

    Java命令学习系列(五)——jhat 2016-01-21 分类:Java 阅读(8708) 评论(3) 阿里大牛珍藏架构资料,点击链接免费获取 jhat(Java Heap Analysis To ...

  8. 浏览器端获取短信验证码java实现——阿里云短信服务

    需求:浏览器端输入手机号,获取验证码.点击登录,验证验证码是否输入错误.是否超时等情况,一旦校验通过,将用户数据保存到数据中(业务逻辑). 前提:注册阿里用户,开通短信服务,申请key.秘钥.签名.短 ...

  9. Python简单的抓取静态网页内容

    import requests from bs4 import BeautifulSoup res = requests.get('http://news.sina.com.cn/china/')#获 ...

  10. bootstrap具体知识点(2)

    3.以移动设备为优先 <meta name=”viewport” content=”width=device-width,initial-scale=1,user-scalable=no”> ...