MVC - 当内容类型是自定义的时候接受JSON(不是application / json)

时间:2017-03-03 13:50:45

标签: java json model-view-controller content-security-policy

我正在尝试为我的网站实施Content-Security-Policy-Report-Only标头。 为了做到这一点,我需要一个接受浏览器POST-Request的控制器 - 它将以JSON的形式发送有关违规的数据。但是,此请求似乎将内容类型指定为application/csp-report而不是application/json(旁注:为什么地狱?)。

这显然会导致Spring拒绝请求 - 似乎@RequestBody的使用使spring只接受Content-Type "application/json"的请求。 即使我专门将consumes注释的值@RequestMapping设置为application/csp-report,它仍然不接受请求。

我已经使用过滤器来使用HttpServletRequestWrapper包装请求 - 这似乎是修改请求行为的常用方法。 我已经覆盖了所有这些方法:getContentType()getHeader(String)getHeaders(String)以返回“application / json”。 但是请求仍然没有通过。

  • 我在这里缺少什么?
  • 有没有更好的解决方案 不要求我对请求做魔术吗?
  • 我甚至不会 介意手动解析JSON,我可以以某种方式接受它 而不是字符串?

3 个答案:

答案 0 :(得分:3)

根据@RequestBody注释的文档,

  

请求的主体通过HttpMessageConverter传递,以根据请求的内容类型解析方法参数

这意味着Spring Framework定义了一个专用API,用于定义某些MediaType在用作REST端点的参数时如何转换为某些Java类型。

Here是一篇很好的文章,展示了这些功能。

如果您的媒体格式可以映射到各自的媒体格式,那么您可以配置和使用大量内置Spring转换器。特别针对您的情况,您应该查看spring.converter.json包中提供的转换器之一。在最简单的情况下,使其工作应该如下:

HttpMessageConverter converter = new <JsonConverterOfYourChoice>(JsonMapper);

converter.getSupportedMediaTypes().add(new MediaType("application", "csp-report"));

然后将这样的转换器注册到弹簧的配置中。

其他可用的转换器类型包括:

  • StringHttpMessageConverter
  • ObjectToStringHttpMessageConverter(如果您已经将Spring的ConversionService配置为从String中读取所需的类型)
  • ByteArrayHttpMessageConverter
  • FormHttpMessageConverter
  • 生活在spring.converter.xml
  • 中的各种基于XML的转换器
  • AllEncompassingFormHttpMessageConverter(转换器构建在Form转换器之上,并且能够处理XML和JSON)
  • Protobuf HttpMessageConverter
  • Atom/RSS Feed消息转换器。

最后,如果以上都不适合您,您可以制作并注册自己的HttpMessageConverter实现。

答案 1 :(得分:1)

M. Prokhorov's answer构建为一个完整的代码段,这段代码可以让我在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);
    }
  };
}

答案 2 :(得分:0)

其他答案可以帮助我弄清楚@ResponseBody的情况。

CSP报告仍然相对较新,并且在浏览器之间还不够标准化:

  

CSP2规范指出,CSP报告请求需要使用   application / csp-report的内容类型标头。在我的样本中   观察到四个不同的内容类型标头:

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与消息转换器一起使用。

例如,在科特林:

@PostMapping(path = [CSP_REPORT_URL])
fun reportFrontendError(
    request: HttpServletRequest
): ResponseEntity<Void> {
    val body = request.inputStream.bufferedReader().use { it.readText() }

    // ...

    return ResponseEntity(HttpStatus.OK)
}