在 Chrome 和 Postman 中调试来自 Spring 的 server-sent 事件流
Debugging server-sent events stream from Spring in Chrome and Postman
根据 Spring documentation,当返回 Flux
时,Spring 应该为订阅返回的每个元素发出一个 server-sent 事件。
这是一个示例性的 REST 控制器:
package myapp.controller;
import myapp.MyOutput;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/api/v1/test")
@Api(tags = "Test API")
public class TestController {
@ApiOperation(
value = "Test",
response = MyOutput.class,
produces = MediaType.TEXT_EVENT_STREAM_VALUE
)
@RequestMapping(
value = "",
method = RequestMethod.PATCH,
produces = MediaType.TEXT_EVENT_STREAM_VALUE
)
@ApiResponses(
value = {
@ApiResponse(code = 200, message = "Service execution successful"),
@ApiResponse(code = 400, message = "Bad input data"),
@ApiResponse(code = 500, message = "An internal server error occurred."),
@ApiResponse(code = 503, message = "The service is not available.")
}
)
public ResponseEntity<Flux<MyOutput>> test() {
return ResponseEntity.ok().header("Connection", "Keep-Alive")
.body(Flux.range(0, 1000)
.delayElements(Duration.ofSeconds(1))
.map(MyOutput::new)
);
}
}
示例响应,使用 wget:
data:{"recordCount":0}
data:{"recordCount":148}
data:{"recordCount":226}
data:{"recordCount":266}
data:{"recordCount":272}
data:{"recordCount":286}
data:{"recordCount":287}
data:{"recordCount":293}
data:{"recordCount":294}
使用 Chrome 或 Postman 调试端点时,客户端似乎将事件解释为分块响应的一部分 - 而不是事件流。我已确认响应数据相同,并且需要预期的时间。请参阅下面 Chrome 网络选项卡的屏幕截图:
事件流 - 选项卡为空:
将此与 http://www.emojitracker.com/ 等网站的 headers 进行比较:
EventStream 选项卡正确显示事件的位置:
重要的事实是,当使用 WebClient
使用 Spring 端点时,我可以使用 .bodyToFlux
在预期时间成功接收每个事件。因此,事件似乎缺少 Chrome 从 server-sent 事件流中期望的某种形式的配置 - 但是哪个?
首先在浏览器中使用EventSource时会执行GET请求,所以使用PATCH在浏览器中并不兼容。
如果我们获取您的代码并更改 GET 中的 PATCH,我们将创建一个简单的页面:
<html lang="fr">
<head>
<title>Test SSE</title>
<script>
const evtSource = new EventSource("/api/v1/test");
evtSource.onmessage = function(event) {
const newElement = document.createElement("li");
const eventList = document.getElementById("results");
newElement.textContent = "message: " + event.data;
eventList.appendChild(newElement);
}
</script>
</head>
<body>
<ul id="results">
</ul>
</body>
</html>
让您的应用程序提供此静态文件并在 chrome 中打开它。您将在“事件”选项卡中正确看到事件。但是,如果您直接请求 /api/v1/test,您将在页面中而不是在“事件”选项卡中执行此事件。我想事件选项卡拦截 EventSource 对象,如果没有创建 EventSource 将不会被使用。
根据 Spring documentation,当返回 Flux
时,Spring 应该为订阅返回的每个元素发出一个 server-sent 事件。
这是一个示例性的 REST 控制器:
package myapp.controller;
import myapp.MyOutput;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/api/v1/test")
@Api(tags = "Test API")
public class TestController {
@ApiOperation(
value = "Test",
response = MyOutput.class,
produces = MediaType.TEXT_EVENT_STREAM_VALUE
)
@RequestMapping(
value = "",
method = RequestMethod.PATCH,
produces = MediaType.TEXT_EVENT_STREAM_VALUE
)
@ApiResponses(
value = {
@ApiResponse(code = 200, message = "Service execution successful"),
@ApiResponse(code = 400, message = "Bad input data"),
@ApiResponse(code = 500, message = "An internal server error occurred."),
@ApiResponse(code = 503, message = "The service is not available.")
}
)
public ResponseEntity<Flux<MyOutput>> test() {
return ResponseEntity.ok().header("Connection", "Keep-Alive")
.body(Flux.range(0, 1000)
.delayElements(Duration.ofSeconds(1))
.map(MyOutput::new)
);
}
}
示例响应,使用 wget:
data:{"recordCount":0}
data:{"recordCount":148}
data:{"recordCount":226}
data:{"recordCount":266}
data:{"recordCount":272}
data:{"recordCount":286}
data:{"recordCount":287}
data:{"recordCount":293}
data:{"recordCount":294}
使用 Chrome 或 Postman 调试端点时,客户端似乎将事件解释为分块响应的一部分 - 而不是事件流。我已确认响应数据相同,并且需要预期的时间。请参阅下面 Chrome 网络选项卡的屏幕截图:
事件流 - 选项卡为空:
将此与 http://www.emojitracker.com/ 等网站的 headers 进行比较:
EventStream 选项卡正确显示事件的位置:
重要的事实是,当使用 WebClient
使用 Spring 端点时,我可以使用 .bodyToFlux
在预期时间成功接收每个事件。因此,事件似乎缺少 Chrome 从 server-sent 事件流中期望的某种形式的配置 - 但是哪个?
首先在浏览器中使用EventSource时会执行GET请求,所以使用PATCH在浏览器中并不兼容。
如果我们获取您的代码并更改 GET 中的 PATCH,我们将创建一个简单的页面:
<html lang="fr">
<head>
<title>Test SSE</title>
<script>
const evtSource = new EventSource("/api/v1/test");
evtSource.onmessage = function(event) {
const newElement = document.createElement("li");
const eventList = document.getElementById("results");
newElement.textContent = "message: " + event.data;
eventList.appendChild(newElement);
}
</script>
</head>
<body>
<ul id="results">
</ul>
</body>
</html>
让您的应用程序提供此静态文件并在 chrome 中打开它。您将在“事件”选项卡中正确看到事件。但是,如果您直接请求 /api/v1/test,您将在页面中而不是在“事件”选项卡中执行此事件。我想事件选项卡拦截 EventSource 对象,如果没有创建 EventSource 将不会被使用。