配色: 字号:
Asp.Net WebAPI核心对象解析
2017-01-14 | 阅:  转:  |  分享 
  
Asp.NetWebAPI核心对象解析



对于.NET的分布式应用开发,可以供我们选择的技术和框架比较多,例如webservice,.netremoting,MSMQ,WCF等等技术。对于这些技术很多人都不会陌生,即时没有深入的了解,但是肯定听说过,每种技术都各有优势和适用范围,没有绝对的好坏,只有相对的合适程度。不过可惜了,今天我们讲解的主题不是这几种技术,今天主要讲解的是ASP.NETWebAPI。



对于ASP.NETWebAPI的优势和特点,在这里就不讲了,需要用到的自然就会选择,也不需要我浪费篇幅去讲解这些,这篇博文主要讲解ASP.NETWebAPI中的HTTP消息的结构和处理消息的核心对象。



一.WebAPI的HTTP概述:



有关HTTP协议的相关内容在这里就不做介绍,在笔者前面的博文中已经做过介绍,现在提供一下地址,因为过多的赘述就是浪费时间,我就姑且看这篇博文的读者已经对HTTP协议和WebAPI都有所了解。



1.在.NET4.5之前的版本中,处理HTTP的核心对象:



(1).在客户端:System.Net.HttpWebRequest用于初始化HTTP请求,处理相关的响应;System.Net.HttpWebResponse处理HTTP响应头和数据读取的检索。



(2).在服务器端:System.Web.HttpContext,System.Web.HttpRequest,System.Web.HttpResponse类用在ASP.NET上下文中,代表单个请求和响应。System.Net.HttpListenerContext类,提供对HTTP请求和响应对象的访问。



2.在.NET4.5版本中,处理HTTP的核心对象:



(1).在客户端和服务器端使用同样的类。(HttpRequestMessage和HttpResponseMessage对象中不包含上下文消息,所以可以在服务器和客户端共用。)



(2).由于在.NET4.5中引入了TAP(异步任务模型),所以在新的HTTP模型中,处理HTTP请求的方法可以使用async和awit实现异步编程。(可以简单高效的实现异步编程)



我们对于新旧的HTTP编程模型时,会很容易的发现在新版本的HTTP模型中,无论是编程的难度和代码编写的精简度,已经执行的效率都是很高的。在对于Web项目的开发中,我们对HTTP知识的了解是必要的,对于ASP.NET的HTTP处理的原理在这里就不做具体的介绍,网上也有比较多的文章可供阅读和了解。



对于ASP.NET的HTTP处理方式的了解,是我在开发微信公众平台时进一步学习的,微信公众平台提供了对外访问的接口,我们的程序和服务器对微信服务器的接口进行请求访问,微信服务器获取HTTP请求后,返回处理结果,本地服务器获取返回结果。这样一个请求-响应模式,组成一个会话。对于微信公众平台的开发对于很多刚学习.NET的人来说有些高大(当然这是相对而言),即时开发过很多次这个类别的程序的人(调用第三方接口的开发)也不一定可以很清晰的知道这个其中的原理,笔者觉得对于这样的第三方平台的开发,其主要的核心部分就是对于HTTP协议的处理,建立请求、获取响应消息和解析消息这三大步骤,返回的消息内容一般为json或者xml,获取响应消息后,主要是对消息内容的反序列化,获得消息的实体信息,进而在程序中进一步处理。



在WeAPI中消息的产生和解析,以及消息的格式都是可以动态的创建和协商,下面我们进一步的了解实现这一过程的核心对象。



二.WebAPI的HTTP消息解析:



HTTP协议的工作方式是在客户端和服务器之间交换请求和响应消息,那么这也就可以说明HTTP的核心就是消息,对于“消息”的了解,我们只要知道消息分为“消息头部”和“消息内容”,我们接下来的对新HTTP编程模型的介绍的主体就是“消息头部”和“消息内容”。



在命名空间System.Net.Http中,具有两个核心对象:HttpRequestMessage和HttpResponseMessage。两个对象的结构如下图:







以上主要讲解了HttpRequestMessage对象和HttpResponseMessage对象包含的主要内容,请求和响应消息都可以包含一个可选的消息正文,两中消息类型以及消息内容,都可以使用响应的标头。接下来具体了解一些消息的结构。



1.HttpRequestMessage对象解析:



(1).HttpRequestMessage主要属性和方法概述:



名称 说明

Version 获取或设置HTTP消息版本

Content 获取或设置HTTP消息的内容

Method 获取或设置HTTP请求信息使用的HTTP方法

RequestUri 获取或设置HTTP请求的Uri

Headers 获取HTTP请求标头的集合

Properties 获取HTTP请求的属性集

ToString 返回表示当前对象的字符串

该对象主要用于表示HTTP请求消息。对于该对象的这些属性和方法,大部分应该都不会陌生,因为一个HTTP消息中主要包含头部、消息内容等等,在这里主要介绍一个属性Properties,该属性并不属于任何标准的HTTP消息,当消息传输时,不会保留该属性。



(2).Properties属性解析:



复制代码

[__DynamicallyInvokable]

publicIDictionaryProperties

{

[__DynamicallyInvokable]

get

{

if(this.properties==null)

{

this.properties=newDictionary();

}

returnthis.properties;

}

}

复制代码

有以上的代码可以很明显的看出该属性只有一个只读属性,并返回一个IDictionary。当消息在服务器或者客户端本地进行处理时,该属性用于保存附加的消息信息。该属性只是一个通用的容器,保存本地消息属性。(与接受消息的连接相关的客户端认证;将消息与配置路由进行匹配,得到的路由数据)



2.HttpResponseMessage对象解析:



(1).HttpRequestMessage主要属性和方法概述:



名称 说明

EnsureSuccessStatusCode 如果HTTP响应的IsSuccessStatusCode属性为false,将引发异常

StatusCode 获取或设置HTTP响应的状态代码

ReasonPhrase 获取或设置服务器与状态代码通常一起发送的原因短语

RequestMessage 获取或设置导致此响应消息的请求消息

IsSuccessStatusCode 获取一个值,该值指示HTTP响应是否成功

对于该对象的一些属性没有列举,因为在HttpRequestMessage对象已经介绍,如:Version、Content、Headers等,该对象主要用于表示HTTP响应消息。在这里主要介绍StatusCode属性。



(2).StatusCode属性:



复制代码

[__DynamicallyInvokable]

publicHttpStatusCodeStatusCode

{

[__DynamicallyInvokable,TargetedPatchingOptOut("PerformancecriticaltoinlinethistypeofmethodacrossNGenimageboundaries")]

get

{

returnthis.statusCode;

}

[__DynamicallyInvokable]

set

{

if((value<((HttpStatusCode)0))||(value>((HttpStatusCode)0x3e7)))

{

thrownewArgumentOutOfRangeException("value");

}

this.CheckDisposed();

this.statusCode=value;

}

}

复制代码

StatusCode属性为枚举属性,该属性可读可写,对于状态码这个概念,很多人都是比较了解的,在HTTP协议中,状态码主要是表示在消息的请求在服务器中处理的结果,状态有2XX,3XX,4XX,5XX等等,具体表示的意义就不再描述。



3.HTTP模型消息标头解析:



在HTTP中,请求和响应消息,以及消息内容自身,都可以使用称为标头的额外字段,包含更多的信息。



(1).标头分类:



标头名称 描述 HTTP模型标头容器类

User-Agent 为请求提供扩展信息,描述产生这个请求的应用程序 HttpRequestHeaders

Server 为响应提供关于源服务器软件的扩展信息 HttpResponseHeaders

Content-Type 定义请求或响应有效载荷正文中,资源表示使用的媒体类型 HttpContentHeaders

(2).HttpHeaders抽象类分析:



名称 描述

Add 添加指定的标头及其值到HttpHeaders集合中。

TryAddWithoutValidation 返回一个值,该值指示指定标头及其值是否已添加到HttpHeaders集合,而未验证所提供的信息。

Clear 从HttpHeaders集合中移除所有标头。

Remove 从HttpHeaders集合中移除指定的标头。

GetValues 返回存储在HttpHeaders集合中所有指定标头的标头值。

Contains 如果指定标头存在于HttpHeaders集合则返回。

ToString 返回表示当前HttpHeaders对象的字符串。

HttpHeaders是一个抽象类,HttpRequestHeaders、HttpResponseHeaders、HttpContentHeaders三个类继承了该类。接下来我们来了解一下Add()方法:



复制代码

[__DynamicallyInvokable]

publicvoidAdd(stringname,stringvalue)

{

HeaderStoreItemInfoinfo;

boolflag;

this.CheckHeaderName(name);

this.PrepareHeaderInfoForAdd(name,outinfo,outflag);

this.ParseAndAddValue(name,info,value);

if(flag&&(info.ParsedValue!=null))

{

this.AddHeaderToStore(name,info);

}

}

复制代码

Add()方法具有两个重载版本,该方法可以向容器添加标头,如果要添加的标头有标准名,在添加之前标头值会进行验证。Add方法还会验证标头是否可以有多个值。



4.HTTP消息内容解析:



在.NET4.5版本的HTTP模型中,HTTP消息的正文由抽象基类HttpContent表示,HttpResponseMessage和HttpRequestMessage对象都包含一个HttpContent类型的Content属性。



(1).HttpContent主要属性和方法:



名称 描述

ReadAsByteArrayAsync 以异步操作将HTTP内容写入字节数组。

SerializeToStreamAsync 以异步操作将HTTP内容序列化到流。

CopyToAsync 以异步操作将HTTP内容写入流。

LoadIntoBufferAsync 以异步操作将HTTP内容序列化到内存缓冲区。

CreateContentReadStreamAsync 以异步操作将HTTP内容写入内存流。

TryComputeLength 确定HTTP内容是否具备有效的字节长度。

Headers 根据RFC2616中的定义,获取内容标头。

(2).CopyToAsync()方法解析:



复制代码

[__DynamicallyInvokable]

publicTaskCopyToAsync(Streamstream,TransportContextcontext)

{

Actioncontinuation=null;

this.CheckDisposed();

if(stream==null)

{

thrownewArgumentNullException("stream");

}

TaskCompletionSourcetcs=newTaskCompletionSource();

try

{

Tasktask=null;

if(this.IsBuffered)

{

task=Task.Factory.FromAsync(newFunc
AsyncCallback,object,IAsyncResult>(stream.BeginWrite),newAction(stream.www.baiyuewang.netEndWrite),

this.bufferedContent.GetBuffer(),0,(int)this.bufferedContent.Length,null);

}

else

{

task=this.SerializeToStreamAsync(stream,context);

this.CheckTaskNotNull(task);

}

if(continuation==null)

{

continuation=delegate(TaskcopyTask){

if(copyTask.IsFaulted)

{

tcs.TrySetException(GetStreamCopyException(copyTask.Exception.GetBaseException()));

}

elseif(copyTask.IsCanceled)

{

tcs.TrySetCanceled();

}

else

{

tcs.TrySetResult(null);

}

};

}

task.ContinueWithStandard(continuation);

}

catch(IOExceptionexception)

{

tcs.TrySetException(GetStreamCopyException(exception));

}

catch(ObjectDisposedExceptionexception2)

{

tcs.TrySetException(GetStreamCopyException(exception2));

}

returntcs.Task;

}

复制代码

在使用消息内容时,需要使用HtppContent的方法或者扩展方法。在HttpContent中利用CopyToAsync()方法以推送方式访问原始的消息内容,由方法代码可以看出,该方法接受两个参数,一个是流对象,一个是有关传输的信息(例如,通道绑定),此参数可以为null。该方法可以把消息内容写入到这个流中。



在该方法的实现代码中创建了一个TaskCompletionSource的泛型对象,该对象表示未绑定到委托的Task的制造者方,并通过Task属性提供对使用者方的访问。SerializeToStreamAsync方法将传入的流对象序列化,该方法为异步方法。



我们需要注意的几点,主要为委托的创建和使用,在C#中,尽量使用有.NET提供的委托类,不要自己去创建。还有一点就是在程序中对异常的处理方式,异常的捕获具有层次性,并且调用了自定义的一个异常处理方法TrySetException。



(2).ReadAsStreamAsync()方法解析:



在获取原始消息内容时,除了调用上面介绍的方法外,还可以调用ReadAsStreamAsync()方法以拉取的方式访问原始的消息内容。



在HttpContent中包含有另外两个类似的方法,ReadAsStringAsync()和ReadAsByteArrayAsync()异步的提供消息内容的缓冲副本,ReadAsByteArrayAsync()返回原始的字节内容,ReadAsStringAsync()将内容解码为字符串返回。



三.DotNet中新旧HTTP模型分析:



1..NET4.5之前版本创建HTTPPOST请求实例:



复制代码

publicstaticstringHttpPost(stringpostUrl,stringpostData)

{

if(string.IsNullOrEmpty(postUrl))

thrownewArgumentNullException(postUrl);

if(string.IsNullOrwww.tt951.comEmpty(postData))

thrownewArgumentNullException(postData);

varrequest=WebRequest.Create(postUrl)asHttpWebRequest;

if(request==null)

thrownewArgumentNullException("postUrl");

try

{

varcookieContainer=newCookieContainer();

request.CookieContainer=cookieContainer;

request.AllowAutoRedirect=true;

request.Method="POST";

request.ContentType="application/x-www-form-urlencoded";

vardata=Encoding.UTF8.GetBytes(postData);

request.ContentLength=data.Length;

varoutstream=request.GetRequestStream();

outstream.Write(data,0,data.Length);

outstream.Close();

//发送请求并获取相应回应数据,获取对应HTTP请求的响应

varresponse=request.GetResponse()asHttpWebResponse;

if(response!=null)

{

varinstream=response.GetResponseStream();

varcontent=string.Empty;

if(instream==null)

{

returncontent;

}

using(varsr=newStreamReader(instream,Encoding.UTF8))

{

content=sr.ReadToEnd();

}

returncontent;

}

}

catch(ArgumentExceptionarex)

{

throwarex;

}

catch(IOExceptionioex)

{

throwioex;

}

returnnull;

}

复制代码

2..NET4.5版本创建HTTPPOST请求实例:



复制代码

asyncstaticvoidgetResponse(stringurl)

{

using(HttpClientclient=newHttpClient())

{

using(HttpResponseMessageresponse=awaitclient.GetAsync(url))

{

using(HttpContentcontent=response.Content)

{

stringmyContent=awaitcontent.ReadAsStringAsync();

}

}

}

}

asyncstaticvoidpostResponse(stringurl)

{

while(true)

{

IEnumerable>queries=newList>()

{

newKeyValuePair("test","test")

};

HttpContentq=newFormUrlEncodedContent(queries);

using(HttpClientclient=newHttpClient())

{

using(HttpResponseMessageresponse=awaitclient.PostAsync(url,q))

{

using(HttpContentcontent=response.Content)

{

stringmyContent=awaitcontent.ReadAsStringAsync();



Console.WriteLine(myContent);

}

}

}

}

}

复制代码

四.总结:



以上主要讲解了.NET4.5之前和之后版本对HTTP编程模式的一些内容,两者的主要区别在于.NET4.5版本之前的HTTP编程模型会区分客户端和服务器,两者使用的对象存在不同,实现的原理上虽然存在一定的相似性,但是使用的类却不同。.NET4.5之后的版本中,对象的使用没有客户端和服务器之分,两者可以共用。

献花(0)
+1
(本文系thedust79首藏)