【问题标题】:Spring changing media type on URI with .au at the end以 .au 结尾的 URI 上的 Spring 更改媒体类型
【发布时间】:2017-05-06 20:52:59
【问题描述】:

目前实现的 REST 端点如下:

@RequestMapping(path = "/login/user/{username:.+}", method = POST, produces = "application/json; charset=utf-8")
@ResponseStatus(code = HttpStatus.OK)
public User userLogin(@PathVariable("username") String username, @RequestBody Password password) {
    //do stuff
    return new User(UUID.randomUUID());
}

我目前使用电子邮件地址作为用户名,当我使用以 .au 结尾的用户名时,端点返回 406 内容不可接受。

我试着把上面的改成这个

@RequestMapping(path = "/login/user/{username:.+}", method = POST, produces = "application/json; charset=utf-8")
@ResponseStatus(code = HttpStatus.OK)
public String userLogin(@PathVariable("username") String username, @RequestBody Password password) {
    //do stuff
    return "blah";
}

当我访问它时,它会提示我下载一个 .au 文件(由 Sun 微系统制作的音频格式...),其中包含“blah”。 如果我在方法中随时检查用户名的值,我会得到正确的电子邮件地址,包括 .au。

我猜测 Spring 堆栈中的某些东西正在解析 .au 并尝试强制执行不同的媒体类型,所以现在它忽略了 application/json

【问题讨论】:

    标签: java spring rest content-type


    【解决方案1】:

    我最近遇到了同样的问题并发现了问题。想在这里分享它,因为它会帮助别人。 @Patrick 解释的这种行为似乎是由于 Spring MVC 中基于 基于 URL(URL 后缀)的内容协商而发生的。

    什么是内容协商?

    在某些情况下,我们必须处理控制器返回的相同数据的多个表示(或视图)。确定要返回的数据格式称为内容协商。

    内容协商如何运作?

    通过 HTTP 发出请求时,可以通过设置 Accept 标头属性来指定您想要的响应类型。然而,浏览器实际上会发送非常混乱的Accept 标头,这使得依赖它们变得不切实际。因此 Spring 为内容协商提供了一些替代约定。

    Spring Content Negotiation Alternatives - URL 后缀和/或 URL 参数

    这些与Accept 标头一起使用。结果, 可以通过三种方式中的任何一种来请求内容类型。默认情况下,他们 按此顺序检查:

    • 在 URL 中添加路径扩展名(后缀)。因此,如果传入的 URL 类似于http://myserver/myapp/accounts/list.html,那么 HTML 是必须的。对于电子表格,URL 应该是 http://myserver/myapp/accounts/list.xls。媒体类型的后缀 映射是通过 JavaBeans Activation 自动定义的 框架或 JAF(所以 activation.jar 必须在类路径上)。

    • 这样的 URL 参数:http://myserver/myapp/accounts/list?format=xls。的名称 参数默认为格式,但可以更改。用一个 参数默认禁用,但启用时,它被选中 第二个。

      • 最后检查了Accept HTTP 标头属性。这就是 HTTP 实际定义的工作方式,但是,如前所述,它可以 使用起来有问题。

    在问题中解释的上述案例中,您看到的是基于路径扩展的内容协商在起作用。 (.au)

    来自ContentNegotiationConfigurer的哈瓦文档,

    favorPathExtension

    公共 ContentNegotiationConfigurer 偏爱路径扩展(布尔 喜欢路径扩展)

    是否应该使用 URL 路径中的路径扩展来确定 请求的媒体类型。

    默认设置为 true,在这种情况下请求 /hotels.pdf 将被解释为对“application/pdf”的请求,无论 “接受”标头。

    解决方案 - 将喜爱路径扩展设置为 false

    @Configuration
    @EnableWebMvc
    public class WebConfig extends WebMvcConfigurerAdapter {
    
      @Override
      public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.favorPathExtension(false).
                favorParameter(true).
                parameterName("mediaType").
                ignoreAcceptHeader(true).
                useJaf(false).
                defaultContentType(MediaType.APPLICATION_JSON).
                mediaType("xml", MediaType.APPLICATION_XML).
                mediaType("json", MediaType.APPLICATION_JSON);
      }
    }
    
    <bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
        <property name="favorPathExtension" value="false" />
        <property name="favorParameter" value="true" />
        <property name="parameterName" value="mediaType" />
        <property name="ignoreAcceptHeader" value="true"/>
        <property name="useJaf" value="false"/>
        <property name="defaultContentType" value="application/json" />
    
        <property name="mediaTypes">
            <map>
                <entry key="json" value="application/json" />
                <entry key="xml" value="application/xml" />
           </map>
        </property>
    </bean>
    

    请注意,除了将喜爱路径扩展设置为 false 之外,上述配置还有一些其他更改。

    更多详情请见here

    为了补全,我们得到的问题回复如下。

    {
      "timestamp": 1518691842254,
      "status": 406,
      "error": "Not Acceptable",
      "exception": "org.springframework.web.HttpMediaTypeNotAcceptableException",
      "message": "Not Acceptable",
      "path": "/rest/token/something.au"
    }
    

    【讨论】:

      【解决方案2】:

      我认为它在序列化器和反序列化器时由 A 数据传输对象 (DTO) 引起。所以用户应该实现 Serializable 接口。

      【讨论】:

        【解决方案3】:

        我遇到了类似的问题。我的资源映射到 /upload/ 需要文件路径)。所以资源 URI 会像 /upload/a/b/c/test1.jpg、upload/xy/test2.xml 等,

        就像@Rajind 提到的那样,它将媒体类型视为 URL 中存在的扩展名(在点 (.) 之后)。

        HTTP 状态 406 – 不可接受

        {
            "timestamp": 1538992653298,
            "status": 406,
            "error": "Not Acceptable",
            "message": "Could not find acceptable representation",
            "path": "/file-manager-services/api-6.0/234833/upload/Mohan/tst.jpg"
        }
        

        我通过添加以下配置解决了它。

        @Configuration
        @EnableWebMvc
        public class **** implements WebMvcConfigurer {
        
            @Override
            public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        
                configurer.favorPathExtension(false);
            }
        
        ....
        ....
        
        }
        

        【讨论】:

          猜你喜欢
          • 2014-07-01
          • 1970-01-01
          • 1970-01-01
          • 2017-09-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-01-13
          相关资源
          最近更新 更多