MVC - 当 content-type 是自定义的(不是 application/json)时接受 JSON
MVC - Accept JSON when content-type is custom (not application/json)
我正在尝试为我的网站实施 Content-Security-Policy-Report-Only Header。
为此,我需要一个接受浏览器 POST-Request 的控制器 - 它将以 JSON 的形式发送有关违规的数据。但是,此请求似乎将 Content-Type 指定为 application/csp-report
而不是 application/json
(旁注:到底为什么??)。
这显然导致 Spring 拒绝请求 - @RequestBody
的使用似乎使 spring 只接受 Content-Type [=16= 的请求].
即使我专门将 @RequestMapping
注释的值 consumes
设置为 application/csp-report
它仍然不接受请求。
我已经使用过滤器将请求包装成 HttpServletRequestWrapper
- 这似乎是修改请求行为的常用方法。
我已经覆盖了所有这些方法:getContentType()
、getHeader(String)
、getHeaders(String)
到 return "application/json"。
但是请求还是没有通过。
- 我在这里错过了什么?
- 有没有更好的解决办法
不需要我对请求施展魔法吗?
- 我什至不会
介意手动解析 JSON,我能以某种方式接受它吗?
改为字符串?
根据有关 @RequestBody
注释的文档,
The body of the request is passed through an HttpMessageConverter to resolve the method argument depending on the content type of the request
这意味着 Spring Framework 定义了一个专门的 API 用于定义某些 MediaTypes 在用作 REST 端点的参数时如何转换为某些 Java 类型。
Here 是一篇很好的文章,展示了这些功能。
有大量内置 Spring 转换器,如果您的媒体格式可以映射到它们各自的媒体格式,您就可以配置和使用它们。特别针对您的情况,您应该查看 spring.converter.json 包中可用的转换器之一。在最简单的情况下,让它工作应该很简单:
HttpMessageConverter converter = new <JsonConverterOfYourChoice>(JsonMapper);
converter.getSupportedMediaTypes().add(new MediaType("application", "csp-report"));
然后将此类转换器注册到 spring 的配置中。
其他可用的转换器类型包括:
- StringHttpMessageConverter
- ObjectToStringHttpMessageConverter(如果您已经配置 Spring 的 ConversionService 以从字符串中读取您想要的类型)
- ByteArrayHttpMessageConverter
- FormHttpMessageConverter
- 生活在spring.converter.xml包中的各种基于XML的转换器
- AllEncompassingFormHttpMessageConverter(转换器建立在 Form 转换器之上,并且能够处理 XML 和 JSON)
- ProtobufHttpMessageConverter
- Atom/RSS 个供稿消息转换器。
最后,如果上述 none 不适合您,您可以制作并注册自己的 HttpMessageConverter 实现。
将 构建成一个完整的片段,这段代码对我有用,可以在 Spring Boot 中添加 application/csp-report
作为 JSON 消息转换器(使用 Jackson):
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
super.extendMessageConverters(converters);
final List<MediaType> cspReportMediaTypes =
Collections.singletonList(new MediaType("application", "csp-report"));
final HttpMessageConverter<Object> cspReportConverter =
new MappingJackson2HttpMessageConverter() {
@Override
public List<MediaType> getSupportedMediaTypes() {
return cspReportMediaTypes;
}
};
converters.add(cspReportConverter);
}
};
}
其他答案帮助我弄清楚 @ResponseBody
发生了什么。
CSP 报告仍然相对较新,在浏览器中还不是很标准化:
The CSP2 spec states that CSP report requests need to use a
content-type header of application/csp-report. In my sample, I
observed four different content-type headers:
application/x-www-form-urlencoded
application/json
application/csp-report
application/json; charset=UTF-8
What to Expect When Expecting Content Security Policy Reports
因此,要接受任何类型的内容,另一种方法是直接从 HttpServletRequest
读取输入流,而不是使用带有消息转换器的 @ReponseBody
。
例如,在 Kotlin 中:
@PostMapping(path = [CSP_REPORT_URL])
fun reportFrontendError(
request: HttpServletRequest
): ResponseEntity<Void> {
val body = request.inputStream.bufferedReader().use { it.readText() }
// ...
return ResponseEntity(HttpStatus.OK)
}
我正在尝试为我的网站实施 Content-Security-Policy-Report-Only Header。
为此,我需要一个接受浏览器 POST-Request 的控制器 - 它将以 JSON 的形式发送有关违规的数据。但是,此请求似乎将 Content-Type 指定为 application/csp-report
而不是 application/json
(旁注:到底为什么??)。
这显然导致 Spring 拒绝请求 - @RequestBody
的使用似乎使 spring 只接受 Content-Type [=16= 的请求].
即使我专门将 @RequestMapping
注释的值 consumes
设置为 application/csp-report
它仍然不接受请求。
我已经使用过滤器将请求包装成 HttpServletRequestWrapper
- 这似乎是修改请求行为的常用方法。
我已经覆盖了所有这些方法:getContentType()
、getHeader(String)
、getHeaders(String)
到 return "application/json"。
但是请求还是没有通过。
- 我在这里错过了什么?
- 有没有更好的解决办法 不需要我对请求施展魔法吗?
- 我什至不会 介意手动解析 JSON,我能以某种方式接受它吗? 改为字符串?
根据有关 @RequestBody
注释的文档,
The body of the request is passed through an HttpMessageConverter to resolve the method argument depending on the content type of the request
这意味着 Spring Framework 定义了一个专门的 API 用于定义某些 MediaTypes 在用作 REST 端点的参数时如何转换为某些 Java 类型。
Here 是一篇很好的文章,展示了这些功能。
有大量内置 Spring 转换器,如果您的媒体格式可以映射到它们各自的媒体格式,您就可以配置和使用它们。特别针对您的情况,您应该查看 spring.converter.json 包中可用的转换器之一。在最简单的情况下,让它工作应该很简单:
HttpMessageConverter converter = new <JsonConverterOfYourChoice>(JsonMapper);
converter.getSupportedMediaTypes().add(new MediaType("application", "csp-report"));
然后将此类转换器注册到 spring 的配置中。
其他可用的转换器类型包括:
- StringHttpMessageConverter
- ObjectToStringHttpMessageConverter(如果您已经配置 Spring 的 ConversionService 以从字符串中读取您想要的类型)
- ByteArrayHttpMessageConverter
- FormHttpMessageConverter
- 生活在spring.converter.xml包中的各种基于XML的转换器
- AllEncompassingFormHttpMessageConverter(转换器建立在 Form 转换器之上,并且能够处理 XML 和 JSON)
- ProtobufHttpMessageConverter
- Atom/RSS 个供稿消息转换器。
最后,如果上述 none 不适合您,您可以制作并注册自己的 HttpMessageConverter 实现。
将 application/csp-report
作为 JSON 消息转换器(使用 Jackson):
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
super.extendMessageConverters(converters);
final List<MediaType> cspReportMediaTypes =
Collections.singletonList(new MediaType("application", "csp-report"));
final HttpMessageConverter<Object> cspReportConverter =
new MappingJackson2HttpMessageConverter() {
@Override
public List<MediaType> getSupportedMediaTypes() {
return cspReportMediaTypes;
}
};
converters.add(cspReportConverter);
}
};
}
其他答案帮助我弄清楚 @ResponseBody
发生了什么。
CSP 报告仍然相对较新,在浏览器中还不是很标准化:
The CSP2 spec states that CSP report requests need to use a content-type header of application/csp-report. In my sample, I observed four different content-type headers:
application/x-www-form-urlencoded application/json application/csp-report application/json; charset=UTF-8
What to Expect When Expecting Content Security Policy Reports
因此,要接受任何类型的内容,另一种方法是直接从 HttpServletRequest
读取输入流,而不是使用带有消息转换器的 @ReponseBody
。
例如,在 Kotlin 中:
@PostMapping(path = [CSP_REPORT_URL])
fun reportFrontendError(
request: HttpServletRequest
): ResponseEntity<Void> {
val body = request.inputStream.bufferedReader().use { it.readText() }
// ...
return ResponseEntity(HttpStatus.OK)
}