为什么Spring优先考虑405 Http Error over 406 Http Error

时间:2013-11-29 14:43:51

标签: java web-services http rest spring-mvc

我在Spring中有一个休息控制器 3.1 ,它允许使用GET和POST方法。

POST方法仅限于使用映射

生成输出

@RequestMapping(value =“projects”,method = RequestMethod.POST,produce = {“application / json”})

如果我发出下一个请求,则返回405错误并显示“Allow:GET”标题。

POST /resources/projects
Content-Type: application/json
Accept: text/html

我发现Spring MVC Framework在handleNoMatch方法中优先考虑RequestMappingInfoHandlerMapping.java中的405 Error。

protected HandlerMethod handleNoMatch(Set<RequestMappingInfo> requestMappingInfos, String lookupPath, HttpServletRequest request) throws ServletException {
....
....
    if (!allowedMethods.isEmpty()) {
        throw new HttpRequestMethodNotSupportedException(request.getMethod(), allowedMethods);
    }
    else if (!consumableMediaTypes.isEmpty()) {
        MediaType contentType = null;
        if (StringUtils.hasLength(request.getContentType())) {
            contentType = MediaType.parseMediaType(request.getContentType());
        }
        throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<MediaType>(consumableMediaTypes));
    }
    else if (!producibleMediaTypes.isEmpty()) {
        throw new HttpMediaTypeNotAcceptableException(new ArrayList<MediaType>(producibleMediaTypes));
    }

但我希望我的REST API优先考虑406错误,因为它是有道理的,我对我的优先权是对的吗?我怎么能做到这一点?

我知道这个问题已在Spring 3.2中得到解决,但我无法升级到3.2。

我被建议扩展RequestMappingInfoHandlerMapping,但我不知道该怎么做。

3 个答案:

答案 0 :(得分:1)

我认为你的应用程序配置不正确。 @RequestMapping不是您所显示的,或者它是注释@Controller中未注册的方法。

以下是我DispatcherServlet

注册的唯一映射
@RequestMapping(value = "projects", method = RequestMethod.POST, produces = { "application/json" })
public String home() {
    System.out.println("HomeController: Passing through...");
    return "WEB-INF/views/home.jsp";
}

并使用

进行测试
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
HttpPost post = new HttpPost("http://localhost:8080/resources/projects");
post.setHeader("Context-type", "application/json");
post.setHeader("Accept", "text/html");
HttpResponse httpResponse = httpClient.execute(post);
System.out.println(httpResponse);

我得到了

HTTP/1.1 406 Not Acceptable [Server: [...]]

正如所料。

您应该仔细检查日志,看看您想要的处理程序方法是否实际上已注册。日志将显示类似

的内容
2013-11-30 01:30:33,958 [localhost-startStop-1] INFO  o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/projects],methods=[POST],params=[],headers=[],consumes=[],produces=[application/json],custom=[]}" onto public java.lang.String xyz.spring.mvc.HomeController.home() 

在您的评论和建议的更改后

@RequestMapping(value = "projects", method = RequestMethod.POST, produces = { "application/json" })
public String home() {
    System.out.println("HomeController: Passing through...");
    return "WEB-INF/views/home.jsp";
}

@RequestMapping(value = "projects", method = RequestMethod.GET)
public String homeGet() {
    System.out.println("HomeControllerGet: Passing through...");
    return "WEB-INF/views/home.jsp";
}

这是启动日志

2013-12-02 08:33:57,233 [localhost-startStop-1] INFO  o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/projects],methods=[POST],params=[],headers=[],consumes=[],produces=[application/json],custom=[]}" onto public java.lang.String xyz.sample.baremvc.HomeController.home() 
2013-12-02 08:33:57,234 [localhost-startStop-1] INFO  o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/projects],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.lang.String xyz.sample.baremvc.HomeController.homeGet() 

Tomcat仍然给了我一个406

HTTP/1.1 406 Not Acceptable [[...]]

这是有道理的。请记住,produces属性的javadoc状态为

  

格式是单一媒体类型或媒体类型序列,带有   请求仅在Accept与其中一种媒体类型匹配时映射。

由于您的请求的Accept标头与媒体类型不匹配,因此请求不可接受,并且会发生406。

答案 1 :(得分:0)

你可以继承RequestMappingInfoHandlerMapping或RequestMappingHandlerMapping吗?

答案 2 :(得分:0)

今天再次处理这个问题后,我对如何向Spring添加自定义处理程序映射以解决问题有一个正确的答案。

这个问题在Spring 3.2中没有发生,正如@Sotirios Delimanolis指出的那样。

如果你想在Spring 3.1中解决它,你可以:

  1. 将HandlerMapping子类化,如您所见:
  2. {

    public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
    
    protected HandlerMethod handleNoMatch(
            Set<RequestMappingInfo> requestMappingInfos, String lookupPath,
            HttpServletRequest request) throws ServletException {
        Set<String> allowedMethods = new HashSet<String>(6);
        Set<MediaType> consumableMediaTypes = new HashSet<MediaType>();
        Set<MediaType> producibleMediaTypes = new HashSet<MediaType>();
        for (RequestMappingInfo info : requestMappingInfos) {
            if (info.getPatternsCondition().getMatchingCondition(request) != null) {
                if (info.getMethodsCondition().getMatchingCondition(request) == null) {
                    for (RequestMethod method : info.getMethodsCondition()
                            .getMethods()) {
                        allowedMethods.add(method.name());
                    }
                }
                if (info.getConsumesCondition().getMatchingCondition(request) == null) {
                    consumableMediaTypes.addAll(info.getConsumesCondition()
                            .getConsumableMediaTypes());
                }
                if (info.getProducesCondition().getMatchingCondition(request) == null) {
                    producibleMediaTypes.addAll(info.getProducesCondition()
                            .getProducibleMediaTypes());
                }
            }
        }
        if (!producibleMediaTypes.isEmpty()) {
            throw new HttpMediaTypeNotAcceptableException(
                    new ArrayList<MediaType>(producibleMediaTypes));
        } else if (!consumableMediaTypes.isEmpty()) {
            MediaType contentType = null;
            if (StringUtils.hasLength(request.getContentType())) {
                contentType = MediaType
                        .parseMediaType(request.getContentType());
            }
            throw new HttpMediaTypeNotSupportedException(contentType,
                    new ArrayList<MediaType>(consumableMediaTypes));
        } else if (!allowedMethods.isEmpty()) {
            throw new HttpRequestMethodNotSupportedException(
                    request.getMethod(), allowedMethods);
        } else {
            return null;
        }
    }
    

    }

    然后你有两个选择: 2.1将此bean添加到Spring XML配置中。

    <bean name="handlerMapping" class="com.ncr.mobile.rest.handler.CustomRequestMappingHandlerMapping">
        <property name="order" value="-1" />
    </bean>
    

    2.2将此方法添加到WebConfig中。该命令可以为该类提供最高优先级。

    @Configuration
    public class WebConfig extends WebMvcConfigurationSupport {
    
      @Override
      @Bean
      public RequestMappingHandlerMapping requestMappingHandlerMapping() {
              RequestMappingHandlerMapping handlerMapping = new CustomRequestMappingHandlerMapping();
              handlerMapping.setOrder(-1);
              handlerMapping.setInterceptors(getInterceptors());
              return handlerMapping;
      }
    
    }
    

    我希望这可以帮助别人。