当前位置: 首页 > 最新文章 > 正文

从文件下载视角来理解Web API

REST是Representational State Transfer的简称,中文翻译为“表征状态转移”;REST从资源的角度来审视整个网络,它将分布在网络中某个节点中的资源通过URI进行标识,客户端应用通过URI来获取资源的表征,获得这些表征致使这些应用程序转变了状态。随着不断获取资源的表征,客户端应用不断地在转变着状态。Web API是一种web形式的服务,其需要可以表征对资源操作的各种情况

admin

REST是Representational State Transfer的简称,中文翻译为“表征状态转移”;REST从资源的角度来审视整个网络,它将分布在网络中某个节点中的资源通过URI进行标识,客户端应用通过URI来获取资源的表征,获得这些表征致使这些应用程序转变了状态。随着不断获取资源的表征,客户端应用不断地在转变着状态。Web API是一种web形式的服务,其需要可以表征对资源操作的各种情况,也就是需要可以通过返回值来表征操作的结果;平时我们都是如下直接返回对应的模型对象publi

一、问题源起

从Web From过来的人应该会比较熟悉以下下载文件的代码;

[HttpPost][Route("Download")]public void Download(){    HttpResponse response = HttpContext.Current.Response;    response.Clear();    response.BufferOutput = true;    response.AddHeader("Content-Type", "application/octet-stream");                response.AddHeader("Content-Disposition", "attachment;filename=myfile.txt");    GetFileContent().CopyTo(response.OutputStream);    response.Flush();}

代码中直接修改Response的header,并叫文件内容写入Response的OutputStream中,最后进行Flush刷新;执行之后可以正常下载文件,但是发现执行的过程中会报如下的错误

System.Web.HttpException (0x80004005): Server cannot set status after HTTP headers have been sent.at System.Web.HttpResponse.set_StatusCode(Int32 value)at System.Web.Http.WebHost.HttpControllerHandler.<CopyResponseStatusAndHeadersAsync>d__25.MoveNext()— End of stack trace from previous location where exception was thrown —at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)at System.Web.Http.WebHost.HttpControllerHandler.<CopyResponseAsync>d__15.MoveNext()— End of stack trace from previous location where exception was thrown —at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)at System.Web.Http.WebHost.HttpControllerHandler.<ProcessRequestAsyncCore>d__12.MoveNext()— End of stack trace from previous location where exception was thrown —at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)at System.Web.TaskAsyncHelper.EndTask(IAsyncResult ar)at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

二、问题原因分析

从错误的堆栈可以看到是ASP.NET的Web框架在请求执行的最后要设置StatusCode的时候抛出了异常;

查看.NET Framework 4.7.2中Response的StatusCode的代码,可以看到首先会检测_headersWritten字段,如果字段值为true,则会抛出我们看到的异常;

public int StatusCode {    get {        return _statusCode;    }    set {        if (_headersWritten)            throw new HttpException(SR.GetString(SR.Cannot_set_status_after_headers_sent));        if (_statusCode != value) {            _statusCode = value;            _subStatusCode = 0;            _statusDescription = null;            _statusSet = true;        }    }}

我们查看HttpHeaderCollection的Add及SetHeader方法,发现并没有修改_headersWritten的值,所以直接向Response添加header并不会导致这个异常;

我们查看Response的Flush方法,发现里边会将_headersWritten的值设置为true;但是如果我们注释掉Flush则就无法下载文件了;

三、从REST角度理解Web API的返回值

我们使用的是Web API,它是从REST借鉴过来的概念;

REST与技术无关,代表的是一种软件架构风格。REST是Representational State Transfer的简称,中文翻译为“表征状态转移”;REST从资源的角度来审视整个网络,它将分布在网络中某个节点中的资源通过URI进行标识,客户端应用通过URI来获取资源的表征,获得这些表征致使这些应用程序转变了状态。随着不断获取资源的表征,客户端应用不断地在转变着状态。

Web API是一种web形式的服务,其需要可以表征对资源操作的各种情况,也就是需要可以通过返回值来表征操作的结果;

平时我们都是如下直接返回对应的模型对象

public class ProductsController : ApiController{    public IEnumerable<Product> Get()    {        return GetAllProductsFromDB();    }}

Web API 使用请求中的 Accept 标头来选择格式化程序,默认情况下会返回json格式的数据;

HTTP/1.1 200 OKContent-Type: application/json; charset=utf-8Server: Microsoft-IIS/8.0Date: Mon, 27 Jan 2014 08:53:35 GMTContent-Length: 56[{"Id":1,"Name":"Yo-yo","Category":"Toys","Price":6.95}]

虽然框架提供了这种简单的方式,但这种方法的缺点是不能直接返回错误代码如404;Web API内部还是会将各种形式的返回结果转化为HttpResponseMessage,最终将HttpResponseMessage转换为 HTTP 响应消息;

public class ValuesController : ApiController{    public HttpResponseMessage Get()    {        HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, "value");        response.Content = new StringContent("hello", Encoding.Unicode);        response.Headers.CacheControl = new CacheControlHeaderValue()        {            MaxAge = TimeSpan.FromMinutes(20)        };        return response;    } }

Web API返回的响应形式如下

HTTP/1.1 200 OKCache-Control: max-age=1200Content-Length: 10Content-Type: text/plain; charset=utf-16Server: Microsoft-IIS/8.0Date: Mon, 27 Jan 2014 08:53:35 GMThello

四、使用Web API的方式实现文件的下载

我们通过HttpResponseMessage来承载文件内容,并修改对应的header;

[HttpPost][Route("Download")]public HttpResponseMessage Download(){    HttpResponse response  = Request.CreateResponse(HttpStatusCode.OK);    response.Content = new StreamContent(GetFileContent());    response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");    response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")    {        FileName = filename    };      return response;}

五、Web API处理管道简介

整个的Web API处理管道如下图所示;

首先Web API通过承载URL模式和对应的处理类的HttpWebRoute对象添加到路由集合;当请求到来的时候,会通过路由模块定位到对应的HttpControllerHandler,HttpControllerHandler会将请求转化成HttpRequestMessage,然后转发给HttpServer,HttpServer会将请求逐个传递个HttpMessageHandler链中的对象进行处理,并最终通过HttpControllerDispatcher转发给实现服务的那个控制器的action;

从文件下载视角来理解Web API

原文地址:https://www.cnblogs.com/wufengtinghai/p/16065503.html


上一篇: 微信开发电商微商具体流程(微商具体流程) 下一篇:Testlink 安装 -centos
返回顶部