我想使用通用方法来管理5xx错误代码,让我们具体说明db在整个spring应用程序中出现故障的情况.我想要一个漂亮的错误json而不是堆栈跟踪.
对于控制器,我有一个@ControllerAdvice
用于不同异常的类,这也是捕获db在请求中间停止的情况.但这并不是全部.我也碰巧有一个自定义CorsFilter
扩展OncePerRequestFilter
,当我打电话给doFilter
我得到CannotGetJdbcConnectionException
它,它将不会被管理@ControllerAdvice
.我在线阅读了几件让我更加困惑的事情.
所以我有很多问题:
我需要实现自定义过滤器吗?我找到了,ExceptionTranslationFilter
但这只是处理AuthenticationException
或AccessDeniedException
.
我想实现自己的HandlerExceptionResolver
,但这让我怀疑,我没有任何自定义异常来管理,必须有一个比这更明显的方法.我还尝试添加一个try/catch并调用一个实现HandlerExceptionResolver
(应该足够好,我的异常没什么特别的)但是这不会在响应中返回任何内容,我得到一个状态200和一个空体.
有什么好方法可以解决这个问题吗?谢谢
所以这就是我做的:
我在这里阅读了有关过滤器的基础知识,并且我发现我需要创建一个自定义过滤器,该过滤器将首先位于过滤器链中,并且将有一个try catch来捕获可能在那里发生的所有运行时异常.然后我需要手动创建json并将其放入响应中.
所以这是我的自定义过滤器:
public class ExceptionHandlerFilter extends OncePerRequestFilter { @Override public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { filterChain.doFilter(request, response); } catch (RuntimeException e) { // custom error response class used across my project ErrorResponse errorResponse = new ErrorResponse(e); response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); response.getWriter().write(convertObjectToJson(errorResponse)); } } public String convertObjectToJson(Object object) throws JsonProcessingException { if (object == null) { return null; } ObjectMapper mapper = new ObjectMapper(); return mapper.writeValueAsString(object); } }
然后我在之前的web.xml中添加了它CorsFilter
.它的工作原理!
exceptionHandlerFilter
xx.xxxxxx.xxxxx.api.controllers.filters.ExceptionHandlerFilter
exceptionHandlerFilter
/*
CorsFilter
org.springframework.web.filter.DelegatingFilterProxy
CorsFilter
/*
我遇到这个问题我自己和我执行的步骤如下重用我ExceptionController
被注释与@ControllerAdvise
供Exceptions
扔在注册的过滤器.
显然有很多方法可以处理异常,但在我的情况下,我希望异常由我处理,ExceptionController
因为我很顽固,也因为我不想复制/粘贴相同的代码(即我有一些处理/记录代码ExceptionController
).我想返回漂亮的JSON
响应,就像其他不是从Filter中抛出的异常一样.
{ "status": 400, "message": "some exception thrown when executing the request" }
无论如何,我设法利用了我ExceptionHandler
,我必须做一些额外的工作,如下所示:
脚步
您有一个自定义过滤器,可能会或可能不会抛出异常
你有一个Spring控制器,使用@ControllerAdvise
ieExceptionController 来处理异常
示例代码
//sample Filter, to be added in web.xml public MyFilterThatThrowException implements Filter { //Spring Controller annotated with @ControllerAdvise which has handlers //for exceptions private MyExceptionController myExceptionController; @Override public void destroy() { // TODO Auto-generated method stub } @Override public void init(FilterConfig arg0) throws ServletException { //Manually get an instance of MyExceptionController ApplicationContext ctx = WebApplicationContextUtils .getRequiredWebApplicationContext(arg0.getServletContext()); //MyExceptionHanlder is now accessible because I loaded it manually this.myExceptionController = ctx.getBean(MyExceptionController.class); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; try { //code that throws exception } catch(Exception ex) { //MyObject is whatever the output of the below method MyObject errorDTO = myExceptionController.handleMyException(req, ex); //set the response object res.setStatus(errorDTO .getStatus()); res.setContentType("application/json"); //pass down the actual obj that exception handler normally send ObjectMapper mapper = new ObjectMapper(); PrintWriter out = res.getWriter(); out.print(mapper.writeValueAsString(errorDTO )); out.flush(); return; } //proceed normally otherwise chain.doFilter(request, response); } }
现在是Exception
在正常情况下处理的示例Spring Controller (即通常不会在Filter级别引发的异常,我们要用于Filter中抛出的异常的异常)
//sample SpringController @ControllerAdvice public class ExceptionController extends ResponseEntityExceptionHandler { //sample handler @ResponseStatus(value = HttpStatus.BAD_REQUEST) @ExceptionHandler(SQLException.class) public @ResponseBody MyObject handleSQLException(HttpServletRequest request, Exception ex){ ErrorDTO response = new ErrorDTO (400, "some exception thrown when " + "executing the request."); return response; } //other handlers }
与希望ExceptionController
用于Exceptions
过滤器的人共享解决方案.
如果需要通用方法,可以在web.xml中定义错误页面:
java.lang.Throwable
/500
并在Spring MVC中添加映射:
@Controller
public class ErrorController {
@RequestMapping(value="/500")
public @ResponseBody String handleException(HttpServletRequest req) {
// you can get the exception thrown
Throwable t = (Throwable)req.getAttribute("javax.servlet.error.exception");
// customize response to what you want
return "Internal server error.";
}
}
所以,这就是我基于上述答案的合并而做的...我们已经有了GlobalExceptionHandler
注释,@ControllerAdvice
我也想找到一种方法来重用这些代码来处理来自过滤器的异常.
我能找到的最简单的解决方案是单独留下异常处理程序,并按如下方式实现错误控制器:
@Controller public class ErrorControllerImpl implements ErrorController { @RequestMapping("/error") public void handleError(HttpServletRequest request) throws Throwable { if (request.getAttribute("javax.servlet.error.exception") != null) { throw (Throwable) request.getAttribute("javax.servlet.error.exception"); } } }
因此,由异常引起的任何错误首先通过ErrorController
并通过从@Controller
上下文中重新抛出它们而被重定向到异常处理程序,而任何其他错误(不是由异常直接引起)都会通过ErrorController
而不进行修改.
为什么这实际上是一个坏主意的任何原因?
我想根据@kopelitsa的答案提供解决方案。主要区别在于:
通过使用重用控制器异常处理HandlerExceptionResolver
。
在XML配置上使用Java配置
首先,您需要确保您有一个类来处理在常规RestController / Controller中发生的异常(用@RestControllerAdvice
或标记的类@ControllerAdvice
和和用标记的方法@ExceptionHandler
)。这可以处理您在控制器中发生的异常。这是使用RestControllerAdvice的示例:
@RestControllerAdvice public class ExceptionTranslator { @ExceptionHandler(RuntimeException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ErrorDTO processRuntimeException(RuntimeException e) { return createErrorDTO(HttpStatus.INTERNAL_SERVER_ERROR, "An internal server error occurred.", e); } private ErrorDTO createErrorDTO(HttpStatus status, String message, Exception e) { (...) } }
要在Spring Security过滤器链中重用此行为,您需要定义一个Filter并将其挂钩到您的安全配置中。筛选器需要将异常重定向到上面定义的异常处理。这是一个例子:
@Component public class FilterChainExceptionHandler extends OncePerRequestFilter { private final Logger log = LoggerFactory.getLogger(getClass()); @Autowired @Qualifier("handlerExceptionResolver") private HandlerExceptionResolver resolver; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { filterChain.doFilter(request, response); } catch (Exception e) { log.error("Spring Security Filter Chain Exception:", e); resolver.resolveException(request, response, null, e); } } }
然后需要将创建的过滤器添加到SecurityConfiguration。您需要非常早地将其挂接到链中,因为不会捕获所有先前过滤器的异常。就我而言,在之前添加它是合理的LogoutFilter
。请参阅官方文档中的默认过滤器链及其顺序。这是一个例子:
@Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private FilterChainExceptionHandler filterChainExceptionHandler; @Override protected void configure(HttpSecurity http) throws Exception { http .addFilterBefore(filterChainExceptionHandler, LogoutFilter.class) (...) } }
这是我的解决方案,通过覆盖默认的Spring Boot /错误处理程序
package com.mypackage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.web.ErrorAttributes; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Map; /** * This controller is vital in order to handle exceptions thrown in Filters. */ @RestController @RequestMapping("/error") public class ErrorController implements org.springframework.boot.autoconfigure.web.ErrorController { private final static Logger LOGGER = LoggerFactory.getLogger(ErrorController.class); private final ErrorAttributes errorAttributes; @Autowired public ErrorController(ErrorAttributes errorAttributes) { Assert.notNull(errorAttributes, "ErrorAttributes must not be null"); this.errorAttributes = errorAttributes; } @Override public String getErrorPath() { return "/error"; } @RequestMapping public ResponseEntity