Asp.net web Api源碼分析-HttpResponseMessage


緊接着上文Asp.net web Api源碼分析-Action的執行 我們的Action已經執行完畢,現在需要把Action的返回結果轉化為HttpResponseMessage 實例,我們也知道轉化工作主要在HttpRequestMessage的CreateResponse附加方法中,

  public static HttpResponseMessage CreateResponse<T>(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<MediaTypeFormatter> 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<T>(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;,在Asp.net web Api源碼分析-HttpServer的創建
曾提到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源碼相對要輕松很多了。


免責聲明!

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



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