博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Asp.net web Api源码分析-HttpResponseMessage
阅读量:6089 次
发布时间:2019-06-20

本文共 8751 字,大约阅读时间需要 29 分钟。

紧接着上文我们的Action已经执行完毕,现在需要把Action的返回结果转化为HttpResponseMessage 实例,我们也知道转化工作主要在HttpRequestMessage的CreateResponse附加方法中,

public static HttpResponseMessage CreateResponse
(this HttpRequestMessage request, HttpStatusCode statusCode, T value, HttpConfiguration configuration) { if (request == null) { throw Error.ArgumentNull("request"); } configuration = configuration ?? request.GetConfiguration(); if (configuration == null) { throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoConfiguration); } IContentNegotiator contentNegotiator = configuration.Services.GetContentNegotiator(); if (contentNegotiator == null) { throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoContentNegotiator, typeof(IContentNegotiator).FullName); } IEnumerable
formatters = configuration.Formatters; // Run content negotiation ContentNegotiationResult result = contentNegotiator.Negotiate(typeof(T), request, formatters); if (result == null) { // no result from content negotiation indicates that 406 should be sent. return new HttpResponseMessage { StatusCode = HttpStatusCode.NotAcceptable, RequestMessage = request, }; } else { MediaTypeHeaderValue mediaType = result.MediaType; return new HttpResponseMessage { // At this point mediaType should be a cloned value (the content negotiator is responsible for returning a new copy) Content = new ObjectContent
(value, result.Formatter, mediaType), StatusCode = statusCode, RequestMessage = request }; } }

 

首先这里需要一个IContentNegotiator实例,这里有这么一句代码:

  IContentNegotiator contentNegotiator = configuration.Services.GetContentNegotiator();在DefaultServices中有   SetSingle<IContentNegotiator>(new DefaultContentNegotiator());这句,我们知道默认的contentNegotiator是DefaultContentNegotiator实例。DefaultContentNegotiator构造函数也比较普通,这里我们还需要一个数据的格式化Formatters,这里有这么一句 IEnumerable<MediaTypeFormatter> formatters = configuration.Formatters;,在

曾提到Formatters主要有JsonMediaTypeFormatter,XmlMediaTypeFormatter, FormUrlEncodedMediaTypeFormatter,JQueryMvcFormUrlEncodedFormatter这4个。接着我们知道要干什么了,需要把我们的value转换为需要的格式,这里创建一个  ContentNegotiationResult result = contentNegotiator.Negotiate(typeof(T), request, formatters);实例,其中Negotiate的主要实现如下:

public virtual ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)

        {
             ....。。。。。。
            // Go through each formatter to compute how well it matches.
            Collection<MediaTypeFormatterMatch> matches = ComputeFormatterMatches(type, request, formatters);
            // Select best formatter match among the matches
            MediaTypeFormatterMatch bestFormatterMatch = SelectResponseMediaTypeFormatter(matches);
            // We found a best formatter
            if (bestFormatterMatch != null)
            {
                // Find the best character encoding for the selected formatter
                Encoding bestEncodingMatch = SelectResponseCharacterEncoding(request, bestFormatterMatch.Formatter);
                if (bestEncodingMatch != null)
                {
                    bestFormatterMatch.MediaType.CharSet = bestEncodingMatch.WebName;
                }
                MediaTypeHeaderValue bestMediaType = bestFormatterMatch.MediaType;
                MediaTypeFormatter bestFormatter = bestFormatterMatch.Formatter.GetPerRequestFormatterInstance(type, request, bestMediaType);
                return new ContentNegotiationResult(bestFormatter, bestMediaType);
            }
            return null;
        }
这里的Negotiate方法实现比较复杂,首先找到当前请求支持的一个MediaTypeFormatterMatch集合,然后再从这个集合中找到找最合适一个bestFormatterMatch (在实际开发中这个实例往往都是JsonMediaTypeFormatter),如果找到那么我们继续找一个bestEncodingMatch (最合适的一个编码很多时候这里是utf-8),这里最后调用GetPerRequestFormatterInstance方法得到一个MediaTypeFormatter实例,然后用这么实例创建一个ContentNegotiationResult实例。

现在我们回到CreateResponse方法中来,最后直接返回一个 HttpResponseMessage实例。

MediaTypeHeaderValue mediaType = result.MediaType;

                return new HttpResponseMessage

                {
                    // At this point mediaType should be a cloned value (the content negotiator is responsible for returning a new copy)
                    Content = new ObjectContent<T>(value, result.Formatter, mediaType),
                    StatusCode = statusCode,
                    RequestMessage = request
                };

这里的ObjectContent的创建也比较一般,我们也就忽略它吧。

现在我们回到HttpControllerHandler的BeginProcessRequest方法中来,

 Task responseBodyTask = _server.Value.SendAsync(request, CancellationToken.None)

                .Then(response => ConvertResponse(httpContextBase, response, request));

SendAsync已经执行完毕了,我们再来看看ConvertResponse吧,它主要就是把HttpResponseMessage的信息写到HttpResponseBase信息做。

   internal static Task ConvertResponse(HttpContextBase httpContextBase, HttpResponseMessage response, HttpRequestMessage request)

        {
            Contract.Assert(httpContextBase != null);
            Contract.Assert(request != null);
            // A null response creates a 500 with no content
            if (response == null)
            {
                CreateEmptyErrorResponse(httpContextBase.Response);
                return TaskHelpers.Completed();
            }
            CopyResponseStatusAndHeaders(httpContextBase, response);
            CacheControlHeaderValue cacheControl = response.Headers.CacheControl;
            // TODO 335085: Consider this when coming up with our caching story
            if (cacheControl == null)
            {
                // DevDiv2 #332323. ASP.NET by default always emits a cache-control: private header.
                // However, we don't want requests to be cached by default.
                // If nobody set an explicit CacheControl then explicitly set to no-cache to override the
                // default behavior. This will cause the following response headers to be emitted:
                //     Cache-Control: no-cache
                //     Pragma: no-cache
                //     Expires: -1
                httpContextBase.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            }
            // Asynchronously write the response body.  If there is no body, we use
            // a completed task to share the Finally() below.
            // The response-writing task will not fault -- it handles errors internally.
            Task writeResponseContentTask = (response.Content == null)
                                        ? TaskHelpers.Completed()
                                        : WriteResponseContentAsync(httpContextBase, response, request);
            return writeResponseContentTask.Finally(() =>
            {
                request.DisposeRequestResources();
                request.Dispose();
                response.Dispose();
            });
        }

这里的CopyResponseStatusAndHeaders方法比较简单我也不多说了,就是把HttpResponseMessage中的StatusAndHeaders信息写到 httpContextBase.Response中去,

我们还是关注一下WriteResponseContentAsync的实现吧:

 internal static Task WriteResponseContentAsync(HttpContextBase httpContextBase, HttpResponseMessage response, HttpRequestMessage request)

        {
            HttpResponseBase httpResponseBase = httpContextBase.Response;
            HttpContent responseContent = response.Content;
            var unused = response.Content.Headers.ContentLength;
            CopyHeaders(response.Content.Headers, httpContextBase);
            // Select output buffering based on the user-controlled buffering policy
            bool isBuffered = _bufferPolicySelector.Value != null ? _bufferPolicySelector.Value.UseBufferedOutputStream(response) : true;
            httpResponseBase.BufferOutput = isBuffered;
            return isBuffered
                    ? WriteBufferedResponseContentAsync(httpContextBase, responseContent, request)
                    : WriteStreamedResponseContentAsync(httpResponseBase, responseContent);
        }

前面CopyResponseStatusAndHeaders方法把HttpResponseMessage中的Headers信息写到 httpContextBase.Response中去了,这里继续把response.Content.Headers信息写到 httpContextBase.Response中去。

private static readonly Lazy<IHostBufferPolicySelector> _bufferPolicySelector =

            new Lazy<IHostBufferPolicySelector>(() => GlobalConfiguration.Configuration.Services.GetHostBufferPolicySelector());

在GlobalConfiguration中有这么一句 config.Services.Replace(typeof(IHostBufferPolicySelector), new WebHostBufferPolicySelector());所以我们知道_bufferPolicySelector这里其实是一个WebHostBufferPolicySelector实例,这里调用WebHostBufferPolicySelector的UseBufferedOutputStream方法来获取当前输出信息是否采用输出缓存,一般情况下这个个方法返回true,主要实现如下:

 public virtual bool UseBufferedOutputStream(HttpResponseMessage response)

        {
            HttpContent content = response.Content;
            if (content != null)
            {
                long? contentLength = content.Headers.ContentLength;
                if (contentLength.HasValue && contentLength.Value >= 0)
                {
                    return false;
                }
                return !(content is StreamContent || content is PushStreamContent);
            }
            return false;
        }
    }

一般情况下response.Content.Headers.ContentLength为null。所以后面调用的是WriteBufferedResponseContentAsync方法,而该方法的主要实现就一句代码

 Task  writeResponseContentTask = HttpResponseMessage.Content.CopyToAsync(HttpContextBase.Response.OutputStream);把当前HttpResponseMessage.Content中的数据全部写到HttpContextBase.Response.OutputStream中去。到这里我们的BeginProcessRequest就差不多执行完了,后面就是调用回调函数的事情了。

到本章为止,整个web api的主要流程就说完了,这个系列中有关参数的具体绑定和返回值的格式化我是忽略了的,他们的实现都相对比较复杂,后面再抽时间来看看他们是如何实现 吧,个人对web api也不是很熟悉,发现它和mvc 中的很多代码相似,相比之下mvc的使用比web api要广泛得多,所以这里建议大家多读读mvc的源码,读了之后再来读web api源码相对要轻松很多了。

转载地址:http://bblwa.baihongyu.com/

你可能感兴趣的文章
【BZOJ2019】nim
查看>>
LINUX内核调试过程
查看>>
【HDOJ】3553 Just a String
查看>>
Java 集合深入理解(7):ArrayList
查看>>
2019年春季学期第四周作业
查看>>
linux环境配置
查看>>
tomcat指定配置文件路径方法
查看>>
linux下查看各硬件型号
查看>>
epoll的lt和et模式的实验
查看>>
Flux OOM实例
查看>>
07-k8s-dns
查看>>
Android 中 ListView 分页加载数据
查看>>
oracle启动报错:ORA-00845: MEMORY_TARGET not supported on this system
查看>>
Go方法
查看>>
Dapper丶DapperExtention,以及AbpDapper之间的关系,
查看>>
搞IT的同学们,你们在哪个等级__那些年发过的帖子
查看>>
且谈语音搜索
查看>>
MySQL数据库导入导出常用命令
查看>>
低版本Samba无法挂载
查看>>
Telegraf+Influxdb+Grafana构建监控平台
查看>>