【问题标题】:Spring MVC: Is it possible to bind a specific HttpMessageConverter to a @RequestBody-annotated parameter?Spring MVC:是否可以将特定的 HttpMessageConverter 绑定到 @RequestBody 注释的参数?
【发布时间】:2017-04-05 19:15:58
【问题描述】:

我有一个默认 AbstractJackson2HttpMessageConverter 的 Spring Boot 应用程序。绑定的 Jackson ObjectMapper 实例注册了一个自定义 SimpleModule,因此现在它支持来自 Google Guava 库的 Multimap

@Bean
public ObjectMapper objectMapper() {
    return new ObjectMapper()
            .setSerializationInclusion(NON_NULL)
            .configure(FAIL_ON_UNKNOWN_PROPERTIES, false)
            .registerModule(new SimpleModule()
                    .addSerializer(multimapType, multimapDuplicateKeysSerializer)
                    .addDeserializer(multimapType, multimapJsonDeserializer)
            );
}

假设我的控制器中有以下方法:

@RequestMapping(method = POST)
@ResponseStatus(OK)
public Object post(
    @RequestBody final Multimap<String, Object> multimap
) {
    ...
}

在这一步一切都很好。现在我必须验证多映射是否有空键或空白键,如果在传入的多映射中发现任何无效键,则返回 HTTP 400。这是一个简单的操作,可以很容易地遍历传入的多图并抛出异常以在控制器建议中处理。但这至少有下一个缺陷:

  • 我不想直接在我的控制器或相关服务中的某处验证多图。
  • 如果这是一个缺陷:我无法使其与 Spring MVC 注释(如 @Valid@ModelAttribute 等)一起使用,但现在我并不真正关心这些,原因如下:
  • 我不希望在反序列化后验证多映射,因为我想在特定HttpMessageConverter 某处的解析器级别验证传入的请求主体——我需要的验证纯粹是 JSON 流验证即使对于不需要在验证之前反序列化整个多图的大型多图,这也可以完美地工作。如果可能,当然可以。

是否可以仅将特定的HttpMessageConverter 绑定到特定的@RequestBody?大概是这样的:

@RequestMapping(method = POST)
@ResponseStatus(OK)
public Object post(
    @SomeMagicSpringAnnotationHere("specificHttpMessageConverter") @RequestBody final Multimap<String, Object> multimap
    OR
    @AnotherMagicSpringAnnotationHere("specificObjectMapper") @RequestBody final Multimap<String, Object> multimap
    OR
    @WhateverMagicSpringAnnotationHere @RequestBody final Multimap<String, Object> multimap
) {
    ...
}

或者我对这种方法有什么误解,它可以通过与 Spring 相关且更优雅的方式来完成?任何建议都非常感谢。提前致谢!

【问题讨论】:

  • 只为这种类型实现你自己的自定义MultiMapHttpMessageConverter,并在你的mvc配置中注册它。 (我什至认为注册它是一个 bean 就足够了,因为 HttpMessageConverter 实例会被自动检测和注册。
  • @M.Deinum 感谢您的回复。如果我理解它应该如何工作,那么我可能会有两个问题:1)这样的HttpMessageConverter必须覆盖现有的AbstractJackson2HttpMessageConverter,它现在不能支持多图; 2)它将绑定到整个应用程序中的所有多图,而不是特定的。我是对的,还是我错过了什么?
  • 为什么要覆盖一个,它会在另一个旁边使用。转换机制检测方法参数的类型并尝试找到合适的转换器。它确实适用于所有MultiMap 参数,您不能只为单个方法/控制器提供一个参数。这不是机制的工作原理。
  • 考虑一下,您可以创建一个注释来使用而不是 @RequestBody 并实现一个 HandlerMethodArgumentResolver 来处理这个问题。然后,您可以将另一个注释放在您的参数上,而不是 @RequestBody。然后,您可以在该课程中做任何您想做的事情。
  • @M.Deinum 嗯,我试图用HandlerMethodArgumentResolver 进行试验,但由于某种原因无法使其工作(可能是因为参数已经用@RequestBody 进行了注释)。我会深入研究一下。

标签: java rest spring-mvc spring-boot


【解决方案1】:

创建一个专门的注解,即@MultiMapRequestBody

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MultiMapRequestBody {

    boolean required() default true;

然后创建一个知道该做什么的MultiMapRequestBodyMethodArgumentResolver

public class MultiMapRequestBodyMethodArgumentResolver implements HandlerMethodArgumentResolver {

    public  boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(MultiMapRequestBody.class); // maybe check argument type too.
    }

    public  Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
        NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        // Logic for validation and converting using Jackson
        // Take a look at MappingJackson2HttpMessageConverter

    }
}

现在您可以自己处理正文转换和验证。您可能想查看MappingJackson2HttpMessageConverter,以获取有关如何阅读和解析正文的一些灵感。或者可以扩展用于参数转换的abstract 类之一。

现在在您的请求处理方法中使用@MultiMapRequestBody 而不是@RequestBody 注释。

@RequestMapping(method = POST)
@ResponseStatus(OK)
public Object post(
    @MultiMapRequestBody final Multimap<String, Object> multimap
) {
    ...
}

【讨论】:

  • 非常感谢!我去看看。
  • 好吧,现在它看起来微不足道,就像我想要的那样简单......正是我需要的。我发誓,我也在尝试应用它。无论我用它做了什么,我都因为任何原因做错了。反正不需要加班,我需要一些假期。非常感谢非常
猜你喜欢
  • 2011-08-21
  • 2013-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-03
相关资源
最近更新 更多