Spring 更改 URI 中以 .au 结尾的媒体类型
Spring changing media type on URI with .au at the end
目前实现了一个 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 结尾的用户名时,端点 returns 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 microsystems 制作的音频格式...),其中包含 "blah"。
如果我在方法中随时检查用户名的值,我会得到正确的电子邮件地址,其中包含 .au。
我猜 Spring 堆栈中的某些东西正在解析 .au 并试图强制执行不同的媒体类型,所以现在它忽略了 application/json
我认为它是由序列化器和反序列化器时的数据传输对象 (DTO) 引起的。所以 User 应该实现 Serializable 接口。
我最近遇到了同样的问题并找到了问题所在。想在这里分享它,因为它会帮助别人。 @Patrick 解释的这种行为似乎是由于 URL(URL 后缀)基于 Spring MVC 中的内容协商 而发生的。
什么是内容协商?
在某些情况下,我们必须处理由控制器 return 编辑的同一数据的多个表示(或视图)。确定哪种数据格式 return 称为内容协商。
内容协商如何工作?
通过 HTTP 发出请求时,可以通过设置 Accept
header 属性 来指定您想要的响应类型。然而,浏览器实际上发送了非常混乱的 Accept
headers,这使得依赖它们变得不切实际。因此 Spring 提供了一些内容协商的替代约定。
Spring 内容协商备选方案 - URL 后缀 and/or 一个 URL 参数
These work alongside the use of Accept
headers. As a result, the
content-type can be requested in any of three ways. By default they
are checked in this order:
Add a path extension (suffix) in the URL. So, if the incoming URL is something like http://myserver/myapp/accounts/list.html then HTML
is required. For a spreadsheet the URL should be
http://myserver/myapp/accounts/list.xls. The suffix to media-type
mapping is automatically defined via the JavaBeans Activation
Framework or JAF (so activation.jar
must be on the class path).
A URL parameter like this: http://myserver/myapp/accounts/list?format=xls. The name of the
parameter is format by default, but this may be changed. Using a
parameter is disabled by default, but when enabled, it is checked
second.
- Finally the
Accept
HTTP header property is checked. This is how HTTP is actually defined to work, but, as previously mentioned, it can
be problematic to use.
在问题中解释的上述案例中,您看到的是基于路径扩展的内容协商在起作用。 (.au)
来自 ContentNegotiationConfigurer、
的 hava 文档
favorPathExtension
public ContentNegotiationConfigurer favorPathExtension(boolean
favorPathExtension)
Whether the path extension in the URL path should be used to determine
the requested media type.
By default this is set to true in which case a request for /hotels.pdf
will be interpreted as a request for "application/pdf" regardless of
the 'Accept' header.
解决方案 - 将 favorPathExtension 设置为 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>
请注意,除了将 favorPathExtension 设置为 false 之外,上述配置还有一些其他更改。
有关这方面的更多详细信息,请参见 here。
为了完成,我们得到的问题回复如下。
{
"timestamp": 1518691842254,
"status": 406,
"error": "Not Acceptable",
"exception": "org.springframework.web.HttpMediaTypeNotAcceptableException",
"message": "Not Acceptable",
"path": "/rest/token/something.au"
}
我遇到了类似的问题。我的资源映射到 /upload/( 需要文件路径)。所以资源URI会像/upload/a/b/c/test1.jpg, upload/xy/test2.xml等,
就像@Rajind 提到的那样,它正在将媒体类型视为 URL(在点 (.) 之后)中存在的扩展名。
HTTP Status 406 – Not Acceptable
{
"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);
}
....
....
}
目前实现了一个 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 结尾的用户名时,端点 returns 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 microsystems 制作的音频格式...),其中包含 "blah"。 如果我在方法中随时检查用户名的值,我会得到正确的电子邮件地址,其中包含 .au。
我猜 Spring 堆栈中的某些东西正在解析 .au 并试图强制执行不同的媒体类型,所以现在它忽略了 application/json
我认为它是由序列化器和反序列化器时的数据传输对象 (DTO) 引起的。所以 User 应该实现 Serializable 接口。
我最近遇到了同样的问题并找到了问题所在。想在这里分享它,因为它会帮助别人。 @Patrick 解释的这种行为似乎是由于 URL(URL 后缀)基于 Spring MVC 中的内容协商 而发生的。
什么是内容协商?
在某些情况下,我们必须处理由控制器 return 编辑的同一数据的多个表示(或视图)。确定哪种数据格式 return 称为内容协商。
内容协商如何工作?
通过 HTTP 发出请求时,可以通过设置 Accept
header 属性 来指定您想要的响应类型。然而,浏览器实际上发送了非常混乱的 Accept
headers,这使得依赖它们变得不切实际。因此 Spring 提供了一些内容协商的替代约定。
Spring 内容协商备选方案 - URL 后缀 and/or 一个 URL 参数
These work alongside the use of
Accept
headers. As a result, the content-type can be requested in any of three ways. By default they are checked in this order:
Add a path extension (suffix) in the URL. So, if the incoming URL is something like http://myserver/myapp/accounts/list.html then HTML is required. For a spreadsheet the URL should be http://myserver/myapp/accounts/list.xls. The suffix to media-type mapping is automatically defined via the JavaBeans Activation Framework or JAF (so
activation.jar
must be on the class path).A URL parameter like this: http://myserver/myapp/accounts/list?format=xls. The name of the parameter is format by default, but this may be changed. Using a parameter is disabled by default, but when enabled, it is checked second.
- Finally the
Accept
HTTP header property is checked. This is how HTTP is actually defined to work, but, as previously mentioned, it can be problematic to use.
在问题中解释的上述案例中,您看到的是基于路径扩展的内容协商在起作用。 (.au)
来自 ContentNegotiationConfigurer、
的 hava 文档favorPathExtension
public ContentNegotiationConfigurer favorPathExtension(boolean favorPathExtension)
Whether the path extension in the URL path should be used to determine the requested media type.
By default this is set to true in which case a request for /hotels.pdf will be interpreted as a request for "application/pdf" regardless of the 'Accept' header.
解决方案 - 将 favorPathExtension 设置为 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>
请注意,除了将 favorPathExtension 设置为 false 之外,上述配置还有一些其他更改。
有关这方面的更多详细信息,请参见 here。
为了完成,我们得到的问题回复如下。
{
"timestamp": 1518691842254,
"status": 406,
"error": "Not Acceptable",
"exception": "org.springframework.web.HttpMediaTypeNotAcceptableException",
"message": "Not Acceptable",
"path": "/rest/token/something.au"
}
我遇到了类似的问题。我的资源映射到 /upload/( 需要文件路径)。所以资源URI会像/upload/a/b/c/test1.jpg, upload/xy/test2.xml等,
就像@Rajind 提到的那样,它正在将媒体类型视为 URL(在点 (.) 之后)中存在的扩展名。
HTTP Status 406 – Not Acceptable
{
"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);
}
....
....
}