我有一个web应用程序与ASP。使用具有控制器类的WebApi。NET(约束于。NET 4.5)。
在这个控制器中,我必须创建两个新的端点,它们必须调用另一个服务,而该服务是使用Spring Web的Java应用程序,但这可能无关紧要。
这里的问题是,这两个端点中的一个需要简单地转发spring应用程序发送的Server-Sent-Events。
我不知道如何在c#中做到这一点。这是我到目前为止尝试过的两件事,但显然它们不起作用:
[HttpPost]
[Route("api/myApp/myEndpoint")]
public async Task<HttpResponseMessage> myEndpoint([FromBody] MyEndpointParams params)
{
try
{
using (var client = new HttpClient(new HttpClientHandler
{
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
}))
{
string uri = "https://theServer/theSpringApp/api/theServerEventsSendingEndpoint";
StringContent content = new StringContent(JsonConvert.SerializeObject(params), Encoding.UTF8, "application/json");
HttpRequestMessage request = new HttpRequestMessage()
{
Method = HttpMethod.Post,
RequestUri = new Uri(uri),
Content = content
};
return await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
}
}
catch (Exception ex)
{
log.Error("[myEndpoint] Error", ex);
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
}
[HttpPost]
[Route("api/myApp/myEndpoint")]
public async Task<IDisposable> myEndpoint([FromBody] MyEndpointParams params)
{
try
{
using (var client = new HttpClient(new HttpClientHandler
{
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
}))
{
string uri = "https://theServer/theSpringApp/api/theServerEventsSendingEndpoint";
StringContent content = new StringContent(JsonConvert.SerializeObject(params), Encoding.UTF8, "application/json");
HttpRequestMessage request = new HttpRequestMessage()
{
Method = HttpMethod.Post,
RequestUri = new Uri(uri),
Content = content
};
var response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
return await response.Result.Content.ReadAsStreamAsync();
}
}
catch (Exception ex)
{
log.Error("[myEndpoint] Error", ex);
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
}
我希望有一种方法可以简单地将我需要的http调用的输入转发到我的响应的输出…
我实际需要做的是:Spring应用程序在完成某个操作时需要流式传输文本。我需要实时读取该文本,以便在操作进行时在用户屏幕上显示一些内容。
我知道我可以做一个异步操作,把它的状态存储在某个地方,然后每秒钟调用一次来获取进度,但我真的想尝试用这种方式来做。
如果你需要更好地了解Spring服务中发生了什么,这是它的端点使用的代码的一部分(当然,我已经更改了一些东西的名称):
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
...
private ExecutorService executorService = Executors.newCachedThreadPool();
...
@PostMapping(path = "/api/theServerEventsSendingEndpoint", produces = MediaType.TEXT_PLAIN_VALUE)
public SseEmitter sendTheServerEvents(@RequestBody(required = true) MyEndpointParams params) {
SseEmitter emitter = new SseEmitter();
executorService.execute(() -> {
// does stuff in a loop, and in every loop iteration it does:
emitter.send("some text that depends on the operation it's doing");
// and once it ends:
emitter.complete();
// (I've omitted the code for error management of course)
});
return emitter;
}
谢谢你的帮助。
如果您不处理来自Java端的响应,只需要将其返回给客户端,我不会使用c#。我会使用URL重写模块和ARR,如果你是运行在IIS或apache2下的反向代理。
我对apache2不太了解,但是在IIS下你可以这样写:
https://learn.microsoft.com/en-us/iis/extensions/url-rewrite-module/reverse-proxy-with-url-rewrite-v2-and-application-request-routing
https://learn.microsoft.com/en-us/iis/extensions/url-rewrite-module/using-the-url-rewrite-module
如果你需要在发送到客户端之前处理响应,你也可以控制浏览器代码,我会使用WebSockets或SignalR。特别是当你真的得到像"事件"这样的东西时;或者Java端长时间运行的响应。
https://learn.microsoft.com/en us/aspnet/core/fundamentals/websockets?view=aspnetcore - 7.0
如果你想继续使用api和c#,我认为你不能使用控制器。你必须切换到处理程序或者在ASP.net Core中直接处理路径上的请求。一旦可以访问响应对象,就可以将Java调用的结果写入响应流。
在(伪)c#和Asp。Net Core 7应该是这样的:
app.MapGet("/your-api-url", async (HttpContext context) => {
//Call Java side with a Request or HttpClient
//while get responseFromJava
//optional responseFromJavaProcessed = process(responsFromJava)
await context.Response.WriteAsync(responseFromJavaProcessed);
//After Loop - just exit here
});