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

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

一.WebAPI的HTTP概述:

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

http://www.cnblogs.com/pengze0902/p/5976388.html

http://www.cnblogs.com/pengze0902/p/6224792.html

http://www.cnblogs.com/pengze0902/p/6230105.html

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]
public IDictionary<string, object> Properties
{
[__DynamicallyInvokable]
get
{
if (this.properties == null)
{
this.properties = new Dictionary<string, object>();
}
return this.properties;
}
}

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

2.HttpResponseMessage对象解析:

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

名称 说明
EnsureSuccessStatusCode 如果 HTTP 响应的 IsSuccessStatusCode 属性为  false, 将引发异常
StatusCode 获取或设置 HTTP 响应的状态代码
ReasonPhrase 获取或设置服务器与状态代码通常一起发送的原因短语
RequestMessage 获取或设置导致此响应消息的请求消息
IsSuccessStatusCode 获取一个值,该值指示 HTTP 响应是否成功

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

(2).StatusCode属性:

[__DynamicallyInvokable]
public HttpStatusCode StatusCode
{
[__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
get
{
return this.statusCode;
}
[__DynamicallyInvokable]
set
{
if ((value < ((HttpStatusCode) )) || (value > ((HttpStatusCode) 0x3e7)))
{
throw new ArgumentOutOfRangeException("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]
public void Add(string name, string value)
{
HeaderStoreItemInfo info;
bool flag;
this.CheckHeaderName(name);
this.PrepareHeaderInfoForAdd(name, out info, out flag);
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 根据 RFC 2616 中的定义,获取内容标头。

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

[__DynamicallyInvokable]
public Task CopyToAsync(Stream stream, TransportContext context)
{
Action<Task> continuation = null;
this.CheckDisposed();
if (stream == null)
{
throw new ArgumentNullException("stream");
}
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
try
{
Task task = null;
if (this.IsBuffered)
{
task = Task.Factory.FromAsync<byte[], int, int>(new Func<byte[], int, int,
AsyncCallback, object, IAsyncResult>(stream.BeginWrite), new Action<IAsyncResult>(stream.EndWrite),
       this.bufferedContent.GetBuffer(), , (int) this.bufferedContent.Length, null);
}
else
{
task = this.SerializeToStreamAsync(stream, context);
this.CheckTaskNotNull(task);
}
if (continuation == null)
{
continuation = delegate (Task copyTask) {
if (copyTask.IsFaulted)
{
tcs.TrySetException(GetStreamCopyException(copyTask.Exception.GetBaseException()));
}
else if (copyTask.IsCanceled)
{
tcs.TrySetCanceled();
}
else
{
tcs.TrySetResult(null);
}
};
}
task.ContinueWithStandard(continuation);
}
catch (IOException exception)
{
tcs.TrySetException(GetStreamCopyException(exception));
}
catch (ObjectDisposedException exception2)
{
tcs.TrySetException(GetStreamCopyException(exception2));
}
return tcs.Task;
}

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

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

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

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

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

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

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

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

        public static string HttpPost(string postUrl, string postData)
{
if (string.IsNullOrEmpty(postUrl))
throw new ArgumentNullException(postUrl);
if (string.IsNullOrEmpty(postData))
throw new ArgumentNullException(postData);
var request = WebRequest.Create(postUrl) as HttpWebRequest;
if (request == null)
throw new ArgumentNullException("postUrl");
try
{
var cookieContainer = new CookieContainer();
request.CookieContainer = cookieContainer;
request.AllowAutoRedirect = true;
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
var data = Encoding.UTF8.GetBytes(postData);
request.ContentLength = data.Length;
var outstream = request.GetRequestStream();
outstream.Write(data, , data.Length);
outstream.Close();
//发送请求并获取相应回应数据,获取对应HTTP请求的响应
var response = request.GetResponse() as HttpWebResponse;
if (response != null)
{
var instream = response.GetResponseStream();
var content = string.Empty;
if (instream == null)
{
return content;
}
using (var sr = new StreamReader(instream, Encoding.UTF8))
{
content = sr.ReadToEnd();
}
return content;
}
}
catch (ArgumentException arex)
{
throw arex;
}
catch (IOException ioex)
{
throw ioex;
}
return null;
}

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

async static void getResponse(string url)
{
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage response = await client.GetAsync(url))
{
using (HttpContent content = response.Content)
{
string myContent = await content.ReadAsStringAsync();
}
}
}
}
async static void postResponse(string url)
{
while (true)
{
IEnumerable<KeyValuePair<string, string>> queries = new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string> ("test","test")
};
HttpContent q = new FormUrlEncodedContent(queries);
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage response = await client.PostAsync(url, q))
{
using (HttpContent content = response.Content)
{
string myContent = await content.ReadAsStringAsync(); Console.WriteLine(myContent);
}
}
}
}
}

四.总结:

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

关于ASP.NET WebAPI中HTTP模型的相关思考的更多相关文章

  1. Asp.net Mvc 中的模型绑定

    asp.net mvc中的模型绑定可以在提交http请求的时候,进行数据的映射. 1.没有模型绑定的时候 public ActionResult Example0() { ) { string id ...

  2. ASP.NET Core 中的模型绑定

    微软官方文档:ASP.NET Core 中的模型绑定 Route 是通过MVC Route URL取值. 如:http://localhost:5000/Home/Index/2,id取出的值就会是2 ...

  3. 在asp.net WebAPI 中 使用Forms认证和ModelValidata(模型验证)

    一.Forms认证 1.在webapi项目中启用Forms认证 Why:为什么要在WebAPI中使用Forms认证?因为其它项目使用的是Forms认证. What:什么是Forms认证?它在WebAP ...

  4. [翻译] ASP.NET WebAPI 中的异常处理

    原文链接:https://docs.microsoft.com/en-us/aspnet/web-api/overview/error-handling/exception-handling 本文介绍 ...

  5. ASP.NET WebApi 中使用swagger 构建在线帮助文档

    1 在Visual Studio 中创建一个Asp.NET  WebApi 项目,项目名:Com.App.SysApi(本例创建的是 .net 4.5 框架程序) 2  打开Nuget 包管理软件,查 ...

  6. 在ASP.NET WebAPI 中使用缓存【Redis】

    初步看了下CacheCow与OutputCache,感觉还是CacheOutput比较符合自己的要求,使用也很简单 PM>Install-Package Strathweb.CacheOutpu ...

  7. ASP.NET MVC中的模型装配 封装方法 非常好用

    下面说一下 我们知道在asp.net mvc中 视图可以绑定一个实体模型 然后我们三层架构中也有一个model模型 但是这两个很多时候却是不一样的对象来的 就拿微软的官方mvc例子来说明 微软的视图实 ...

  8. Asp.Net WebAPI 中Cookie 获取操作方式

    1. /// <summary> /// 获取上下文中的cookie /// </summary> /// <returns></returns> [H ...

  9. Asp.Net WebAPI中Filter过滤器的使用以及执行顺序

    转发自:http://www.cnblogs.com/UliiAn/p/5402146.html 在WEB Api中,引入了面向切面编程(AOP)的思想,在某些特定的位置可以插入特定的Filter进行 ...

随机推荐

  1. jQuery的ready方法实现原理分析

    jQuery中的ready方法实现了当页面加载完成后才执行的效果,但他并不是window.onload或者doucment.onload的封装,而是使用 标准W3C浏览器DOM隐藏api和IE浏览器缺 ...

  2. VBOX安装Centos设置分辨率为1366x768[已解决]

    最近想了解下GTK+,但是对于直接在系统上搭建环境有点心里阴影,怕又把桌面玩挂,所以打算在虚拟机中先试试 本来打算使用Fedora的,怕gnome太吃资源所以下了个xfce的,不过貌似有BUG,无法安 ...

  3. 用lucene.net根据关键字检索本地word文档

    目前在做一个winform小软件,其中有一个功能是能根据关键字检索本地保存的word文档.第一次是用com读取word方式(见上一篇文章),先遍历文件夹下的word文档,读取每个文档时循环关键字查找, ...

  4. String 的intern() 方法说明

    1.说明 Java中string.intern()方法调用会先去字符串常量池中查找相应的字符串,如果字符串不存在,就会在字符串常量池中创建该字符串然后再返回. 2.源码说明 public native ...

  5. QT添加exe文件的图标LOGO

    首先你需要一个ICO文件,这样的一个: 使用百度搜索到的在线工具直接生成一个ICO文件保存到本地就可以了 将这人LOGO.ico文件保存到自己的resource文件夹下 然后在工程中新建一个qrc文件 ...

  6. 你是否应该使用一个Javascript MVC框架?

    你是否应该使用一个Javascript MVC框架?本文摘自smashingmagazine的Journey Through The JavaScript MVC Jungle部分内容,希望对大家有帮 ...

  7. [置顶] 纯手工打造漂亮的垂直时间轴,使用最简单的HTML+CSS+JQUERY完成100个版本更新记录的华丽转身!

    前言 FineUI控件库发展至今已经有 5 个年头,目前论坛注册的QQ会员 5000 多人,捐赠用户 500 多人(捐赠用户转化率达到10%以上,在国内开源领域相信这是一个梦幻数字!也足以证明Fine ...

  8. Binder机制,从Java到C (10. Binder驱动)

    Binder驱动的代码都在kernel里面,这里就简单讲一下里面涉及到的几个东西: 1.MemoryBinder其实本质上就是一中数据传输方式,这种方式是通过binder driver实现的. 我们知 ...

  9. D14

    T1:有点坑的模拟,考心细+代码能力 T2:最小生成树 记得开long long,不然会全WA.. 莫名超时三个点,据学长说可以开并查集优化,叫做按秩排序..=-=现在木有网啊..哪天再看 T3:di ...

  10. 关于AngularJS的分享

    去年下半年在部门内部做的一个关于AngularJS的分享,放在这里,当作一个记录. 点击下载