gpt4 book ai didi

java - 如何根据 Spring MVC Controller 方法请求的 URI 实例化 @RequestBody 参数的特定子类型?

转载 作者:行者123 更新时间:2023-12-01 18:32:39 33 4
gpt4 key购买 nike

给定以下基本域模型:

abstract class BaseData { ... }

class DataA extends BaseData { ... }

class DataB extends BaseData { ... }

我想编写一个 Spring MVC Controller 端点...

@PostMapping(path="/{typeOfData}", ...)
ResponseEntity<Void> postData(@RequestBody BaseData baseData) { ... }

所需的baseData具体类型可以从路径中的typeOfData推断出来。

这使我能够使用一个方法来处理具有不同正文有效负载的多个 URL。我将为每个有效负载指定一个具体类型,但我不想创建多个 Controller 方法来执行相同的操作(尽管每个方法只执行很少的操作)。

我面临的挑战是如何“通知”反序列化过程,以便实例化正确的具体类型。

我可以想到两种方法来做到这一点。

首先使用自定义HttpMessageConverter ...


@Bean
HttpMessageConverter httpMessageConverter() {

return new MappingJackson2HttpMessageConverter() {
@Override
public Object read(final Type type, final Class<?> contextClass, final HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {

// TODO How can I set this dynamically ?
final Type subType = DataA.class;

return super.read(subType, contextClass, inputMessage);
}
};
}

...这给我带来了根据 HttpInputMessage 确定子类型的挑战。也许我可以使用 Filter当我可以使用 URL 时,尽早设置自定义 header ,或者我可以使用 ThreadLocal也可通过 Filter 设置。这对我来说听起来都不理想。

我的第二种方法是再次使用 Filter这次将传入的有效负载包装在一个外部对象中,然后该对象将以某种方式提供类型,使 Jackson 能够通过 @JsonTypeInfo 完成工作。目前这可能是我首选的方法。

我调查过HandlerMethodArgumentResolver但如果我尝试注册一个自定义的,它会在RequestResponseBodyMethodProcessor之后注册并且该类优先。

最佳答案

嗯,所以在输入所有内容后,我在发布问题之前快速检查了 RequestResponseBodyMethodProcessor 中的某些内容,并找到了另一种探索途径,效果很好。

请原谅 @Configuration/@RestController/WebMvcConfigurer 混搭和公共(public)字段,所有这些都是为了简洁。以下是对我有用并达到我想要的效果的方法:

@Configuration
@RestController
@RequestMapping("/dummy")
public class DummyController implements WebMvcConfigurer {

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface BaseData {}

public static class AbstractBaseData {}

public static class DataA extends AbstractBaseData {
public String a;
}

public static class DataB extends AbstractBaseData {
public String b;
}

private final MappingJackson2HttpMessageConverter converter;

DummyController(final MappingJackson2HttpMessageConverter converter) {
this.converter = converter;
}

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {

resolvers.add(
new RequestResponseBodyMethodProcessor(Collections.singletonList(converter)) {

@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(BaseData.class)
&& parameter.getParameterType() == AbstractBaseData.class;
}

@Override
protected <T> Object readWithMessageConverters(
NativeWebRequest webRequest, MethodParameter parameter, Type paramType)
throws IOException, HttpMediaTypeNotSupportedException,
HttpMessageNotReadableException {

final String uri =
webRequest.getNativeRequest(HttpServletRequest.class).getRequestURI();

return super.readWithMessageConverters(
webRequest, parameter, determineActualType(webRequest, uri));
}

private Type determineActualType(NativeWebRequest webRequest, String uri) {
if (uri.endsWith("data-a")) {
return DataA.class;
} else if (uri.endsWith("data-b")) {
return DataB.class;
}

throw new HttpMessageNotReadableException(
"Unable to determine actual type for request URI",
new ServletServerHttpRequest(
webRequest.getNativeRequest(HttpServletRequest.class)));
}
});
}

@PostMapping(
path = "/{type}",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<? extends AbstractBaseData> post(@BaseData AbstractBaseData baseData) {
return ResponseEntity.ok(baseData);
}
}

关键是我停止使用 @RequestBody 因为这阻止了我覆盖内置行为。通过使用@BaseData,我得到了一个唯一支持该参数的HandlerMethodArgumentResolver

除此之外,这是组装已经完成我需要的两个对象的情况,因此 Autowiring MappingJackson2HttpMessageConverter 并使用该转换器实例化 RequestResponseBodyMethodProcessor 。然后选择正确的方法进行重写,以便我可以控制在有权访问 URI 时使用的参数类型。

快速测试。给定两个请求的以下有效负载...

{ “一”:“一个”, “b”:“B”}

发布http://localhost:8081/dummy/data-a

...给出的响应...

{ “一个”:“一个”}

发布http://localhost:8081/dummy/data-b

...给出的响应...

{ “b”:“B”}

在我们的实际示例中,这意味着我们将能够编写一种支持 POST/PUT 的方法。我们需要构建对象并可能配置验证 - 或者,如果我们使用我们正在研究的 OpenAPI 3.0,我们可以生成模型并进行验证,而无需编写任何进一步的代码......但这是一个单独的任务;)

关于java - 如何根据 Spring MVC Controller 方法请求的 URI 实例化 @RequestBody 参数的特定子类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60130367/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com