自定义参数解析器未被调用

时间:2015-08-24 08:24:39

标签: java spring spring-mvc

我有一个控制器,它有以下方法

@RequestMapping(value = "/cases/{caseId}", params = "meta", method = PUT, produces = APPLICATION_JSON_VALUE)
@ResponseBody
@ResponseStatus(HttpStatus.OK)
public String updateUIMetadata(@PathVariable("caseId") final String caseId,
       @RequestBody  @JsonData(schemaLocation = "schema/metadata_schema.json") final String metadataJson) {

}

我正在实施自定义HandlerMethodArgumentResolver

public class ValidateJsonSchema implements HandlerMethodArgumentResolver {


@Override
public boolean supportsParameter(MethodParameter parameter) {
    return parameter.hasParameterAnnotation(JsonData.class);
}

@Override
public String resolveArgument(MethodParameter parameter,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
        WebDataBinderFactory binderFactory) throws Exception {
    System.out.println("Inside ValidateJsonSchema");
    String json = (String) getRequestResponseBodyMethodProcessor().resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    return validateJson(json, parameter);

}
}

我已将其注册为bean,并在配置文件

中注册了参数解析器
@Bean
public ValidateJsonSchema validateJsonSchema() {
    return new ValidateJsonSchema();
}

@Override
public void addArgumentResolvers(
        List<HandlerMethodArgumentResolver> argumentResolvers) {
    argumentResolvers.add(validateJsonSchema());
}

但是由于一些奇怪的原因,参数解析器类从未调用过。有没有我错过的东西?

编辑:JsonData.java

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface JsonData {
public String schemaLocation();
}

编辑2:AppInitializer.java

public class AppInitializer implements WebApplicationInitializer {

@Override
public void onStartup(ServletContext servletContext) throws ServletException {
    WebApplicationContext context = getContext();
    servletContext.addListener(new ContextLoaderListener(context));
    ServletRegistration.Dynamic dispatcher = servletContext.addServlet(DISPATCHER_SERVLET, new DispatcherServlet(context));
    dispatcher.setLoadOnStartup(1);
    dispatcher.addMapping(MAPPING_URL);
}

private AnnotationConfigWebApplicationContext getContext() {
    AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
    context.setConfigLocation(CONFIG_FILE_LOCATION);
    return context;
}
}

3 个答案:

答案 0 :(得分:0)

确保您的配置类使用 @Configuration 进行注释,并且还扩展 WebMvcConfigurationSupport

在配置类中也使用 @EnableWebMvc

答案 1 :(得分:0)

您的自定义参数解析器未被调用的原因是您的参数已由Spring RequestResponseBodyMethodProcessor处理。该类负责处理用@RequestBody注释的参数。

要使您的参数解析器发挥作用,只需删除@RequestBody注释。

答案 2 :(得分:0)

另一种方法是使用 RequestBodyAdvice 并在方法 beforeBodyRead 中验证 json。唯一的缺点是当验证失败时你会得到响应 500,因为控制器永远不会收到对象。

@ControllerAdvice
class JsonSchemaBodyAdvice : RequestBodyAdvice {

  @Autowired
  private var jsonValidatorService: JsonValidatorService? = null

  override fun supports(parameter: MethodParameter, type: Type, clazz: Class<out HttpMessageConverter<*>>): Boolean {
    return parameter.parameter.getAnnotation(RequestBody::class.java) != null
  }

  override fun beforeBodyRead(
    message: HttpInputMessage,
    parameter: MethodParameter,
    type: Type,
    clazz: Class<out HttpMessageConverter<*>>
  ): HttpInputMessage {
    val json = String(message.body.readAllBytes())
    
    // VALIDATE SCHEMA HERE

    // we can not reset the original input stream, wrap the json in a new object
    return object : HttpInputMessage {
      override fun getHeaders() = message.headers
      override fun getBody() = ByteArrayInputStream(json.toByteArray(Charsets.UTF_8))
    }
  }

  override fun afterBodyRead(
    body: Any,
    message: HttpInputMessage,
    parameter: MethodParameter,
    type: Type,
    clazz: Class<out HttpMessageConverter<*>>
  ): Any {
    return body
  }

  override fun handleEmptyBody(
    body: Any?,
    message: HttpInputMessage,
    parameter: MethodParameter,
    type: Type,
    clazz: Class<out HttpMessageConverter<*>>
  ) = body
}

您也可以创建自己的注解来将架构文件设置为参数,例如@ValidRequestBody("schema.json")