在Asp.net core返回PushStream

最近用asp.net core webapi实现了一个实时视频流的推送功能,在Asp.net中,这个是通过PushStreamContent来实现的。

基于对asp.net core的知识,随手写了一个(要求控制器继承自Controller基类)

[HttpGet]
public async Task Get()
{
    var response = HttpContext.Response;
    response.ContentType = "text/html";
    response.StatusCode = 200;
    var stream = HttpContext.Response.Body;

    while (true)
    {
        await Task.Delay(1000);
        var content = DateTime.Now + @"<br>";
        var data = Encoding.Default.GetBytes(content);
        await stream.WriteAsync(data, 0, data.Length);
        await stream.FlushAsync();
    }
}

 

使用chrome调试这个接口时,发现它确实行之有效的将当前的时间推送到了浏览器的页面上。

然而,当我进一步的调试它的异常情况时,发现就算将chrome关掉,这个程序却依然在继续运行。从调试器中看到stream的状态为Aborted,已经识别到位终止的流了。

  

并且从VS的调试窗口也能看到异常信息:

  

但下面这两行就是不抛异常:

await stream.WriteAsync(data, 0, data.Length);
await stream.FlushAsync();

单单从接口的实现角度上来看,这个已经不合理了。这是一个很大的坑,功能看上去还是正确的,没有详细调试还看不出来。一个不留神就踩上了。不知道微软为什么要这么设计。

埋怨归埋怨,问题还是要解决的。我查看了下FileStreamResult的源码,发现它是靠HttpContext.RequestAborted来判断客户端是否终止了的。这是一个CancellationToken类型的对象,当客户端连接断开后,它就处于被取消的状态。

知道原因后,就可以知道如何修改我的程序了。

[HttpGet]
public async Task Get()
{
    var cancel = HttpContext.RequestAborted;
 
    var response = HttpContext.Response;
    response.ContentType = "text/html";
    response.StatusCode = 200;
    var stream = HttpContext.Response.Body;
 
    while (true)
    {
        cancel.ThrowIfCancellationRequested();
        await Task.Delay(1000, cancel);
        var content = DateTime.Now + @"<br>";
        var data = Encoding.Default.GetBytes(content);
        await stream.WriteAsync(data, 0, data.Length, cancel);
        await stream.FlushAsync(cancel);
    }
}

 

再然后就是封装了,我这里将其封装为了一个PushStreamResult,这样就可以在PocoController中使用了。

class MyPushStreamResult :IActionResult
{
    Func<Stream, CancellationToken, Task> _pushAction;
    string _contentType;
 
    public MyPushStreamResult(Func<Stream, CancellationToken, Task> pushAction, string contentType)
    {
        _pushAction = pushAction;
        _contentType = contentType;
    }

    public Task ExecuteResultAsync(ActionContext context)
    {
        var response = context.HttpContext.Response;
        response.ContentType = _contentType;
        response.StatusCode = 200;
 

        return _pushAction(response.Body, context.HttpContext.RequestAborted);
    }
}

 

使用方法如下:

[HttpGet]
public IActionResult Get()
{
    return new MyPushStreamResult(pushData, "text/html");
}
 
async Task pushData(Stream stream, CancellationToken cancel)
{
    while (true)
    {
        if (cancel.IsCancellationRequested)
            return;
        await Task.Delay(1000, cancel);
        var content = DateTime.Now + @"<br>";
        var data = Encoding.Default.GetBytes(content);
        await stream.WriteAsync(data, 0, data.Length, cancel);
        await stream.FlushAsync(cancel);
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值