紧接着上文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
源码相对要轻松很多了。