我正在使用Spring Boot(2.3.2(实现一个简单的rest控制器,以通过POST从外部源接收数据。该源不随它一起发送ContentType,而是提供了一个包含JSON的主体。
我的控制器真的很简单
@RestController
@RequiredArgsConstructor
public class WaitTimeImportController {
private final StorageService storageService;
@PostMapping(value = "/endpoint")
public void setWaitTimes(@RequestBody DataObject toStore) {
storageService.store(toStore);
}
}
@Data
@NoArgsConstructor
class DataObject {
private String data;
}
@Service
class StorageService {
void store(DataObject do) {
System.out.println("received");
}
}
现在,如果我向它发送一个json字符串,它将不会触发该请求映射。
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
class WaittimesApplicationTests {
@Test
void happyPath() throws IOException {
WebClient client = client();
String content = "{ "data": "test"} ";
AtomicBoolean ab = new AtomicBoolean();
await().atMost(Duration.FIVE_SECONDS).ignoreExceptions()
.until(() -> {
ClientResponse response = client.post()
// .header("Content-Type", "application/json")
.bodyValue(content)
.exchange()
.timeout(java.time.Duration.ofMillis(200))
.block();
ab.set(response.statusCode() == HttpStatus.ACCEPTED);
return ab.get();
});
}
}
除非我取消注释";标题";上面的行;那就好了。
由于我对提交POST请求的源没有影响,所以我不能添加application/json
作为请求头。
我在春季搜索了很多关于内容协商的内容,但我尝试过的都没有成功。
- 向
@PostMapping
注释添加consumes="application/json"
- 向
@PostMapping
注释添加consumes=MediaType.ALL_VALUE
- 添加
MappingJackson2HttpMessageConverter
bean不起作用 - 添加CCD_ 7 bean没有编译";无法访问javax.servlet.ServletException"-这是一条值得走的路吗
我能做些什么来强制映射接受非";内容类型";请求并将其解码为JSON?
Ugg。因此,Lebecca的评论在如何使用Spring调整头字段的过程中发出(我使用的是webflux,所以链接的解决方案本身不适用(,我找不到合适的教程,文档在搜索时遇到的几个页面中有点分散。
这里有一些障碍,所以这里有一个浓缩版本。
干净的方式:调整标题以注入内容类型`
因此,您可以创建一个WebFilter
,在请求实际到达映射之前对其进行评估。只需定义一个实现它的@Component
要添加到传递的ServerWebExchange
对象,请将其包装在ServerWebExchangeDecorator
中。
@Component
class MyWebFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(new MyServerExchangeDecorator(exchange));
}
}
该装饰器可以同时操作请求和响应;只需覆盖适当的方法。当然,请求也需要修饰——不能继续处理原始对象(如果你试图设置它的属性,它实际上会抛出一个异常,因为它是只读的(。
class MyServerExchangeDecorator extends ServerWebExchangeDecorator {
protected MyServerExchangeDecorator(ServerWebExchange delegate) {
super(delegate);
}
@Override
public ServerHttpRequest getRequest() {
return new ContentRequestDecorator(super.getRequest());
}
}
现在您只需实现ContentRequestDecorator
,就可以开始了。
class ContentRequestDecorator extends ServerHttpRequestDecorator {
public ContentRequestDecorator(ServerHttpRequest delegate) {
super(delegate);
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders httpHeaders = HttpHeaders.writableHttpHeaders(super.getHeaders());
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
return httpHeaders;
}
}
尝试仅对传入的对象调用setContentType
会导致OperationNotSupportedException
,因为它也是只读的;从而产生CCD_ 15呼叫。
当然,这将调整所有传入请求的所有标头,因此,如果您计划这样做,并且不想要核选项,请事先检查请求。
简单的方法
因此,上面的方式触发了传入请求到consumes="application/json"
端点的Springs映射。
然而,如果你没有读到我之前的描述中有一点讽刺的意味,我应该不会那么含蓄。对于一个非常简单的问题,这是一个非常复杂的解决方案。
我最终的做法是
@Autowired
private ObjectMapper mapper;
@PostMapping(value = "/endpoint")
public void setMyData(HttpEntity<String> json) {
try {
MyData data = mapper.readValue(json.getBody(), MyData.class);
storageService.setData(data);
} catch (JsonProcessingException e) {
return ResponseEntity.unprocessableEntity().build();
}
}