以 .au 结尾的 URI 上的 Spring 更改媒体类型

Posted

技术标签:

【中文标题】以 .au 结尾的 URI 上的 Spring 更改媒体类型【英文标题】:Spring changing media type on URI with .au at the end 【发布时间】: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

【问题讨论】:

【参考方案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);
    

....
....


【讨论】:

以上是关于以 .au 结尾的 URI 上的 Spring 更改媒体类型的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Azure AD 为应用服务上的 Spring Boot 应用设置重定向 URI

配置 Spring Security 5 Oauth 2 以使用 access_token uri 参数

为啥当参数以(.pl)结尾时,Spring MVC @RequestMapping 会为映射(/user/username:.+)抛出 406 错误

如何重定向以“.jsp”结尾的网址

为啥 Spring MVC 以 404 响应并报告“No mapping found for HTTP request with URI [...] in DispatcherServlet”?

更改线hr的颜色