【问题标题】:Sending error with Spring's HandlerInterceptor results in 406 (Not Acceptable) response使用 Spring 的 HandlerInterceptor 发送错误会导致 406(不可接受)响应
【发布时间】:2016-07-15 21:12:02
【问题描述】:

我正在尝试使用 Spring 的 HandlerInterceptorAdapter 来处理应用程序计划维护以下休息端点的情况:/api/authentication

所以我通过扩展HandlerInterceptorAdapter创建了一个拦截器:

public class MigrationStateInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if(someLogic) {
            return true
        }

        response.reset();
        response.sendError(HttpStatus.SERVICE_UNAVAILABLE.value());
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}

然后我把它添加到我的InterceptorRegistry

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new MigrationStateInterceptor()).addPathPatterns("/api/authentication");
}

问题是,我没有在客户端收到503 (Service Unavailable) 错误,而是收到Failed to load resource: the server responded with a status of 406 (Not Acceptable)

从代码 sn-p 中可以看出,我尝试重置响应,但没有结果。 我还尝试修改 AcceptContent-Type 标头,但没有运气:

response.setContentType(MediaType.TEXT_PLAIN_VALUE);
response.addHeader("Accept", "text/plain");

知道为什么会发生这种情况以及如何避免吗?

注意:我在客户端使用 Angular。从我看到的HttpServletResponse#sendError 将内容类型设置为text/html,使cookie 和其他标题保持不变。这会是个问题吗?

编辑: 我什至尝试抛出异常而不是使用HttpServletResponse#sendError,并单独处理,但最终结果是一样的。

例如:

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    if (some logic) {
        return true;
    }
    throw new MigrationStateException("Migration process is disabled");
}

 @ExceptionHandler(MigrationStateException.class)
public ResponseEntity<String> migrationStateError(MigrationStateException e) {
    return new ResponseEntity<>(e.getMessage(), HttpStatus.SERVICE_UNAVAILABLE);
}

【问题讨论】:

  • 你可以让调试器在 preHandle 中中断吗?
  • 是的,我可以。您有兴趣了解什么?
  • 你的客户真的期待text/plain吗?那将是非常不寻常的。 sendError 不只是设置内容类型,它会发送一个错误页面。您想发送自己的回复。异常处理程序仅适用于控制器。
  • @zeroflagL 异常处理程序捕获了异常,即使它不在控制器中。从非控制器环境返回的响应是否存在问题?关于我在客户端的期望,我可以很容易地改变,但是在服务器端发生了一些事情,将 503 转换为 406。至少,这是我观察到的。您对此有什么建议吗?谢谢!
  • 406 表示客户端期望服务器没有提供的东西。了解客户期望的媒体类型很重要。我假设服务器将ResponseEntity&lt;String&gt; 视为text/plain 而客户端期望application/json。尝试返回 Map

标签: java spring spring-mvc spring-boot


【解决方案1】:

我认为问题在于您映射到/api/authenticate 的请求不是期待text/html,或者您发送的请求类型错误(即GET 而不是POST 等)

您说 Angular 正在发送 text/html,鉴于与身份验证端点关联的通常默认值,它可能期待 application/x-www-form-urlencoded 数据。因此,Spring 正在拦截一个 Content-Type 它没有映射和 406'ing。

【讨论】:

  • 嗨@dardo,Spring 将内容类型设置为text/html,而不是角度。更确切地说是HttpServletResponse#sendError 方法。我尝试通过抛出异常来绕过它并从异常处理程序发送响应,但没有成功。请求没问题,问题出在 Spring 发送的响应上。
  • 那是因为你在 spring 收到请求之后拦截了它。在这种情况下,text/html 是错误页面本身,而不是您期望的响应。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-05-17
  • 1970-01-01
  • 2011-11-20
  • 2015-03-17
  • 1970-01-01
  • 2017-04-08
  • 2014-09-27
相关资源
最近更新 更多