ASP.NET Core IActionResult返回Chrome中的随机内容

时间:2017-05-05 15:26:36

标签: http asp.net-core asp.net-core-mvc

我正在尝试在ASP.NET Core中创建一个IActionResult来处理range requests以向HTML5媒体播放器提供内容。

我认为我得到了实现,并且在使用cURL或wget这样的基本用户代理调用时似乎正常工作,但是当Chrome调用它来获取音频代码的内容时,它就无法运行。

我手动检查了返回的内容和物理文件,以确保我提供正确的内容,它似乎正在运作。

全内容请求检查:

dd ibs=1 if=CA98AF68-766B-457C-828E-DB3B8C2A57BE.webm 2> /dev/null | \
openssl dgst -sha256 -binary | \
openssl enc -base64 -A
80AE1Im8ebN12f3te7UvBcug7DkGluam21wgL7zJ4h4=

curl --range 0- --silent http://localhost:3038/Media/CA98AF68-766B-457C-828E-DB3B8C2A57BE/webm | \
openssl dgst -sha256 -binary | \
openssl enc -base64 -A
80AE1Im8ebN12f3te7UvBcug7DkGluam21wgL7zJ4h4=

远程内容请求检查:

dd ibs=1 if=CA98AF68-766B-457C-828E-DB3B8C2A57BE.webm count=1024 2> /dev/null | \
openssl dgst -sha256 -binary | \
openssl enc -base64 -A
SFAoua0OkQ3ZbEnXXd42CqcucQdkOTxpkjQwBNKOF7Y=

curl --range 0-1024 --silent http://localhost:3038/Media/CA98AF68-766B-457C-828E-DB3B8C2A57BE/webm \
openssl dgst -sha256 -binary | \
openssl enc -base64 -A
SFAoua0OkQ3ZbEnXXd42CqcucQdkOTxpkjQwBNKOF7Y=

物理文件块的哈希值和ASP.NET返回的内容对于完整文件和部分文件都是相同的,所以我认为代码是正确的。

然而,Chrome正在调用与获取随机数据完全相同的URI:有时更多,有时更少,有时会引发请求中止并且任务取消异常抛出。

返回80.2千字节的请求:

A request with 80.2 kilobytes returned.

请求返回196个字节:

A request with 196 bytes returned.

返回132千字节的请求:

A request with 132 kilobytes returned

任务取消了引发的异常:

A raised task canceled exception.

实现非常简单,它支持(目前)只有单范围请求而且没有铃声或口哨声(以下代码是一个简单的沙箱,绝不是生产准备好的。)

这是远程操作结果实现:

namespace Sandbox.Results
{
    using System;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Threading.Tasks;

    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Http.Extensions;
    using Microsoft.AspNetCore.Http.Headers;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.FileProviders;
    using Microsoft.Net.Http.Headers;

    public class StreamingActionResult : IActionResult, IDisposable
    {
        private readonly string contentType;
        private readonly FileStream stream;

        public StreamingActionResult(IFileInfo fileInfo, string contentType)
        {
            this.stream = new FileStream(fileInfo.PhysicalPath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.Asynchronous);
            this.contentType = contentType;
        }

        public void Dispose()
        {
            this.Dispose(true);

            GC.SuppressFinalize(this);
        }

        public async Task ExecuteResultAsync(ActionContext context)
        {
            context.HttpContext.Response.Headers.Add("Accept-Ranges", "bytes");

            RequestHeaders requestHeaders = context.HttpContext.Request.GetTypedHeaders();

            if (requestHeaders.Range == null)
            {
                context.HttpContext.Response.ContentLength = this.stream.Length - this.stream.Position;
                context.HttpContext.Response.ContentType = this.contentType;
                context.HttpContext.Response.StatusCode = (int) HttpStatusCode.PartialContent;

                await this.stream.CopyToAsync(context.HttpContext.Response.Body, 4096, context.HttpContext.RequestAborted);

                return;
            }

            if (requestHeaders.Range.Unit != "bytes" || requestHeaders.Range.Ranges.Count != 1)
            {
                context.HttpContext.Response.StatusCode = (int) HttpStatusCode.RequestedRangeNotSatisfiable;

                return;
            }

            RangeItemHeaderValue range = requestHeaders.Range.Ranges.First();

            this.stream.Seek(range.From.Value, SeekOrigin.Begin);

            context.HttpContext.Response.ContentType = this.contentType;
            context.HttpContext.Response.StatusCode = (int) HttpStatusCode.PartialContent;

            if (range.To.HasValue)
            {
                context.HttpContext.Response.ContentLength = range.To.Value - range.From.Value;
                context.HttpContext.Response.Headers.Add("Content-Range", $"bytes {range.From.Value:D}-{range.To.Value:D}/{this.stream.Length:D}");

                await StreamCopyOperation.CopyToAsync(this.stream, context.HttpContext.Response.Body, range.To.Value - range.From.Value, 4096, context.HttpContext.RequestAborted);
            }
            else
            {
                context.HttpContext.Response.ContentLength = this.stream.Length - range.From.Value;
                context.HttpContext.Response.Headers.Add("Content-Range", $"bytes {range.From.Value:D}-{this.stream.Length:D}/{this.stream.Length:D}");

                await this.stream.CopyToAsync(context.HttpContext.Response.Body, 4096, context.HttpContext.RequestAborted);
            }
        }

        protected virtual void Dispose(bool isDisposing)
        {
            if (isDisposing)
            {
                this.stream.Dispose();
            }
        }
    }
}

这是使用行动结果的方式:

namespace Sandbox.Controllers
{
    using System;

    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Extensions.FileProviders;

    using Sandbox.Results;

    [Route("/Media")]
    public class MediaController : Controller
    {
        private readonly IHostingEnvironment environment;

        public MediaController(IHostingEnvironment environment)
        {
            this.environment = environment;
        }

        [HttpGet("{id}/{format}")]
        public IActionResult Get([FromRoute] Guid id, [FromRoute] string format)
        {
            IFileInfo fileInfo = this.environment.ContentRootFileProvider.GetFileInfo($"Media\\{id:D}.{format}");

            return new StreamingActionResult(fileInfo, $"audio/{format}");
        }
    }
}

这就是媒体元素的宣告方式:

<!doctype html>
<html>
    <head>
        <title>Sandbox</title>
    </head>
    <body>
        <h1>Sandbox</h1>
        <audio controls preload="metadata">
            <source src="/Media/CA98AF68-766B-457C-828E-DB3B8C2A57BE/webm" type="audio/webm" />
            <p>Your browser does not support audio content.</p>
        </audio>
    </body>
</html>

我无法理解错误,如果它是我的代码中的内容,Chrome中的内容或其他内容。

0 个答案:

没有答案