zoukankan      html  css  js  c++  java
  • Asp.Net WebApi核心对象解析(二)

       在接着写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[1]
              {
                (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[1]
            {
              (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路由机制,处理架构,托管方式等等,如有不足和错误之处还望多多指正。(对我来说总算是写完了,写了上篇就得写下篇,实在痛苦)

  • 相关阅读:
    171. Excel Sheet Column Number (Easy)
    349. Intersection of Two Arrays (Easy)
    453. Minimum Moves to Equal Array Elements (Easy)
    657. Judge Route Circle (Easy)
    CSS笔记
    保存页面状态
    UI开发总结
    ubuntu 下配置munin
    反向代理配置
    JavaScript 高级程序设计第二版
  • 原文地址:https://www.cnblogs.com/pengze0902/p/6230105.html
Copyright © 2011-2022 走看看