我正在开发 REST 服务。它使用 JSON,并且必须返回一些预定义的 JSON 对象,以防出现问题。默认的 Spring 响应如下所示:
{
"timestamp": 1512578593776,
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/swagger-ui.html"
}
我想用自己的 JSON 替换这个默认 JSON(带有堆栈跟踪和其他异常相关信息)。
Spring 提供了一种覆盖默认行为的便捷方法。人们应该定义一个@RestControllerAdvice
具有自定义异常处理程序的 bean。像这样
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = {Exception.class})
public ResponseEntity<ExceptionResponse> unknownException(Exception ex) {
ExceptionResponse resp = new ExceptionResponse(ex, level); // my custom response object
return new ResponseEntity<ExceptionResponse>(resp, resp.getStatus());
}
@ExceptionHandler(value = {AuthenticationException.class})
public ResponseEntity<ExceptionResponse> authenticationException(AuthenticationExceptionex) {
// WON'T WORK
}
}
习俗ExceptionResponse
然后 Spring 使用特殊的消息转换器将对象转换为 JSON。
问题是,安全异常如InsufficientAuthenticationException
不能被注释为的方法拦截@ExceptionHandler
。这种异常发生在 Spring MVC 调度程序 servlet 进入并且所有 MVC 处理程序初始化之前。
可以使用自定义过滤器拦截此异常并从头开始构建自己的 JSON 序列化。在这种情况下,人们将获得完全独立于 Spring MVC 基础设施其余部分的代码。这不好。
我找到的解决方案似乎有效,但看起来很疯狂。
@Configuration
public class CustomSecurityConfiguration extends
WebSecurityConfigurerAdapter {
@Autowired
protected RequestMappingHandlerAdapter requestMappingHandlerAdapter;
@Autowired
protected GlobalExceptionHandler exceptionHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.fullyAuthenticated();
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint());
}
public AuthenticationEntryPoint authenticationEntryPoint() {
return new AuthenticationEntryPoint() {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
try {
ResponseEntity<ExceptionResponse> objResponse = exceptionHandler.authenticationException(authException);
Method unknownException = exceptionHandler.getClass().getMethod("authenticationException", AuthenticationException.class);
HandlerMethod handlerMethod = new HandlerMethod(exceptionHandler, unknownException);
MethodParameter returnType = handlerMethod.getReturnValueType(objResponse);
ModelAndViewContainer mvc = new ModelAndViewContainer(); // not really used here.
List<HttpMessageConverter<?>> mconverters = requestMappingHandlerAdapter.getMessageConverters();
DispatcherServletWebRequest webRequest = new DispatcherServletWebRequest(request, response);
HttpEntityMethodProcessor processor = new HttpEntityMethodProcessor(mconverters);
processor.handleReturnValue(objResponse, returnType, mvc, webRequest);
} catch (IOException e) {
throw e;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new ServletException(e);
}
}
};
}
有没有一种方法可以使用 Spring 序列化管道(在消息转换器中使用 Spring 构建、MIME 格式协商等),它看起来比这更好?