PushStreamContent в asp.net 5 / mvc 6 не работает
Я пытаюсь перенести проект веб-API (классический проект web.config), там использовать PushStreamContent в последнее веб-приложение asp.net 5 (project.json).
Моя проблема в том, что я не могу заставить PushStreamContent работать.
Когда я использую этот контроллер API - результат будет в формате JSON, а не в виде потока:
[Route("api/[controller]")]
public class EventsController : Controller
{
private static readonly ConcurrentQueue<StreamWriter> s_streamWriter = new ConcurrentQueue<StreamWriter>();
[HttpGet]
public HttpResponseMessage Get(HttpRequestMessage request)
{
HttpResponseMessage response = request.CreateResponse();
response.Content = new PushStreamContent(new Action<Stream, HttpContent, TransportContext>(WriteToStream), "text/event-stream");
return response;
}
private void WriteToStream(Stream outputStream, HttpContent headers, TransportContext context)
{
var streamWriter = new StreamWriter(outputStream) {AutoFlush = true};
s_streamWriter.Enqueue(streamWriter);
}
}
Если я изменю действие контроллера, чтобы вернуть задачу и обернуть PushStreamContent в класс MyPushStreamResult - как это:
[HttpGet]
public async Task<IActionResult> Get(HttpRequestMessage request)
{
var stream = new PushStreamContent(new Action<Stream, HttpContent, TransportContext>(WriteToStream), "text/event-stream");
return new MyPushStreamResult(stream, "text/event-stream");
}
public class MyPushStreamResult : ActionResult
{
public string ContentType { get; private set; }
public PushStreamContent Stream { get; private set; }
public MyPushStreamResult(PushStreamContent stream, string contentType)
{
Stream = stream;
ContentType = contentType;
}
public override async Task ExecuteResultAsync(ActionContext context)
{
var response = context.HttpContext.Response;
response.ContentType = ContentType;
await Stream.CopyToAsync(response.Body);
}
}
Запрос к действию моего контроллера теперь возвращает поток, НО поток не сбрасывается до его закрытия на стороне сервера или содержит много данных. Когда я отправляю данные в выходной поток PushStreamContent, я сбрасываю их после каждой записи текста, но я предполагаю, что сброс не выполняется в потоке response.Body.
Что я скучаю? Не удается найти образцы со структурой asp.net 5.
2 ответа
HttpResponseMessage
не обрабатывается специально в ASP.NET 5, если вы не используете Microsoft.AspNet.Mvc.WebApiCompatShim
пакет. Этот пакет не рекомендуется, если вы можете использовать функции ASP.NET 5 для выполнения подобных задач, и был создан для поддержки обратной совместимости.
Так как HttpResponseMessage
не считается особенным, его переводится как JSON JsonOutuptFormatter
как и любой другой объект.NET
Вместо PushStreamContent
Вы в настоящее время имеете доступ к потоку ответов напрямую через HttpContext.Response.Body
свойство, так что вы можете просто написать прямо в поток.
Обновлено:PushStreamContent
в веб-API позволяет напрямую писать в поток ответов. Этот тип был создан (командой веб-API и отсутствует как часть System.Net.Http
библиотека, в которой находятся все остальные типы контента), чтобы можно было писать напрямую в поток, например, из контроллера, фильтра и т. д. Альтернатива PushStreamContent была StreamContent
Это позволило вам предоставить только объект Stream, а затем слои хоста "скопировать" данные из исходного потока (например, "извлечение" данных). Также PushStreamContent сам по себе ничего особенного. Можно написать свой собственный тип, который происходит от HttpContent
,
Подвести итоги, PushStreamContent
разрешена запись в поток ответов напрямую, где, как и в ASP.NET 5, у нас есть прямой доступ к потоку, и поэтому вы можете писать в него.
Обновлено:
В самой базовой форме (верно, вы можете преобразовать в actionresult для тестируемости) должно работать следующее.
[HttpGet]
public Task Get()
{
HttpContext.Response.ContentType = "text/event-stream";
var sourceStream = // get the source stream
return sourceStream.CopyToAsync(HttpContext.Response.Body);
}
Как @Thieme прокомментировал сообщение @Kiran, ключевым моментом, как указано здесь, является использованиеawait HttpContext.Response.Body.FlushAsync();
например:
[Route("/api/sse")]
public class ServerSentEventController : Controller
{
[HttpGet]
public async Task Get()
{
var response = Response;
response.Headers.Add("Content-Type", "text/event-stream");
for(var i = 0; true; ++i)
{
await response
.WriteAsync($"data: Controller {i} at {DateTime.Now}\r\r");
response.Body.Flush();
await Task.Delay(5 * 1000);
}
}
}
Вам следует очистить поток Body во время выполнения задачи действия.