Spring ResponseBodyException不会在响应中设置Content-Type头

o8x7eapl  于 2024-01-05  发布在  Spring
关注(0)|答案(2)|浏览(286)

我正在编写一个 Package 器来 Package 请求处理程序,使它们能够流式传输HTTP响应。

响应封装:

  1. @Component
  2. public class ResponseBodyEmitterProcessor {
  3. public ResponseBodyEmitter process(Supplier<?> supplier) {
  4. ResponseBodyEmitter emitter = new ResponseBodyEmitter();
  5. Executors.newSingleThreadExecutor()
  6. .execute(() -> {
  7. CompletableFuture<?> future = CompletableFuture.supplyAsync(supplier)
  8. .thenAccept(result -> {
  9. try {
  10. emitter.send(result, MediaType.APPLICATION_JSON_UTF8);
  11. emitter.complete();
  12. } catch (IOException e) {
  13. emitter.completeWithError(e);
  14. }
  15. });
  16. while (!future.isDone()) {
  17. try {
  18. emitter.send("", MediaType.APPLICATION_JSON_UTF8);
  19. Thread.sleep(500);
  20. } catch (IOException | InterruptedException e) {
  21. emitter.completeWithError(e);
  22. }
  23. }
  24. });
  25. return emitter;
  26. }
  27. }

字符串

控制器:

  1. @RestController
  2. @RequestMapping("/something")
  3. public class MyController extends AbstractController {
  4. @GetMapping(value = "/anything")
  5. public ResponseEntity<ResponseBodyEmitter> getAnything() {
  6. return ResponseEntity.ok()
  7. .contentType(MediaType.APPLICATION_JSON_UTF8)
  8. .body(process(() -> {
  9. //long operation
  10. }));
  11. }


我所做的只是每隔半秒发送一个空字符串来保持请求的活动。这是一些工具不超时关闭它所必需的。这里的问题是,我在响应中没有看到任何Content-Type头。根本什么都没有,尽管我从我的控制器方法返回ResponseEntity,正如它在这个线程中所说的那样:
https://github.com/spring-projects/spring-framework/issues/18518
看起来只有TEXT_HTML媒体类型才能接受流式传输。难道没有一种方法可以流式传输json吗?我甚至使用objectMapper手动将我的dtosMap到json字符串,但仍然没有运气。尝试使用APPLICATION_JSON和APPLICATION_STREAM_JSON -不起作用。在不同的浏览器中尝试-相同的结果。
我还在控制器中手动设置了ResponseEntity的Content-Type header。现在响应中有了header,但我不确定我的数据是否真的流了。在Chrome中,我只能看到我的操作结果,而不能看到我发送的中间字符(为了测试,将它们改为“a”)。
我检查了两个选项的请求处理时间:

**不带发射器(只是普通的控制器处理程序)**x1c 0d1x
带发射器

据我所知,等待状态的意思是:“等待第一个字节出现”。似乎发射器的第一个字节出现得更早-这看起来像我需要的。我可以把它作为我的解决方案工作的证明吗?
也许在Spring中还有另一种方法可以做到这一点?我需要的只是通过发送一些无用的数据来通知浏览器请求仍在处理中,直到实际操作完成-然后返回结果。
任何帮助都将不胜感激。

sg24os4d

sg24os4d1#

查看ResponseBodyEmitter#send的源代码,似乎应该在AbstractHttpMessageConverter#addDefaultHeaders方法中设置指定的MediaType,但仅当没有其他contentType头时才存在。

  1. protected void addDefaultHeaders(HttpHeaders headers, T t, MediaType contentType) throws IOException{
  2. if (headers.getContentType() == null) {
  3. MediaType contentTypeToUse = contentType;
  4. // ...
  5. if (contentTypeToUse != null) {
  6. headers.setContentType(contentTypeToUse);
  7. }
  8. }
  9. // ...
  10. }

字符串
我建议在那里设置一个断点,看看为什么头不被应用。也许@RestController设置了一个默认头。
作为一种解决方法,你可以尝试通过MVC控制器中的注解来设置contentType头。
例如

  1. @RequestMapping(value = "/something", produces = MediaType.APPLICATION_JSON_UTF8)

展开查看全部
esyap4oy

esyap4oy2#

你可以直接继承ResponseBodyException,然后覆盖方法extendResponse(ServerHttpResponse outputMessage),就像SseEmitter一样。然后使用这个子类:

  1. public class JsonEmitter extends ResponseBodyEmitter {
  2. @Override
  3. protected void extendResponse(ServerHttpResponse outputMessage) {
  4. super.extendResponse(outputMessage);
  5. HttpHeaders headers = outputMessage.getHeaders();
  6. if (headers.getContentType() == null) {
  7. headers.setContentType(MediaType.APPLICATION_JSON);
  8. }
  9. }
  10. }

字符串

相关问题