在Spring Boot中定制HTTP 错误返回
作者:Jesus J. Puente
译者:沈歌
这篇文章中,我将介绍在Spring Boot中如何返回定制化HTTP错误。当我们发起HTTP请求时,都要考虑有可能返回错误。
典型的示例是我们使用RESTful请求查询一个记录,但是记录不存在。在这个示例中,你通常需要返回HTTP码404(找不到),使用以下代码,你也可以返回一个Spring Boot定制的JSON格式对象:
{
"timestamp": "2018-11-20T11:46:10.255+0000",
"status": 404,
"error": "Not Found",
"message": "bean: 8 not Found",
"path": "/get/8"
}
但是如果你想输出一点有效信息,比如:
{
"timestamp": "2018-11-20T12:51:42.699+0000",
"mensaje": "bean: 8 not Found",
"detalles": "uri=/get/8",
"httpCodeMessage": "Not Found"
}
我们必须在项目中放一些类。
GitHub 上有我的源代码。
创建一个基础的Spring Boot,只有一个简单的对象叫做MiBean,有两个变量,code 和 value。 通过/get请求资源,返回这个对象: 请求 http://localhost: 8080/get /1 ,返回一个这样的JSON对象:
{
"codigo": 1,
"valor": "valor uno"
}
如果你想要获取id大于3的MiBean,会抛出异常,因为我们只有三条记录。
这个类ErrorResource,对于请求 /get 进行处理。
public class ErrorResource {
@Autowired
MiBeanService service;
@GetMapping("/get/{id}")
public MiBean getBean(@PathVariable int id) {
MiBean bean = null;
try {
bean = service.getBean(id);
} catch (NoSuchElementException k)
{
throw new BeanNotFoundException("bean: "+id+ " not Found" );
}
return bean;
}
}
正如getBean()方法所示,我们请求MiBeanService对象的getBean(int id)方法。 这是MiBeanService的源代码。
@Component
public class MiBeanService {
private static List<MiBean> miBeans = new ArrayList<>();
static {
miBeans.add(new MiBean(1, "valor uno"));
miBeans.add(new MiBean(2, "valor dos"));
miBeans.add(new MiBean(3, "valor tres"));
}
public MiBean getBean(int id) {
MiBean miBean =
miBeans.stream()
.filter(t -> t.getCodigo()==id)
.findFirst()
.get();
return miBean;
}
}
如果方法getBean(int id) 在ListmiBeans中找不到值时,会抛出NoSuchElementException。这个异常会被controller捕获并抛出一个BeanNotFoundException。
类BeanNotFoundException如下所示:
@ResponseStatus(HttpStatus.NOT_FOUND)
public class BeanNotFoundException extends RuntimeException {
public BeanNotFoundException(String message) {
super(message);
}
}
这个简单的类继承RuntimeException并且被@ResponseStatus(HttpStatus.NOT_FOUND) 注解,将返回一个404码给客户端。
现在如果我们发起大于3的请求,将会接收到这样一个返回:

但是正如我们所说,我们想要定制错误信息。创建一个新类用于定义错误信息。这个类是ExceptionResponse,代码如下:
public class ExceptionResponse {
private Date timestamp;
private String mensaje;
private String detalles;
private String httpCodeMessage;
public ExceptionResponse(Date timestamp, String message, String details,String httpCodeMessage) {
super();
this.timestamp = timestamp;
this.mensaje = message;
this.detalles = details;
this.httpCodeMessage=httpCodeMessage;
}
public String getHttpCodeMessage() {
return httpCodeMessage;
}
public Date getTimestamp() {
return timestamp;
}
public String getMensaje() {
return mensaje;
}
public String getDetalles() {
return detalles;
}
}
这个类表明BeanNotFoundException抛出时应该返回的JSON对象。现在我们写配置Spring抛出JSON对象的代码。
@ControllerAdvice
@RestController
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(BeanNotFoundException.class)
public final ResponseEntity<ExceptionResponse> handleNotFoundException(BeanNotFoundException ex, WebRequest request) {
ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(), ex.getMessage(),
request.getDescription(false),HttpStatus.NOT_ACCEPTABLE.getReasonPhrase());
return new ResponseEntity<ExceptionResponse>(exceptionResponse, HttpStatus.NOT_ACCEPTABLE);
}
}
这个类必须继承处理公共异常的ResponseEntityExceptionHandler类。
我们使用@ControllerAdvice 和 @RestController 进行注解。
@ControllerAdvice是从@Component中来的,用于处理异常的类。作为被@RestController标记的类,它只处理REST controller抛出的异常。
在handleNotFoundException方法中,我们定义当BeanNotFoundException 异常被抛出时,我们必须返回ExceptionResponse对象。通过创建一个ResponseEntity对象,我们可以方便的初始化。
注意它定义了HTTP返回码,在这个示例中,我们返回406,而不是404。事实上,在我们的例子中,我们可以移除BeanNotFoundException的@ResponseStatus(HttpStatus.NOT_FOUND) 注解,有没有这个注解没啥区别。
这样,我们就定制了输出,如下图:
