2017-03-01 133 views
1

以下Spring引导documentation我定义了自己的ErrorAttributes bean(见下文),我能够使json响应显示我想要的信息,包括我自己的错误代码和消息通过使用自定义异常来包装该信息并从中生成错误响应。唯一的问题是响应的http状态与我在状态属性中定义的状态不匹配,它没有被覆盖。Spring Boot自定义ErrorAttributes http状态未设置为响应

@Bean 
public ErrorAttributes errorAttributes() { 
    return new DefaultErrorAttributes() { 
     @Override 
     public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) { 

      Map<String, Object> errorAttributes = super.getErrorAttributes(requestAttributes, includeStackTrace); 

      Throwable error = getError(requestAttributes); 

      if (error instanceof MyException) { 
       MyException myException = (MyException) error; 

       errorAttributes.put("errorCode", myException.getErrorCode()); 
       errorAttributes.put("message", myException.getMessage()); 
       errorAttributes.put("status", myException.getStatus()); 

       HttpStatus correspondentStatus = HttpStatus.valueOf(myException.getStatus()); 
       errorAttributes.put("error", correspondentStatus.getReasonPhrase()); 
      } 

      return errorAttributes; 
     } 
    }; 
} 

响应的HTTP状态不匹配的JSON的状态,例如:

HTTP/1.1 500 
Content-Type: application/json;charset=UTF-8 
Transfer-Encoding: chunked 
Date: Wed, 01 Mar 2017 18:48:22 GMT 
{ 
    "timestamp": "2017-03-01T18:48:21.894+0000", 
    "status": 403, 
    "error": "Forbidden", 
    "exception": "com.myapp.MyException", 
    "message": "You are not authorized. This user doesn't exist in the db", 
    "path": "/account", 
    "errorCode": "00013" 
} 
+0

每http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/web/DefaultErrorAttributes.html#getErrorAttributes-org.springframework.web.context .request.RequestAttributes-boolean-您正在覆盖的方法不会更改http状态代码 – xiumeteo

回答

3

我发现了一种从创建自定义ErrorAttributes bean的逻辑内设置http状态的方法,这样我就可以重新使用Spring Boot错误响应创建并使用我的自定义信息进行更新而不需要异常处理程序和控制器建议。

通过添加下一行,您可以设置覆盖requestAttributes中的当前状态的http状态。

requestAttributes.setAttribute("javax.servlet.error.status_code", httpStatus, 0); 

其中httpStatus是您要设置的状态。

以下是完整的bean定义与所添加一行:

@Bean 
public ErrorAttributes errorAttributes() { 
    return new DefaultErrorAttributes() { 
     @Override 
     public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) { 

      Map<String, Object> errorAttributes = super.getErrorAttributes(requestAttributes, includeStackTrace); 

      Throwable error = getError(requestAttributes); 

      if (error instanceof MyException) { 
       MyException myException = (MyException) error; 
       int httpStatus = myException.getStatus(); 

       errorAttributes.put("errorCode", myException.getErrorCode()); 
       errorAttributes.put("message", myException.getMessage()); 
       errorAttributes.put("status", httpStatus); 

       HttpStatus correspondentStatus = HttpStatus.valueOf(httpStatus); 
       errorAttributes.put("error", correspondentStatus.getReasonPhrase()); 
       requestAttributes.setAttribute("javax.servlet.error.status_code", httpStatus, 0); 
      } 

      return errorAttributes; 
     } 
    }; 
} 

我怎么找到它?通过查看DefaultErrorAttributes类,我发现有一个方法addStatus是私有的,但它显示了代码用来生成响应的http状态的属性名称,这是我寻找的线索:

private void addStatus(Map<String, Object> errorAttributes, RequestAttributes requestAttributes) { 
    Integer status = (Integer)this.getAttribute(requestAttributes, "javax.servlet.error.status_code"); 
... 

寻找更进我发现代码getAttribute方法正被称为有实际调用从RequestAttributes接口中的方法:

private <T> T getAttribute(RequestAttributes requestAttributes, String name) { 
    return requestAttributes.getAttribute(name, 0); 
} 

该接口,我发现里面检查也有一个的setAttribute方法。有效。

HTTP/1.1 403 
Content-Type: application/json;charset=UTF-8 
Transfer-Encoding: chunked 
Date: Wed, 01 Mar 2017 20:59:33 GMT 
{ 
    "timestamp": "2017-03-01T20:59:32.774+0000", 
    "status": 403, 
    "error": "Forbidden", 
    "exception": "com.myapp.MyException", 
    "message": "You are not authorized. This user doesn't exist in the db", 
    "path": "/account", 
    "errorCode": "00013" 
} 
4

所有你正在做的是建立你的错误响应的身体,你可以从你的样品看。 Spring是处理状态码的人。

如果你想对响应的所有部分完全控制,那么你应该使用ControllerAdvice方法如图所示在其文档中:

@ControllerAdvice(basePackageClasses = FooController.class) 
public class FooControllerAdvice extends ResponseEntityExceptionHandler { 

    @ExceptionHandler(MyException.class) 
    public ResponseEntity<Message> handleRequestErrorMyException(HttpServletRequest request, MyException myException) { 
     HttpStatus status = HttpStatus.valueOf(myException.getStatus(); 
     return new ResponseEntity<>(new CustomErrorType(status.value(), ex.getMessage()), status); 
    } 

} 

有了这个bean全部到位MyException下抛出的任何控制器FooController包将被handleRequestErrorMyException捕获和处理,原始请求的响应将是该方法返回的响应。只需确保在你的配置类中扫描了该软件包。

+0

我一直试图避免使用异常处理程序,ErrorAttributes已经有一些预先填充的信息,除了我之外还想返回自定义数据,如路径,异常,堆栈跟踪...在我的方法中唯一缺少的是http状态,应该有一个设置它的方法,我试图找到它 – raspacorp

+0

我不明白ControllerAdvice为什么不足以满足您的要求。该方法使您可以访问异常和请求信息,但我没有看到ErrorAttributes bean中的哪些信息在ControllerAdvice中没有。我看到的唯一区别是您必须手动添加一些属性,如时间戳,路径,但除此之外我没有看到问题。 – artemisian

+0

我同意,控制器的建议是足够的,它实际上比我需要的更充分。我关于使用ErrorAtrributes的想法是使用Spring Boot的开箱即用机制,而不添加额外的配置,因此遵循Spring引导加上我的自定义字段的标准错误响应。这样我就不会为这些异常处理程序增加维护负担,在这种情况下,更改是增量式的,而不是复制当前机制已提供的内容。 – raspacorp

相关问题