博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Self Host模式下的ASP. NET Web API是如何进行请求的监听与处理的?
阅读量:6184 次
发布时间:2019-06-21

本文共 18309 字,大约阅读时间需要 61 分钟。

构成ASP.NET Web API核心框架的消息处理管道既不关心请求消息来源于何处,也不需要考虑响应消息归于何方。当我们采用Web Host模式将一个ASP.NET应用作为目标Web API的宿主时,实际上是由ASP.NET管道解决了这两个问题。具体来说,ASP.NET自身的URL路由系统借助于HttpControllerHandler这个自定义的HttpHandler实现了ASP.NET管道和ASP.NET Web API管道之间的“连通”,但是在Self Host寄宿模式下,请求的监听、接收和响应又是如何实现的呢?[本文已经同步到《》]

目录

一、HttpBinding模型
    Binding模型
    HttpBinding
    实例演示:直接利用HttpBinding进行请求的接收和响应
二、HttpSelfHostServer
    HttpSelfHostConfiguration
    HttpSelfHostServer与消息处理管道
    实例演示:创建自定义HttpServer模拟HttpSelfHostServer的工作原理

一、HttpBinding模型

和WCF服务一样,我们可以采用Self Host模式将Web API寄宿于任何一种类型的托管应用程序下,宿主可以是一个Windows Form应用、WPF应用、控制台应用以及Windows Service。Self Host模式下的WCF和ASP.NET Web API不仅外在表现形式极为相似,其实在内部实现原理上也是一致的。

Binding模型

对于WCF具有基本了解的读者应该都知道,它是一个基于消息的分布式通信框架,消息交换借助于客户端和服务端对等的终结点(Endpoint)来完成,而终结点由经典的ABC(Address、Binding、Contract)三元素组成。WCF同样具有一个处理消息的管道,这个管道是一组Channel的有序组合,WCF下的Channel相对于ASP.NET Web API下的HttpMessageHandler。

WCF的消息处理管道的缔造者是作为终结点三要素之一的Binding。Binding不仅仅为服务端创建用于接收请求回复响应的管道,同时也为客户端创建发送请求接收响应的管道。Binding模型本身也相对比较复杂,所以我们不可能对其进行详细讨论。如果读者对此比较感兴趣,可以参阅《》。由于ASP.NET Web API只是利用HttpBinding创建服务端消息处理管道,所以我们只讨论Binding的服务端模型。

从结构上讲,一个Binding是若干BindingElement对象的有序组合。对于最终创建的消息处理管道来说,每个Channel都对应着一个BindingElement。BindingElement并非直接创建对应的Channel,由它直接创建的实际上是一个名为ChannelListener的对象,Channel由ChannelListener创建。右图基本揭示了Binding的服务端模型。

顾名思义,ChannelListener用于请求的监听。当Binding对象开启(调用其Open方法)时,每个BindingElement会创建各自的ChannelListener。这些ChannelListener按照对应BindingElement的顺序连接成串,位于底部(面向传输层)的ChannelListener被绑定到某个端口进行请求的监听。一旦探测到抵达的请求,它会利用由所有ChannelListener创建的Channel组成的管道来接收并处理该请求。对于最终需要返回的响应消息,则按照从上到下的顺序被这个管道进行处理并最终返回给客户端。

对于这个由Channel组成消息处理管道来说,有两种类型的Channel是必不可少的。一种是面向传输层用于发送和接收消息的TransportChannel,另一种被称为MessageEncodingChannel则负责对接收的消息实施解码并对发送的消息实施编码。TransportChannel由TransportChannelListener创建,而后者由TransportBindingElement创建。与之类似,MessageEncodingBindingElement是MessageEncodingChannelListener的创建者,而后者又是MessageEncodingChannel的创建者。

如果采用Self Host寄宿模式,请求的监听是由一个类型为HttpBinding的Binding对象创建的ChannelListener管道来完成的,由它创建的管道实现了针对请求的接收和针对响应的回复。HttpBinding类型定义在“System.Web.Http.SelfHost.Channels”命名空间下,我们接下来对它进行详细讲述。

HttpBinding

Binding存在的目的在于创建用于处理和传输消息的信道栈,组成信道栈的每一个Channel均对应着一个BindingElement,所以Binding本身处理消息的能力由其BindingElement的组成来决定,我们可以通过分析BindingElement的组成来了解消息最终是如何处理的。现在我们就来讨论一下ASP.NET Web API在Self Host模式下使用的HttpBinding由哪些BindingElement构成。

如左图所示,HttpBinding仅仅由两种必需的BindingElement构成,TransportBindingElement的类型决定于最终采用的传输协议。如果采用单纯的HTTP协议,采用的TransportBindingElement是一个HttpTransportBindingElement对象。在采用HTTPS协议的情况下,TransportBindingElement的类型是HttpsTransportBindingElement。

我们现在着重来分析与消息编码/解码相关的BindingElement,从图3-11可以看出这是一个HttpMessageEncodingBindingElement对象(HttpMessageEncodingBindingElement是一个定义在程序集“System.Web.Http.SelfHost.dll”中的内部类型),它最终会创建一个MessageEncoder对象完成针对消息的编码/解码工作。

ASP.NET Web API分别利用 HttpRequestMessage和HttpResponseMessage对象表示消息处理管道处理的请求和响应,而WCF消息处理管道的请求和响应均是一个Message对象(Message是定义在命名空间“System.ServiceModel.Channels”下的一个抽象类型)。经过HttpMessageEncoder解码后的Message对象会转成一个HttpRequestMessage对象并传入ASP.NET Web API消息处理管道进行处理,由此管道返回的HttpResponseMessage对象需要转换成一个Message对象并由HttpMessageEncoder根据需求进行解码。

这个具体的消息实际上是一个HttpMessage对象,HttpMessage继承自抽象类Message,它是一个定义在程序集“System.Web.Http.SelfHost.dll”中的内部类型。如下面的代码片断所示,HttpMessage实际上是对一个HttpRequestMessage或者HttpResponseMessage对象的封装,两个方法GetHttpRequestMessage和GetHttpResponseMessage分别用于提取被封装的HttpRequestMessage和HttpResponseMessage对象。

1: internal sealed class HttpMessage : Message
2: {
3:     //其他成员
4:     public HttpMessage(HttpRequestMessage request);
5:     public HttpMessage(HttpResponseMessage response);
6:
7:     public HttpRequestMessage  GetHttpRequestMessage(bool extract);
8:     public HttpResponseMessage GetHttpResponseMessage(bool extract);
9: }

这两个方法均具有一个布尔类型的参数extract,它表示是否“抽取”被封装的HttpRequestMessage/HttpResponseMessage对象。如果指定的参数值为True,方法执行之后被封装的HttpRequestMessage/HttpResponseMessage对象会从HttpMessage对象中抽取出来,所以再次调用它们会返回Null。

再次将我们的关注点拉回到由HttpBinding创建的消息处理管道上。当我们开启HttpBinding后,它利用创建的ChannelListener管道监听请求。一旦探测到抵达的请求后,基于HTTP/HTTPS协议的TransportChannel会负责接收请求。接收的二进制数据会由MessageEncoder解码后生成一个HttpRequestMessage对象,该对象进而被封装成一个HttpMessage对象,传入消息处理管道的HttpRequestMessage是直接通过调用GetHttpRequestMessage方法从该HttpMessage对象中提取的。

当ASP.NET Web API消息处理管道完成了请求的处理并最终输出一个HttpResponseMessage对象后,该对象同样先被封装成一个HttpMessage对象。在通过传输层将响应返回给客户端之前,需要利用MessageEncoder对其进行编码,而解码的内容实际上就是调用GetHttpResponseMessage方法提取的HttpResponseMessage对象。

实例演示:直接利用HttpBinding进行请求的接收和响应

当我们采用Self Host寄宿模式将一个非Web应用程序作为目标Web API的宿主时,最终网络监听任务实际上是由HttpBinding创建的ChannelListener管道来完成的,而ChannelListener管道创建的消息处理管道最终实现了对请求的接收和对响应的发送。为了让读者对此具有深刻的认识,我们通过一个简单的实例来演示如何直接使用HttpBinding实现对请求的监听、接收和响应。

我们创建一个空的控制台程序作为监听服务器,它相当于Self Host寄宿模式下的宿主程序。如下面的代码片断所示,我们创建了一个HttpBinding,并指定监听地址("http://127.0.0.1:3721")调用其BuildChannelListener<IReplyChannel>方法创建了一个ChannelListener管道(返回的是组成管道的第一个ChannelListener对象)。在调用Open方法开启该ChannelListener管道之后,我们调用其AcceptChannel方法创建了消息处理管道,返回的是组成管道的第一个Channel对象。在Open方法将其开启后,我们在一个While循环中调用Channel对象的ReceiveRequest方法进行请求的监听和接收。

1: class Program
2: {
3:     static void Main(string[] args)
4:     {
5:         Uri listenUri = new Uri("http://127.0.0.1:3721");
6:         Binding binding = new HttpBinding();
7: 
8:         //创建、开启信道监听器
9:         IChannelListener
channelListener = binding.BuildChannelListener
(listenUri);
10:         channelListener.Open();
11: 
12:         //创建、开启回复信道
13:         IReplyChannel channel = channelListener.AcceptChannel(TimeSpan.MaxValue);
14:         channel.Open();
15: 
16:         //开始监听
17:         while (true)
18:         {
19:             //接收输出请求消息
20:             RequestContext requestContext = channel.ReceiveRequest(TimeSpan.MaxValue);
21:             PrintRequestMessage(requestContext.RequestMessage);
22:             //消息回复
23:             requestContext.Reply(CreateResponseMessage());
24:         }
25:     }
26: }

对于成功接收的消息,我们调用具有如下定义的PrintRequestMessage方法将相关的信息打印在控制台上。通过上面的介绍我们知道这个接收到的消息实际上是一个HttpMessage对象,由于这是一个内部类型,所以我们只能以反射的方式调用其GetHttpRequestMessage方法获取被封装的HttpRequestMessage对象。在得到表示请求的HttpRequestMessage对象之后,我们将请求地址和所有报头输出到控制台上。

1: private static void PrintRequestMessage(Message message)
2: {
3:     MethodInfo method = message.GetType().GetMethod("GetHttpRequestMessage");
4:     HttpRequestMessage request = (HttpRequestMessage)method.Invoke(message, new object[]{
false});
5: 
6:     Console.WriteLine("{0, -15}:{1}", "RequestUri", request.RequestUri);
7:     foreach (var header in request.Headers)
8:     {
9:         Console.WriteLine("{0, -15}:{1}", header.Key, string.Join("," ,header.Value.ToArray()));
10:     }
11: }

在对请求进行处理之后,我们需要创建一个Message对象对该请求予以响应,响应消息的创建是通过CreateResponseMessage方法完成的。如下面的代码片断所示,我们首先创建了一个响应状态为“200, OK”的HttpResponseMessage对象,并将其表示主体内容的Content属性设置为一个ObjectContent<Employee>对象。由于我们采用MediaTypeFormatter类型为JsonMediaTypeFormatter,指定的Employee对象会以JSON格式进行序列化。最终的响应消息依然是一个HttpMessage对象,它是对我们创建的HttpResponseMessage对象的封装。限于“内部类型”的限制,我们也不得不采用反射的方式来创建这么一个HttpMessage对象。

1: private static Message CreateResponseMessage()
2: {
3:     HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
4:     Employee employee = new Employee("001","Zhang San","123456","zhangsan@gmail.com");
5:     response.Content = new ObjectContent
(employee, new JsonMediaTypeFormatter());
6: 
7:     string httpMessageTypeName = "System.Web.Http.SelfHost.Channels.HttpMessage, System.Web.Http.SelfHost";
8:     Type httpMessageType = Type.GetType(httpMessageTypeName);
9:     return (Message)Activator.CreateInstance(httpMessageType, new object[] { response });
10: }
11: 
12: public class Employee
13: {
14:     public string Id { get; set; }
15:     public string Name { get; set; }
16:     public string PhoneNo { get; set; }
17:     public string EmailAddress { get; set; }
18: 
19:     public Employee(string id, string name, string phoneNo, string emailAddress)
20:     {
21:         this.Id              = id;
22:         this.Name            = name;
23:         this.PhoneNo         = phoneNo;
24:         this.EmailAddress    = emailAddress;
25:     }
26: }

当我们直接运行该程序后,实际上就已经启动了针对基地址"http://127.0.0.1:3721"的监听器。现在我们通过浏览器对这个监听器发起请求,为了使请求更像一个针对Web API的调用,我们将请求地址设置为“http://127.0.0.1:3721/employees/001”(看起来好像是获取某个编号为001的员工信息)。如右图所示,通过浏览器发送的请求相关信息会显示在控制台上,而浏览器上也会显示基于JSON格式的员工信息。

二、HttpSelfHostServer

ASP.NET Web API的Self Host寄宿模式是通过一个System.Web.Http.SelfHost.HttpSelfHostServer对象来完成的,那么HttpSelfHostServer与上面介绍的HttpBinding又有何关系呢?HttpSelfHostServer与ASP.NET Web API的消息处理管道又是如何集成的呢?

如下面的代码片断所示,HttpSelfHostServer其实是HttpServer的子类,所以在Self Host模式下HttpSelfHostServer本身就是消息管道的组成部分。换句话说,以HttpSelfHostServer为“龙头”的消息处理管道本身具有请求监听、接收、处理和响应的能力。

1: public sealed class HttpSelfHostServer : HttpServer
2: {
3:     public HttpSelfHostServer(HttpSelfHostConfiguration configuration);
4:     public HttpSelfHostServer(HttpSelfHostConfiguration configuration, HttpMessageHandler dispatcher);
5:
6:     public Task OpenAsync();
7:     public Task CloseAsync();
8:
9:     protected override void Dispose(bool disposing);
10: }

ASP.NET Web API的消息处理管道的配置利用通过HttpServer的Configuration属性表示的HttpConfiguration对象来完成,对于HttpSelfHostServer来说,它的Configuration属性返回一个HttpSelfHostConfiguration对象(HttpSelfHostConfiguration类型定义在 “System.Web.Http.SelfHost” 命名空间下)。如上面的代码片断所示,当我们调用构造函数创建一个HttpSelfHostServer对象时,需要通过参数指定此HttpSelfHostConfiguration。

HttpSelfHostConfiguration

如下面的代码片断所示,HttpSelfHostConfiguration直接继承自HttpConfiguration。我们在创建一个HttpSelfHostConfiguration对象的时候需要指定一个Uri对象作为监听基地址,这个地址通过只读属性BaseAddress返回。

1: public class HttpSelfHostConfiguration : HttpConfiguration
2: {
3:     public HttpSelfHostConfiguration(Uri baseAddress);
4:
5:     public Uri                         BaseAddress { get; }
6:     public HostNameComparisonMode      HostNameComparisonMode { get; set; }
7:     public TransferMode                TransferMode { get; set; }
8: 
9:     public int      MaxBufferSize { get; set; }
10:     public int      MaxConcurrentRequests { get; set; }
11:     public long     MaxReceivedMessageSize { get; set; }
12: 
13:     public TimeSpan ReceiveTimeout { get; set; }
14:     public TimeSpan SendTimeout { get; set; }
15: 
16:     public HttpClientCredentialType      ClientCredentialType { get; set; }
17:     public UserNamePasswordValidator     UserNamePasswordValidator { get; set; }
18:     public X509CertificateValidator      X509CertificateValidator { get; set; }
19: }

由于Self Host寄宿模式下请求的监听、接收和响应基本上全部是通过HttpBinding实现的,所以定义在HttpSelfHostConfiguration中的众多属性实际上基本都用于对创建的HttpBinding进行配置。从如下给出的代码片断可以看出HttpBinding类型与HttpSelfHostConfiguration具有类似的属性定义。

1: public abstract class Binding : IDefaultCommunicationTimeouts
2: {
3:     //其他成员
4:     public TimeSpan ReceiveTimeout { get;  set; }
5:     public TimeSpan SendTimeout { get;  set; }
6: }
7: 
8: public class HttpBinding : Binding, IBindingRuntimePreferences
9: {
10:     //其他成员
11:     public HostNameComparisonMode HostNameComparisonMode { get; set; }
12:     public TransferMode TransferMode { get; set; }
13: 
14:     public long     MaxBufferPoolSize { get; set; }
15:     public int      MaxBufferSize { get; set; }
16:     public long     MaxReceivedMessageSize { get; set; }
17: 
18:     public HttpBindingSecurity Security { get; set; }
19: }

由于Binding在WCF是一个核心组件,其设计本身相对复杂,要深入了解定义在HttpBinding中的这些属性需要相关的背景知识。篇幅所限,我们不能因为这些属性将Binding相关的内容全部搬过来,所以在这里我们仅仅通过下表对它们进行概括性的介绍。

属性

描述

HostNameComparisonMode

如果请求URL没有指定服务器的IP地址而是主机名称,当从URL提取主机名称后会按照相应的比较模式来最终确定匹配的主机名。该属性的类型为System.ServiceModel.HostNameComparisonMode枚举,用以确定主机名比较模式。

TransferMode

MaxBufferSize

消息传输具有Streamed和Buffered两种模式,前者以流的形式进行消息传输,后者则将整个消息的内容先保存于内存缓冲区后一并传输。该属性类型为System.ServiceModel.TransferMode枚举,用以控制针对请求消息和响应消息的传输模式。在默认情况下,请求消息和响应消息均以Buffered模式进行传输。MaxBufferSize属性表示在采用Buffered模式下消息最大缓冲大小,默认值为65536(0x10000)。

MaxConcurrentRequests

MaxReceivedMessageSize

这两个是针对请求的限制,分别表示运行的最大并发访问量和请求消息允许的最大尺寸。两者的默认值分别为100和65536(0x10000)。值得一提的是MaxConcurrentRequests针对最大并发请求的限制是针对单个处理器设定的,对于多处理器或者多核处理来说,应该乘以处理器的数量。

ReceiveTimeout

SendTimeout

这两个属性分别表示接收请求消息和发送响应消息的超时时限,默认值分别为10分钟和1分钟。

ClientCredentialType

UserNamePasswordValidator
X509CertificateValidator

这是三个与安全认证相关的属性。ClientCredentialType表示客户端采用的用户凭证类型,而UserNamePasswordValidator和X509CertificateValidator属性值分别在用户凭证为“用户名+密码”和“X.509证书”的情况下实施认证。

HttpSelfHostServer与消息处理管道

在采用Self Host模式寄宿Web API时,我们会根据指定的监听基地址创建一个HttpSelfHostConfiguration对象,然后据此创建HttpSelfHostServer。当我们调用方法OpenAsync开启它时,HttpSelfHostServer会创建一个HttpBinding对象,并根据指定的HttpSelfHostConfiguration对其作相应的设置。随后HttpBinding会针对指定的监听地址创建一个ChannelListener管道,并调用其BeginOpen方法以异步的方式开启。除了OpenAsync方法外,HttpSelfHostServer还定义了一个CloseAsync方法使我们可以以异步的方式关闭已经开启的ChannelListener管道。

一旦ChannelListener管道被成功开启后,它便绑定到指定的监听地址进行请求的监听。当抵达的请求被探测到,它会利用创建的消息处理管道来接收该请求。通过上面对HttpBinding的介绍我们知道,接收到的二进制数据经过解码之后会生成一个HttpMessage对象,后者是对一个HttpRequestMessage的封装。

接下来HttpSelfHostServer从生成的HttpMessage中提取被封装的HttpRequestMessage对象,并直接分发给后续的HttpMessageHandler作进一步处理。对于最终返回的表示响应的HttpResponseMessage对象,HttpSelfHostServer将其封装成一个HttpMessage对象并利用消息处理管道返回给客户端。在通过传输层发送响应消息之前,HttpMessage会先编码。通过上面的介绍我们知道整个编码工作完全是针对被HttpMessage封装的HttpResponseMessage对象进行的,在HttpResponseMessage中保存的响应内容就是客户端接收到的内容。左图基本揭示了Self Host寄宿模式下整个消息处理管道的结构。

 

实例演示:创建自定义HttpServer模拟HttpSelfHostServer的工作原理

通过上面的介绍,我想读者朋友们应该对Self Host模式下消息处理管道如何进行请求的监听、接收、处理和响应已经有了全面的了解。如果我们能够创建一个自定义的HttpServer来模拟HttpSelfHostServer的工作原理,我想大家对此的印象一定更加深刻。在本章内容即将完结之前,我们就来完成这么一个演示实例。

我们通过继承HttpServer创建如下一个用于模拟HttpSelfHostServer的MyHttpSelfHostServer类型。两者对于请求的监听、接收和响应的实现原理是一致的,不同之处在于HttpSelfHostServer基本采用异步的操作方式,MyHttpSelfHostServer采用同步编程方式。

1: public class MyHttpSelfHostServer: HttpServer
2: {
3:     public Uri BaseAddress { get; private set; }
4:     public IChannelListener
ChannelListener { get; private set; }
5: 
6:     public MyHttpSelfHostServer(HttpConfiguration configuration, Uri baseAddress)
7:         : base(configuration)
8:     {
9:         this.BaseAddress = baseAddress;
10:     }
11: 
12:     public void Open()
13:     {
14:         HttpBinding binding = new HttpBinding();
15:         this.ChannelListener = binding.BuildChannelListener
(this.BaseAddress);
16:         this.ChannelListener.Open();
17: 
18:         IReplyChannel channnel = this.ChannelListener.AcceptChannel();
19:         channnel.Open();
20: 
21:         while (true)
22:         {
23:             RequestContext requestContext = channnel.ReceiveRequest(TimeSpan.MaxValue);
24:             Message message = requestContext.RequestMessage;
25:             MethodInfo method = message.GetType().GetMethod("GetHttpRequestMessage");
26:             HttpRequestMessage request = (HttpRequestMessage)method.Invoke(message, new object[] {
true});
27:             Task
processResponse = base.SendAsync(request, new CancellationTokenSource().Token);
28:             processResponse.ContinueWith(task =>
29:                 {
30:                     string httpMessageTypeName = "System.Web.Http.SelfHost.Channels.HttpMessage, System.Web.Http.SelfHost";
31:                     Type httpMessageType = Type.GetType(httpMessageTypeName);
32:                     Message reply = (Message)Activator.CreateInstance(httpMessageType, new object[] { task.Result });
33:                     requestContext.Reply(reply);
34:                 });
35:         }
36:     }
37: 
38:     public void Close()
39:     {
40:         if (null != this.ChannelListener && this.ChannelListener.State == CommunicationState.Opened)
41:         {
42:             this.ChannelListener.Close();
43:         }
44:     }
45: }

MyHttpSelfHostServer的只读属性BaseAddress表示监听基地址,该属性直接在构造函数中指定。与HttpSelfHostServer不同的是,用于创建MyHttpSelfHostServer提供的配置对象是一个HttpConfiguration对象而不再是HttpSelfHostConfiguration。

在Open方法中,我们根据提供的监听基地址利用HttpBinding对象创建一个ChannelListener对象,MyHttpSelfHostServer的只读属性ChannelListener引用的也正是这个对象。在开启该ChannelListener之后,我们调用其AccpetChannel方法创建信道栈,最终返回位于栈顶的Channel。在该Channel开启的情况下,我们在一个“永不终止”的While循环中调用其ReceiveRequest方法进行请求的监听。

当信道栈成功接收请求消息后(这是一个HttpMessage对象),我们从中提取出被封装的HttpRequestMessage对象,并将其作为参数调用SendAsync方法,表示请求的HttpReuqestMessage自此进入了消息处理管道这个流水车间。

调用SendAsync方法返回的是一个Task<HttpResponseMessage>对象,我们执行这个Task对象并获得表示响应的HttpResponseMessage对象,然后以反射的形式将其封装成HttpMessage对象。我们最终利用此HttpMessage对象对请求作最终的响应。HttpSelfHostServer定义了OpenAsync和CloseAsync方法开启和关闭监听器,与之相匹配,我们也为Open方法定义了匹配的Close方法来关闭已经开启的ChannelListener。

为了验证我们自定义的MyHttpSelfHostServer是否能够替代“原生”的HttpSelfHostServer,我们在一个控制台中定义了如下一个继承自ApiController的ContactsController。它具有两个重载的Action方法Get,前者用于返回所有的联系人列表,后者返回指定ID的某个联系人信息。

1: public class ContactsController : ApiController
2: {
3:     private static List
contacts = new List
4:     {
5:         new Contact{ Id="001", Name = "张三",  PhoneNo="123", EmailAddress="zhangsan@gmail.com"},
6:         new Contact{ Id="002",Name = "李四", PhoneNo="456", EmailAddress="lisi@gmail.com"}
7:     };
8: 
9:     public IEnumerable
Get()
10:     {
11:         return contacts;
12:     }
13: 
14:     public Contact Get(string id)
15:     {
16:         return contacts.FirstOrDefault(c => c.Id == id);
17:     }
18: }
19: 
20: public class Contact
21: {
22:     public string Id { get; set; }
23:     public string Name { get; set; }
24:     public string PhoneNo { get; set; }
25:     public string EmailAddress { get; set; }
26: }

在作为入口的Main方法中我们编写了如下一段简单的“寄宿”程序。我们根据创建的HttpConfiguration对象和指定的监听基地址(“http://127.0.0.1:3721”)创建了一个MyHttpSelfHostServer对象。在调用Open方法开始监听之前,我们注册了一个URL模板为“http://127.0.0.1:3721”的HttpRoute。

1: class Program
2: {
3:     static void Main(string[] args)
4:     {
5:         using (MyHttpSelfHostServer httpServer = new MyHttpSelfHostServer(new HttpConfiguration(), new Uri("http://127.0.0.1:3721")))
6:         {
7:             httpServer.Configuration.Routes.MapHttpRoute(
8:                 name             : "DefaultApi",
9:                 routeTemplate    : "api/{controller}/{id}",
10:                 defaults         : new { id = RouteParameter.Optional });
11: 
12:             httpServer.Open();
13:             Console.Read();
14:         }
15:     }
16: }

运行该程序之后,这个“宿主”程序便开始进行请求的监听。现在我们直接利用浏览器对定义在ContactsController中的两个Action方法Get发起请求,通过注册的HttpRoute和“请求的HTTP方法直接作为Action名称”的原理,我们使用的URL分别为“http://127.0.0.1:3721/api/contacts”和“http://127.0.0.1:3721/api/contacts/001”。如右图所示,我们期望的联系人信息直接以XML的形式显示在浏览器中,由此可见我们自定义的MyHttpSelfHostServer“不辱使命”。

作者:蒋金楠
微信公众账号:大内老A
微博:
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号
蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
你可能感兴趣的文章
Arduino 4X3键盘 AD模拟分量实现
查看>>
C# 服务程序 - 调试服务
查看>>
LeetCode 627. Swap Salary
查看>>
CentOS 6.7安装Spark 1.5.2
查看>>
POJ 2449 Remmarguts' Date ——A*算法求k短路
查看>>
渗透测试之正则过滤
查看>>
PL/SQL — 函数
查看>>
托尔斯泰
查看>>
c语言之if语句
查看>>
Unity_UIWidgets学习笔记08_组件Row/Column
查看>>
用最简单的例子实现jQuery图片即时上传
查看>>
关闭sublime自动检测更新提示
查看>>
Oracle的游标
查看>>
SDOI2009 虔诚的墓主人
查看>>
2017北京国庆刷题Day6 morning
查看>>
安装cocoapods遇到两大坑-Ruby版本升级和Podfile的配置
查看>>
【原创】Linux下的ngix服务器安装步骤
查看>>
转载VC6LineNumberAddin
查看>>
网站设计欣赏:45+靓丽的蓝绿色主题网站作品【上篇】
查看>>
ADO.NET编程(3)在内存中对DataTable进行增/删/改操作
查看>>