Asp.Net WebApi核心对象解析(下篇)

时间:2022-09-06 23:39:14

在接着写Asp.Net WebApi核心对象解析(下篇)之前,还是一如既往的扯扯淡,元旦刚过,整个人还是处于晕的状态,一大早就来处理系统BUG,简直是坑爹(好在没让我元旦赶过来该BUG),队友挖的坑,还让我含着泪去填。改BUG前看队友写的代码,这里就不评价了,反正是边改边骂,我的嘴巴就没停过,作为开发者,我那时的心情,就不再描述了,反正是找不到一个好词形容。

新年展望,我感觉我是没啥好展望的,反正去年的展望是一个都没实现,事情该怎么做还是怎么做的,估计大多数人跟我差不多,任何事不能强求,事前努力去办,事后得不到也就看淡一点,太苛求,迟早身心俱疲。

扯淡完毕,接着聊正事,上一篇写的是Asp.Net WebApi核心对象解析(上篇),本文是下篇,不管写的怎么样,还望大家多多指正。

一.WebApi处理架构:

我们在学习Asp.Net WebApi时,应该对Asp.Net WebApi的内部运行机制有一个大致的了解,很多人说了解这些基本原理的意义不大,实际开发中应用不到而且还浪费时间,这样说有一定的道理,但是如果我们的眼光放的长远一些,就不会这样想聊,我们了解基本原理后,可以在一定的程度上帮助我们处理一些程序底层的bug,而且还有可以让我们从中学会思考,去深入的理解设计者的意图,有利于我们更加熟练的运用。

在谈WebApi处理架构之前,我们还是来看一下微软为WebApi提供的海报,这里就不拿图了,需要看的可以点击下载:下载地址

Asp.Net Web Api处理架构可以分为三层,分别是托管层、消息处理程序管道、控制器处理。

托管层:位于WebApi和底层HTTP栈之间,是最底层负责WebApi托管。

消息处理程序管道层:用与实现消息的横切关注点,例如日志和缓存。

控制器处理层:控制器和操作是在这一层进行调用,参数再次绑定和验证,HTTP响应消息也在这里创建。

对于托管层测说明,会在下面进行讲解。消息处理程序是对一个操作的抽象,它接受HTTP请求消息并返回HTTP响应消息。连接消息处理程序管道和控制器处理层的桥梁是控制器分发程序。控制器分发还是一个消息处理程序,主要是选择、创建和调用正确的控制器来处理请求。

二.WebApi托管方式解析:

在Asp.Net Web Api的托管方式有三种,接下来我们来大致了解一下这三种托管方式。

(1).在任何Windows进程中自托管。

(2).Web托管,即在IIS之上使用ASP.NET管道进行托管。(如果需要了解IIS和ASPI.NET管道的知识,可以自己搜索查看,笔者建议做web开发的人员了解一下其运行机制,有利于我们对asp.net web程序有一个深入的了解。)

(3).OWIN托管。(在一个owin兼容的服务器上建立一个webapi层)

在使用web托管时,所使用的是ASP.NET的管道和路由功能,将HTTP请求转发到一个新的ASP.NET处理程序,HttpControllerHandler中。这个程序接收到HtppRequest实例转换成HttpRequestMesssage实例,然后推送到WebApi管道,从而在传统的asp.net管道和新的asp.net webapi架构间建立起链接。

这里我们具体了解一下HttpControllerHandler这个类:

HttpControllerHandler类在 System.Web.Http.WebHost命名空间下,根据命名空间的名称,我们就可以清晰的了解到该命名空间主要用于创建web托管的。

public class HttpControllerHandler : IHttpAsyncHandler, IHttpHandler

该类继承自IHttpAsyncHandler, IHttpHandler接口,我们由底层代码可知,该类实际具体继承自HttpTaskAsyncHandler类,用与Http任务异步处理程序。接下来我们具体看一下该类的一些方法:

1.ProcessRequestAsync方法:提供处理异步任务的代码。

    /// <summary>
/// 提供处理异步任务的代码
/// </summary>
/// <returns>
/// 异步任务。
/// </returns>
/// <param name="context">HTTP 上下文。</param>
public override Task ProcessRequestAsync(HttpContext context)
{
return this.ProcessRequestAsyncCore((HttpContextBase) new HttpContextWrapper(context));
} internal async Task ProcessRequestAsyncCore(HttpContextBase contextBase)
{
HttpRequestMessage request = HttpContextBaseExtensions.GetHttpRequestMessage(contextBase) ?? HttpControllerHandler.ConvertRequest(contextBase);
System.Net.Http.HttpRequestMessageExtensions.SetRouteData(request, this._routeData);
CancellationToken cancellationToken = HttpResponseBaseExtensions.GetClientDisconnectedTokenWhenFixed(contextBase.Response);
HttpResponseMessage response = (HttpResponseMessage) null;
try
{
response = await this._server.SendAsync(request, cancellationToken);
await HttpControllerHandler.CopyResponseAsync(contextBase, request, response, HttpControllerHandler._exceptionLogger.Value, HttpControllerHandler._exceptionHandler.Value, cancellationToken);
}
catch (OperationCanceledException ex)
{
contextBase.Request.Abort();
}
finally
{
System.Net.Http.HttpRequestMessageExtensions.DisposeRequestResources(request);
request.Dispose();
if (response != null)
response.Dispose();
}
}

该方法是一个异步方法,并且接收的参数是HttpContext,表示http上下文内容,调用GetHttpRequestMessage()获取HttpRequestMessage对象实例,调用SetRouteData()方法设置路由信息,调用GetClientDisconnectedTokenWhenFixed()方法获取客户端断开令牌时修复,并返回取消令牌,该方法生成http请求后,对消息进行异步发送处理操作。

2.GetStreamContent方法:获取请求获取流内容。

 private static HttpContent GetStreamContent(HttpRequestBase requestBase, bool bufferInput)
{
if (bufferInput)
return (HttpContent) new HttpControllerHandler.LazyStreamContent((Func<Stream>) (() =>
{
if (requestBase.ReadEntityBodyMode == ReadEntityBodyMode.None)
return (Stream) new SeekableBufferedRequestStream(requestBase);
if (requestBase.ReadEntityBodyMode == ReadEntityBodyMode.Classic)
{
requestBase.InputStream.Position = 0L;
return requestBase.InputStream;
}
if (requestBase.ReadEntityBodyMode == ReadEntityBodyMode.Buffered)
{
if (requestBase.GetBufferedInputStream().Position <= 0L)
return (Stream) new SeekableBufferedRequestStream(requestBase);
requestBase.InputStream.Position = 0L;
return requestBase.InputStream;
}
throw new InvalidOperationException(string.Format((IFormatProvider) CultureInfo.CurrentCulture, SRResources.RequestBodyAlreadyReadInMode, new object[]
{
(object) ReadEntityBodyMode.Bufferless
}));
}));
return (HttpContent) new HttpControllerHandler.LazyStreamContent((Func<Stream>) (() =>
{
if (requestBase.ReadEntityBodyMode == ReadEntityBodyMode.None)
return requestBase.GetBufferlessInputStream();
if (requestBase.ReadEntityBodyMode == ReadEntityBodyMode.Classic)
throw new InvalidOperationException(SRResources.RequestStreamCannotBeReadBufferless);
if (requestBase.ReadEntityBodyMode == ReadEntityBodyMode.Bufferless)
{
Stream bufferlessInputStream = requestBase.GetBufferlessInputStream();
if (bufferlessInputStream.Position > 0L)
throw new InvalidOperationException(SRResources.RequestBodyAlreadyRead);
return bufferlessInputStream;
}
throw new InvalidOperationException(string.Format((IFormatProvider) CultureInfo.CurrentCulture, SRResources.RequestBodyAlreadyReadInMode, new object[]
{
(object) ReadEntityBodyMode.Buffered
}));
}));
}

该方法用与获取HTTP请求的流内容,根据参数HttpRequestBase可知,该方法接受到HTTP请求后,对消息进行处理,bufferInput参数判断传入的是否为流对象,传入的流对象,进入LazyStreamContent类进行处理,LazyStreamContent类的构造函数接受一个含有返回值的委托。

public LazyStreamContent(Func<Stream> getStream)
{
this._getStream = getStream;
}

GetStreamContent方法的相关操作主要是对HTTP请求内容的解析操作。

三.WebApi核心对象HttpRequestMessage和HttpResponseMessage:

1.HttpRequestMessageExtensions:HTTP消息请求实例的扩展类。

(1).GetRequestContext方法:获取HTTP请求消息内容:

 public static HttpRequestContext GetRequestContext(this HttpRequestMessage request)
{
if (request == null)
throw Error.ArgumentNull("request");
return HttpRequestMessageExtensions.GetProperty<HttpRequestContext>(request, HttpPropertyKeys.RequestContextKey);
}

根据传入的HTTP请求,调用GetProperty()方法获取属性,我们具体看一下GetProperty()方法:

 private static T GetProperty<T>(this HttpRequestMessage request, string key)
{
T obj;
DictionaryExtensions.TryGetValue<T>(request.Properties, key, out obj);
return obj;
}

该方法获取请求对象,并根据KEY值调用TryGetValue()方法获取属性。

(2).CreateResponse():创建请求信息的响应。

    /// <summary>
/// 创建与关联的 HttpRequestMessage连接的HttpResponseMessage
/// </summary>
/// <returns>
/// 与关联的 HttpRequestMessage连接的已初始化 HttpResponseMessage
/// </returns>
/// <param name="request">导致此响应消息的 HTTP 请求消息。</param>
<param name="statusCode">HTTP 响应状态代码。</param>
<param name="value">HTTP 响应消息的内容。</param>
<param name="configuration">包含用于解析服务的依赖关系解析程序的 HTTP 配置。</param>
<typeparam name="T">HTTP 响应消息的类型。</typeparam>
public static HttpResponseMessage CreateResponse<T>(this HttpRequestMessage request, HttpStatusCode statusCode, T value, HttpConfiguration configuration)
{
if (request == null)
throw Error.ArgumentNull("request");
configuration = configuration ?? HttpRequestMessageExtensions.GetConfiguration(request);
if (configuration == null)
throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoConfiguration);
IContentNegotiator contentNegotiator = ServicesExtensions.GetContentNegotiator(configuration.Services);
if (contentNegotiator == null)
throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoContentNegotiator, (object) typeof (IContentNegotiator).FullName);
IEnumerable<MediaTypeFormatter> formatters = (IEnumerable<MediaTypeFormatter>) configuration.Formatters;
return NegotiatedContentResult<T>.Execute(statusCode, value, contentNegotiator, request, formatters);
}

该方法根据请求消息提供的相关信息,在处理完毕请求消息后,创建响应消息内容。

2.HttpResponseMessageExtensions:HTTP应答消息实例的扩展类。

TryGetContentValue():获取内容的值。

    /// <summary>
/// 尝试检索HttpResponseMessageExtensions 的内容的值。
/// </summary>
/// <returns>
/// 内容值的检索结果。
/// </returns>
/// <param name="response">操作的响应。</param>
<param name="value">内容的值。</param>
<typeparam name="T">要检索的值的类型。</typeparam>
public static bool TryGetContentValue<T>(this HttpResponseMessage response, out T value)
{
if (response == null)
throw Error.ArgumentNull("response");
ObjectContent objectContent = response.Content as ObjectContent;
if (objectContent != null && objectContent.Value is T)
{
value = (T) objectContent.Value;
return true;
}
value = default (T);
return false;
}

根据传入的响应消息对象获取响应消息的内容。

四.WebApi核心对象HttpClient:

上面介绍完服务器端的接收和响应HTTP请求的操作方法,接下来介绍一个客户端生成HTTP请求,用与请求和获取服务器返回的消息,在新版本的.NET中,提供类HTTPClient类用来在客户端生成和获取HTTP请求的类。

1.属性概要:

BaseAddress:获取或设置发送请求时所使用的互联网资源的统一资源标识符(URI)的基地址。

DefaultRequestHeaders:获取应随每个请求发送的头。

MaxResponseContentBufferSize:获取或设置中的最大字节数读取响应内容时缓冲。

Timeout:获取或设置的毫秒数请求超时之前等待。

2.方法概要:

CancelPendingRequests:取消此实例上的所有未决请求。

DeleteAsync(String):发送一个DELETE请求到指定的URI为异步操作。

GetAsync(String):发送GET请求到指定的URI为异步操作。

GetStreamAsync(String):发送GET请求到指定的URI并返回响应主体作为一个异步操作流。

PostAsync(String, HttpContent):发送POST请求到指定的URI作为一个异步操作。

SendAsync(HttpRequestMessage):发送一个HTTP请求作为一个异步操作。

3.方法和属性解析:

(1).BaseAddress:获取或设置发送请求时所使用的互联网资源的统一资源标识符(URI)的基地址。

    /// <summary>
/// 获取或设置发送请求时使用的 Internet 资源的统一资源标识符 (URI) 的基址。
/// </summary>
/// <returns>
/// 返回 <see cref="T:System.Uri"/>。发送请求时使用的 Internet 资源的统一资源标识符 (URI) 的基址。
/// </returns>
[__DynamicallyInvokable]
public Uri BaseAddress
{
[__DynamicallyInvokable] get
{
return this.baseAddress;
}
[__DynamicallyInvokable] set
{
HttpClient.CheckBaseAddress(value, "value");
this.CheckDisposedOrStarted();
if (Logging.On)
Logging.PrintInfo(Logging.Http, (object) this, "BaseAddress: '" + (object) this.baseAddress + "'");
this.baseAddress = value;
}
}

(2).GetContentAsync:根据指定的uri异步的获取内容。

private Task<T> GetContentAsync<T>(Uri requestUri, HttpCompletionOption completionOption, T defaultValue, Func<HttpContent, Task<T>> readAs)
{
TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
HttpUtilities.ContinueWithStandard<HttpResponseMessage>(this.GetAsync(requestUri, completionOption), (Action<Task<HttpResponseMessage>>) (requestTask =>
{
if (HttpClient.HandleRequestFaultsAndCancelation<T>(requestTask, tcs))
return;
HttpResponseMessage result = requestTask.Result;
if (result.Content == null)
{
tcs.TrySetResult(defaultValue);
}
else
{
try
{
HttpUtilities.ContinueWithStandard<T>(readAs(result.Content), (Action<Task<T>>) (contentTask =>
{
if (HttpUtilities.HandleFaultsAndCancelation<T>((Task) contentTask, tcs))
return;
tcs.TrySetResult(contentTask.Result);
}));
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}
}));
return tcs.Task;
}

该方法为异步的方法,用与生成get、post请求后,获取对应的内容。TrySetResult()方法将底层System.Threading.Tasks.Task`1转换为RanToCompletion状态。

(3).SendAsync(): 以异步操作发送 HTTP 请求。

    /// <summary>
/// 以异步操作发送 HTTP 请求。
/// </summary>
/// <returns>
/// 返回 <see cref="T:System.Threading.Tasks.Task`1"/>。表示异步操作的任务对象。
/// </returns>
/// <param name="request">要发送的 HTTP 请求消息。</param>
<param name="completionOption">操作应完成时(在响应可利用或在读取整个响应内容之后)。</param>
<param name="cancellationToken">取消操作的取消标记。</param>
<exception cref="T:System.ArgumentNullException">
<paramref name="request"/> 为 null。</exception>
<exception cref="T:System.InvalidOperationException">请求消息已由 <see cref="T:System.Net.Http.HttpClient"/> 实例发送。</exception>
[__DynamicallyInvokable]
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
{
if (request == null)
throw new ArgumentNullException("request");
this.CheckDisposed();
HttpClient.CheckRequestMessage(request);
this.SetOperationStarted();
this.PrepareRequestMessage(request);
CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, this.pendingRequestsCts.Token);
this.SetTimeout(linkedCts);
TaskCompletionSource<HttpResponseMessage> tcs = new TaskCompletionSource<HttpResponseMessage>();
HttpUtilities.ContinueWithStandard<HttpResponseMessage>(base.SendAsync(request, linkedCts.Token), (Action<Task<HttpResponseMessage>>) (task =>
{
try
{
this.DisposeRequestContent(request);
if (task.IsFaulted)
this.SetTaskFaulted(request, linkedCts, tcs, task.Exception.GetBaseException());
else if (task.IsCanceled)
{
this.SetTaskCanceled(request, linkedCts, tcs);
}
else
{
HttpResponseMessage result = task.Result;
if (result == null)
this.SetTaskFaulted(request, linkedCts, tcs, (Exception) new InvalidOperationException(SR.net_http_handler_noresponse));
else if (result.Content == null || completionOption == HttpCompletionOption.ResponseHeadersRead)
this.SetTaskCompleted(request, linkedCts, tcs, result);
else
this.StartContentBuffering(request, linkedCts, tcs, result);
}
}
catch (Exception ex)
{
if (Logging.On)
Logging.Exception(Logging.Http, (object) this, "SendAsync", ex);
tcs.TrySetException(ex);
}
}));
return tcs.Task;
}

该方法是以异步发的方法将HTTP请求发送出去,该方法的三个参数中,HttpRequestMessage表示http请求对象,HttpCompletionOption表示操作完成项,CancellationToken表示取消令牌。在发送HTTP请求之前,调用CheckRequestMessage方法对消息进行检查。在使用异步方法时,需要考虑操作的取消等外部因素对方法的影响。

介绍完毕HttpClient对象,对于HttpClient的实际操作就不做介绍,HttpClient对象的使用非常的简单,但是该类的底层实现还是比较的复杂。

五.总结:

本文分为上下两篇,简单的介绍类一下Asp.Net WebApi的一些核心对象,并简单介绍了Asp.Net WebApi路由机制,处理架构,托管方式等等,如有不足和错误之处还望多多指正。(对我来说总算是写完了,写了上篇就得写下篇,实在痛苦)

Asp.Net WebApi核心对象解析(下篇)的更多相关文章

  1. Asp&period;Net WebApi核心对象解析(二)

    在接着写Asp.Net WebApi核心对象解析(下篇)之前,还是一如既往的扯扯淡,元旦刚过,整个人还是处于晕的状态,一大早就来处理系统BUG,简直是坑爹(好在没让我元旦赶过来该BUG),队友挖的坑, ...

  2. Asp&period;Net WebApi核心对象解析

    在接着写Asp.Net WebApi核心对象解析(下篇)之前,还是一如既往的扯扯淡,元旦刚过,整个人还是处于晕的状态,一大早就来处理系统BUG,简直是坑爹(好在没让我元旦赶过来该BUG),队友挖的坑, ...

  3. Asp&period;Net WebApi核心对象解析(上篇)

    生活需要自己慢慢去体验和思考,对于知识也是如此.匆匆忙忙的生活,让人不知道自己一天到晚都在干些什么,似乎每天都在忙,但又好似不知道自己到底在忙些什么.不过也无所谓,只要我们知道最后想要什么就行.不管怎 ...

  4. Asp&period;Net WebApi核心对象解析(一)

    生活需要自己慢慢去体验和思考,对于知识也是如此.匆匆忙忙的生活,让人不知道自己一天到晚都在干些什么,似乎每天都在忙,但又好似不知道自己到底在忙些什么.不过也无所谓,只要我们知道最后想要什么就行.不管怎 ...

  5. Asp&period;Net WebAPI核心对象解析(三)

    对于.NET的分布式应用开发,可以供我们选择的技术和框架比较多,例如webservice,.net remoting,MSMQ,WCF等等技术.对于这些技术很多人都不会陌生,即时没有深入的了解,但是肯 ...

  6. asp&period;net core 核心对象解析

    首先声明这篇文章的所有内容均来自https://www.cnblogs.com/artech/p/inside-asp-net-core-framework.html ----感谢大内老A(artec ...

  7. ASP&period;NET Ajax核心对象

    本章学习目标 主要掌握AJAX的基本概念和实现机制,学习并创建XMLHttpRequest对象,使用XMLHttpRequestObject对象获取服务器端的数据 主要内容如下,请点击ASP.NET ...

  8. Asp&period;Net的核心对象

    原文地址:http://www.cnblogs.com/fish-li/archive/2011/08/21/2148640.html 1.HttpRuntime 对象在处理Http请求的asp.ne ...

  9. ASP&period;NET Core的路由&lbrack;2&rsqb;:路由系统的核心对象&mdash&semi;&mdash&semi;Router

    ASP.NET Core应用中的路由机制实现在RouterMiddleware中间件中,它的目的在于通过路由解析为请求找到一个匹配的处理器,同时将请求携带的数据以路由参数的形式解析出来供后续请求处理流 ...

随机推荐

  1. Android开发学习之路-Android Studio真神器!

    放假之后电脑配置升级就开始用Android Studio(下面简称AS)了,那个酸爽真的不是一般的啊,这里开一篇博客来记录下AS里面各种酷炫的功能,有更好玩的,大家不要吝啬,评论告诉我吧! 最近And ...

  2. Solr中初学Demo

    import java.util.Collection; import java.util.Date; import org.apache.solr.client.solrj.SolrQuery; i ...

  3. asp&period;net 2&period;0中新增的web&period;config的默认namespace功能 &lpar;转&rpar;

    看上去这个题目比较长,但实际上,我在看资料时发现,这就是说,在asp.net 2.0中,只需要在web.config里定义你要用的那些namespace,则在aspx页面中就不需要再象1.1那样,用 ...

  4. Codeforces Round &num;345 &lpar;Div&period; 1&rpar; B&period; Image Preview

    Vasya's telephone contains n photos. Photo number 1 is currently opened on the phone. It is allowed ...

  5. Python教程:连接数据库,对数据进行增删改查操作

    各位志同道合的同仁可以点击上方关注↑↑↑↑↑↑ 本教程致力于程序员快速掌握Python语言编程. 本文章内容是基于上次课程Python教程:操作数据库,MySql的安装详解 和python基础知识之上 ...

  6. XML初学笔记

    一.基本概要: XML,全称是eXtensible Markup Language,可扩展的标记语言,是Web服务的基础之一,使用XML,用户可以定义自己需要的标记.而用户创建的标记可以使用文档类型定 ...

  7. Qt5该插件机制(2)--QxxxFactory类和QFactoryLoader类别

    <<<<<<<<<<<<<<<<<<<<<<<<< ...

  8. 爬虫(heritrix框架)

    Heritrix 下载 目前 Heritrix 的最新版本是 1.14.4(2010-5-10 发布),您可以从 SourceForge(http://sourceforge.net/projects ...

  9. iterm2 快捷键大全 Mac item2常用快捷键

    整理使用 iTerm 2 过程中得常用快捷键,Mac 原来自带的终端工具 Terminal 不好用是出了名的,虽然最近几个版本苹果稍微做了些优化,功能上,可用性方面增强不少,无奈有个更好用的 Iter ...

  10. 大数据应用日志采集之Scribe演示实例完全解析

    大数据应用日志采集之Scribe演示实例完全解析 引子: Scribe是Facebook开源的日志收集系统,在Facebook内部已经得到大量的应用.它能够从各种日志源上收集日志,存储到一个*存储系 ...