在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);
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM