Self Host模式下的ASP.NET Web API与WCF非常相似,都可以寄宿在任意类型的托管应用程序中,宿主可以是Windows Form 、WPF、控制台应用以及Windows Service,寄宿的请求的监听、接收 和响应功能,是通过一个类型为HttpBinding的Binding对象创建的ChannelListener管道来完成的。
一、Binding绑定模型
Binding用来创建处理和传输消息的信道栈,信道栈有一组Channel组成,Binding也由一组BindingElement组成,每个BindingElement会创建一个ChannelListener,ChannelListener再创建对应的Channel,而每个Channel负责处理消息的单独一块功能,Binding启动时候会创建多个Channel组成一个消息处理管道,对请求依次进行处理,对相应按相反方向进行处理,有点类似ASP.NET Web API的消息处理管道,而这是宿主内部的功能,要注意区分。
对于这种由Binding创建的多个Channel组成的消息处理管道的消息处理能力,是由其包含的Channel决定的,有两种Channel是必不可少的,TransportChannel和MessageEncodingChanneI:
- TransportChannel 面向传输层用于发送和接收消息
- MessageEncodingChanneI 负责对接收(请求)的消息实施解码 ,并对发送(响应)的消息实施编码
二、涉及的类及源码分析
类主要在程序集System.Web.Http SeIfHost.dII中。
1、HttpBinding
继承自Binding,其只由两个BindingElement组成,Http(s)TransportBindingElement和HttpMessageEncodingBindingElement
Http(s)TransportBindingElement 决定最终采用的传输协议,Http或Https
HttpMessageEncodingBindingElement 创 建一个MessageEncoder对象完成针对消息的编码/解码工作 。
public class HttpBinding : Binding, IBindingRuntimePreferences
{
internal const string CollectionElementName = "httpBinding";
//默认传输模式为Buffered
internal const TransferMode DefaultTransferMode = System.ServiceModel.TransferMode.Buffered;
//传输BindingElement
private HttpsTransportBindingElement _httpsTransportBindingElement;
private HttpTransportBindingElement _httpTransportBindingElement;
private HttpBindingSecurity _security;
//编码/解码BindingElement
private HttpMessageEncodingBindingElement _httpMessageEncodingBindingElement;
private Action<HttpTransportBindingElement> _configureTransportBindingElement;
//初始化HttpBinding
public HttpBinding()
{
Initialize();
}
public HttpBinding(HttpBindingSecurityMode securityMode)
: this()
{
_security.Mode = securityMode;
}
//从URL中确定主机名的比较模式
public HostNameComparisonMode HostNameComparisonMode
{
get { return _httpTransportBindingElement.HostNameComparisonMode; }
set
{
_httpTransportBindingElement.HostNameComparisonMode = value;
_httpsTransportBindingElement.HostNameComparisonMode = value;
}
}
public long MaxBufferPoolSize
{
get { return _httpTransportBindingElement.MaxBufferPoolSize; }
set
{
_httpTransportBindingElement.MaxBufferPoolSize = value;
_httpsTransportBindingElement.MaxBufferPoolSize = value;
}
}
//Buffered模式下的消息的最大缓冲区大小,Buffered模式,即消息先会保存于内存缓冲区后一并传输
[DefaultValue(TransportDefaults.MaxBufferSize)]
public int MaxBufferSize
{
get { return _httpTransportBindingElement.MaxBufferSize; }
set
{
_httpTransportBindingElement.MaxBufferSize = value;
_httpsTransportBindingElement.MaxBufferSize = value;
}
}
//请求消息的最大尺寸,默认为65536
[DefaultValue(TransportDefaults.MaxReceivedMessageSize)]
public long MaxReceivedMessageSize
{
get { return _httpTransportBindingElement.MaxReceivedMessageSize; }
set
{
_httpTransportBindingElement.MaxReceivedMessageSize = value;
_httpsTransportBindingElement.MaxReceivedMessageSize = value;
}
}
public Action<HttpTransportBindingElement> ConfigureTransportBindingElement
{
get { return _configureTransportBindingElement; }
set
{
if (value == null)
{
throw Error.PropertyNull();
}
_configureTransportBindingElement = value;
}
}
//传输模式
[DefaultValue(HttpTransportDefaults.TransferMode)]
public TransferMode TransferMode
{
get { return _httpTransportBindingElement.TransferMode; }
set
{
_httpTransportBindingElement.TransferMode = value;
_httpsTransportBindingElement.TransferMode = value;
}
}
//创建BindingElements,只包含两种
public override BindingElementCollection CreateBindingElements()
{
BindingElementCollection bindingElements = new BindingElementCollection();
bindingElements.Add(_httpMessageEncodingBindingElement);
bindingElements.Add(GetTransport());
return bindingElements.Clone();
}
private TransportBindingElement GetTransport()
{
HttpTransportBindingElement result = null;
if (_security.Mode == HttpBindingSecurityMode.Transport)
{
_security.Transport.ConfigureTransportProtectionAndAuthentication(_httpsTransportBindingElement);
result = _httpsTransportBindingElement;
}
else if (_security.Mode == HttpBindingSecurityMode.TransportCredentialOnly)
{
_security.Transport.ConfigureTransportAuthentication(_httpTransportBindingElement);
result = _httpTransportBindingElement;
}
else
{
_security.Transport.DisableTransportAuthentication(_httpTransportBindingElement);
result = _httpTransportBindingElement;
}
if (_configureTransportBindingElement != null)
{
_configureTransportBindingElement(result);
}
return result;
}
//初始化各种对象
private void Initialize()
{
_security = new HttpBindingSecurity();
_httpTransportBindingElement = new HttpTransportBindingElement();
_httpTransportBindingElement.ManualAddressing = true;
_httpsTransportBindingElement = new HttpsTransportBindingElement();
_httpsTransportBindingElement.ManualAddressing = true;
_httpMessageEncodingBindingElement = new HttpMessageEncodingBindingElement();
}
}
2、HttpMessage
Binding处理管道中处理的消息是HttpMessage,其继承自Message,它是对ASP.NET Web API处理的消息HttpRequestMessage和HttpResponseMessage的封装;HttpMessageEncoder对请求消息解码后得到HttpMessage对象,其会转成—个HttpRequestMessage对象并传入ASP.NET WebAPI消息处理管道进行处理,处理完后返回HttpResponseMessage对象,其被封装成HttpMessage对象,在通过传输层将响应返回给客户端之前,需要利用HttpMessageEncoder对其进行编码,然后进行传输。
internal sealed class HttpMessage : Message
{
private HttpRequestMessage _request;
private HttpResponseMessage _response;
private MessageHeaders _headers;
private MessageProperties _properties;
//请求对象为参数,进行封装
public HttpMessage(HttpRequestMessage request)
{
Contract.Assert(request != null, "The 'request' parameter should not be null.");
_request = request;
Headers.To = request.RequestUri;
IsRequest = true;
}
//响应对象为参数,进行封装
public HttpMessage(HttpResponseMessage response)
{
Contract.Assert(response != null, "The 'response' parameter should not be null.");
_response = response;
IsRequest = false;
}
public override MessageVersion Version
{
get
{
EnsureNotDisposed();
return MessageVersion.None;
}
}
public override MessageHeaders Headers
{
get
{
EnsureNotDisposed();
if (_headers == null)
{
_headers = new MessageHeaders(MessageVersion.None);
}
return _headers;
}
}
public override MessageProperties Properties
{
get
{
EnsureNotDisposed();
if (_properties == null)
{
_properties = new MessageProperties();
_properties.AllowOutputBatching = false;
}
return _properties;
}
}
public override bool IsEmpty
{
get
{
long? contentLength = GetHttpContentLength();
return contentLength.HasValue && contentLength.Value == 0;
}
}
public override bool IsFault
{
get { return false; }
}
public bool IsRequest { get; private set; }
//从HttpMessage中获取HttpRequestMessage,参数extract是否是抽取,抽取即访问一次后第二次访问会返回null
public HttpRequestMessage GetHttpRequestMessage(bool extract)
{
EnsureNotDisposed();
Contract.Assert(IsRequest, "This method should only be called when IsRequest is true.");
if (extract)
{
HttpRequestMessage req = _request;
//设置为null,第二次访问为null
_request = null;
return req;
}
return _request;
}
//从HttpMessage中获取HttpResponseMessage ,参数extract是否是抽取,抽取即访问一次后第二次访问会返回null
public HttpResponseMessage GetHttpResponseMessage(bool extract)
{
EnsureNotDisposed();
Contract.Assert(!IsRequest, "This method should only be called when IsRequest is false.");
if (extract)
{
HttpResponseMessage res = _response;
//设置为null,第二次访问为null
_response = null;
return res;
}
return _response;
}
protected override void OnClose()
{
base.OnClose();
if (_request != null)
{
_request.DisposeRequestResources();
_request.Dispose();
_request = null;
}
if (_response != null)
{
_response.Dispose();
_response = null;
}
}
private static string GetNotSupportedMessage()
{
return Error.Format(
SRResources.MessageReadWriteCopyNotSupported,
HttpMessageExtensions.ToHttpRequestMessageMethodName,
HttpMessageExtensions.ToHttpResponseMessageMethodName,
typeof(HttpMessage).Name);
}
private void EnsureNotDisposed()
{
if (IsDisposed)
{
throw Error.ObjectDisposed(SRResources.MessageClosed, typeof(Message).Name);
}
}
private long? GetHttpContentLength()
{
HttpContent content = IsRequest
? GetHttpRequestMessage(false).Content
: GetHttpResponseMessage(false).Content;
if (content == null)
{
return 0;
}
return content.Headers.ContentLength;
}
}
3、HttpSelfHostServer
继承自HttpServer,是WebAPI 消息处理管道的第一个处理器,类似Web Host模式,管道的配置是通过HttpConfiguration完成,其对应由HttpSelfHostConfiguration来完成,也是在构造函数里指定。
重要逻辑都在代码注释里,代码太多,只是拿出重要的代码,以下是注意点:
- 虽然继承HttpServer,但是没有重写SendAsync方法,只是重用了HttpServer的SendAsync,所以这部分逻辑是一致的
- HttpSelfHostServer本身会打开和开启channel来监听,监听到消息后,会创建HttpRequestMessage,并调用基类HttpServer的SendAsync,进行后续的消息处理,返回响应HttpResponseMessage后,转换成Message返回给客户端。
public sealed class HttpSelfHostServer : HttpServer
{
private ConcurrentBag<IReplyChannel> _channels = new ConcurrentBag<IReplyChannel>();
private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
private bool _disposed;
private HttpSelfHostConfiguration _configuration;
private IChannelListener<IReplyChannel> _listener;
private readonly object _windowSizeLock = new object();
//调用基类HttpServer构造函数,所以最有一个处理器默认也是HttpRoutingDispatcher
public HttpSelfHostServer(HttpSelfHostConfiguration configuration)
: base(configuration)
{
if (configuration == null)
{
throw Error.ArgumentNull("configuration");
}
_configuration = configuration;
InitializeCallbacks();
}
//初始化回调,接受到消息后,通过回调触发
private void InitializeCallbacks()
{
//...
_onReceiveRequestContextComplete = new AsyncCallback(OnReceiveRequestContextComplete);
_onReplyComplete = new AsyncCallback(OnReplyComplete);
}
//打开服务
public Task OpenAsync()
{
if (Interlocked.CompareExchange(ref _state, 1, 0) == 1)
{
throw Error.InvalidOperation(SRResources.HttpServerAlreadyRunning, typeof(HttpSelfHostServer).Name);
}
_openTaskCompletionSource = new TaskCompletionSource<bool>();
BeginOpenListener(this);
return _openTaskCompletionSource.Task;
}
private void BeginOpenListener(HttpSelfHostServer server)
{
Contract.Assert(server != null);
try
{
// 创建 WCF HTTP transport channel
HttpBinding binding = new HttpBinding();
// 获取配置(从HttpSelfHostConfiguration ),并设置到HttpBinding
BindingParameterCollection bindingParameters = server._configuration.ConfigureBinding(binding);
if (bindingParameters == null)
{
bindingParameters = new BindingParameterCollection();
}
// 创建channel listener
server._listener = binding.BuildChannelListener<IReplyChannel>(server._configuration.BaseAddress, bindingParameters);
if (server._listener == null)
{
throw Error.InvalidOperation(SRResources.InvalidChannelListener, typeof(IChannelListener).Name, typeof(IReplyChannel).Name);
}
//开始监听
IAsyncResult result = server._listener.BeginOpen(_onOpenListenerComplete, server);
if (result.CompletedSynchronously)
{
//监听到请求消息,触发回调函数
OpenListenerComplete(result);
}
}
catch (Exception e)
{
FaultTask(server._openTaskCompletionSource, e);
}
}
//..省略各种回调
//Channel接收到消息后,通过回调最后会调用此方法
private async void ProcessRequestContext(ChannelContext channelContext, RequestContext requestContext)
{
Contract.Assert(channelContext != null);
Contract.Assert(requestContext != null);
//调用下边的核心方法SendAsync
HttpResponseMessage response = await SendAsync(channelContext, requestContext);
//异步调用处理完后返回响应消息,把响应消息转换成Message
Message reply = response.ToMessage();
//传回给客户端
BeginReply(new ReplyContext(channelContext, requestContext, reply));
}
//核心方法,注意没有重写(override)基类SendAsync
private async Task<HttpResponseMessage> SendAsync(ChannelContext channelContext, RequestContext requestContext)
{
HttpRequestMessage request = null;
try
{
request = CreateHttpRequestMessage(requestContext);
}
catch
{
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}
try
{
//SendAsync方法是HttpServer的SendAsync,因为本类HttpSelfHostServer没有override该方法,
//所以其他逻辑,比如建立Web API消息处理管道等,都和HttpServer一样
HttpResponseMessage response = await channelContext.Server.SendAsync(request, channelContext.Server._cancellationTokenSource.Token);
if (response == null)
{
response = request.CreateResponse(HttpStatusCode.InternalServerError);
}
return response;
}
catch (OperationCanceledException operationCanceledException)
{
return request.CreateErrorResponse(HttpStatusCode.ServiceUnavailable, SRResources.RequestCancelled, operationCanceledException);
}
}
//构建HttpRequestMessage
private HttpRequestMessage CreateHttpRequestMessage(RequestContext requestContext)
{
// 从HTTP请求中获取 WCF Message
HttpRequestMessage request = requestContext.RequestMessage.ToHttpRequestMessage();
if (request == null)
{
throw Error.InvalidOperation(SRResources.HttpMessageHandlerInvalidMessage, requestContext.RequestMessage.GetType());
}
// 创建windows授权的 principal 信息并添加进请求HttpRequestMessage
SetCurrentPrincipal(request);
HttpRequestContext httpRequestContext = new SelfHostHttpRequestContext(requestContext, _configuration,
request);
request.SetRequestContext(httpRequestContext);
// 添加查询客户端证书委托到属性字典中,以便以后可以查询
request.Properties.Add(HttpPropertyKeys.RetrieveClientCertificateDelegateKey, _retrieveClientCertificate);
// 添加表示是否是本地请求信息到请求的属性字典中
request.Properties.Add(HttpPropertyKeys.IsLocalKey, new Lazy<bool>(() => IsLocal(requestContext.RequestMessage)));
return request;
}
protected override void Dispose(bool disposing)
{
if (!_disposed)
{
_disposed = true;
if (_cancellationTokenSource != null)
{
_cancellationTokenSource.Dispose();
_cancellationTokenSource = null;
}
}
base.Dispose(disposing);
}
private static void SetCurrentPrincipal(HttpRequestMessage request)
{
SecurityMessageProperty property = request.GetSecurityMessageProperty();
if (property != null)
{
ServiceSecurityContext context = property.ServiceSecurityContext;
if (context != null && context.PrimaryIdentity != null)
{
WindowsIdentity windowsIdentity = context.PrimaryIdentity as WindowsIdentity;
if (windowsIdentity != null)
{
//设置Thread.CurrentPrincipal 为WindowsPrincipal
Thread.CurrentPrincipal = new WindowsPrincipal(windowsIdentity);
}
}
}
}
}
4、HttpSelfHostConfiguration
继承自HttpConfiguration,构造函数中指定一个Uri作为监听基地址,Self Host模式下,请求的监听、接收、响应基本都是通过HttpBinding完成的,HttpSelfHostConfiguration的大部分属性都是用于对创建HttpBinding进行配置,所以它们的属性基本相同。
public class HttpSelfHostConfiguration : HttpConfiguration
{
public HttpSelfHostConfiguration(string baseAddress)
: this(CreateBaseAddress(baseAddress))
{
}
//基地址Uri
public Uri BaseAddress
{
get { return _baseAddress; }
}
//最大请求并发量,默认值是100,若是多处理器,要cheny乘以处理器个数
public int MaxConcurrentRequests
{
get { return _maxConcurrentRequests; }
set
{
if (value < MinConcurrentRequests)
{
throw Error.ArgumentMustBeGreaterThanOrEqualTo("value", value, MinConcurrentRequests);
}
_maxConcurrentRequests = value;
}
}
//消息传输模式,分Streamed和Buffered(默认)
public TransferMode TransferMode
{
get { return _transferMode; }
set
{
TransferModeHelper.Validate(value, "value");
_transferMode = value;
}
}
//从URI中获取主机名的匹配比较模式
public HostNameComparisonMode HostNameComparisonMode
{
get { return _hostNameComparisonMode; }
set
{
HostNameComparisonModeHelper.Validate(value, "value");
_hostNameComparisonMode = value;
}
}
//Buffered模式的最大缓冲池大小,默认值65536
public int MaxBufferSize
{
get
{
if (_maxBufferSizeIsInitialized || TransferMode != TransferMode.Buffered)
{
return _maxBufferSize;
}
long maxReceivedMessageSize = MaxReceivedMessageSize;
if (maxReceivedMessageSize > Int32.MaxValue)
{
return Int32.MaxValue;
}
return (int)maxReceivedMessageSize;
}
set
{
if (value < MinBufferSize)
{
throw Error.ArgumentMustBeGreaterThanOrEqualTo("value", value, MinBufferSize);
}
_maxBufferSizeIsInitialized = true;
_maxBufferSize = value;
}
}
//允许请求消息的最大大小,默认为65536
public long MaxReceivedMessageSize
{
get { return _maxReceivedMessageSize; }
set
{
if (value < MinReceivedMessageSize)
{
throw Error.ArgumentMustBeGreaterThanOrEqualTo("value", value, MinReceivedMessageSize);
}
_maxReceivedMessageSize = value;
}
}
//接收请求消息的超时时间,默认为10分钟
public TimeSpan ReceiveTimeout
{
get { return _receiveTimeout; }
set
{
if (value < TimeSpan.Zero)
{
throw Error.ArgumentMustBeGreaterThanOrEqualTo("value", value, TimeSpan.Zero);
}
_receiveTimeout = value;
}
}
//发送响应消息的超时时间,默认为1分钟
public TimeSpan SendTimeout
{
get { return _sendTimeout; }
set
{
if (value < TimeSpan.Zero)
{
throw Error.ArgumentMustBeGreaterThanOrEqualTo("value", value, TimeSpan.Zero);
}
_sendTimeout = value;
}
}
//客户端采用的用户凭证类型
public HttpClientCredentialType ClientCredentialType
{
get { return _clientCredentialType; }
set { _clientCredentialType = value; }
}
//将配置应用到httpBinding
internal BindingParameterCollection ConfigureBinding(HttpBinding httpBinding)
{
return OnConfigureBinding(httpBinding);
}
protected virtual BindingParameterCollection OnConfigureBinding(HttpBinding httpBinding)
{
if (httpBinding == null)
{
throw Error.ArgumentNull("httpBinding");
}
if (_clientCredentialType != HttpClientCredentialType.Basic && _credentials.UserNameAuthentication.CustomUserNamePasswordValidator != null)
{
throw Error.InvalidOperation(SRResources.CannotUseOtherClientCredentialTypeWithUserNamePasswordValidator);
}
if (_clientCredentialType != HttpClientCredentialType.Certificate && _credentials.ClientCertificate.Authentication.CustomCertificateValidator != null)
{
throw Error.InvalidOperation(SRResources.CannotUseOtherClientCredentialTypeWithX509CertificateValidator);
}
httpBinding.MaxBufferSize = MaxBufferSize;
httpBinding.MaxReceivedMessageSize = MaxReceivedMessageSize;
httpBinding.TransferMode = TransferMode;
httpBinding.HostNameComparisonMode = HostNameComparisonMode;
httpBinding.ReceiveTimeout = ReceiveTimeout;
httpBinding.SendTimeout = SendTimeout;
if (_baseAddress.Scheme == Uri.UriSchemeHttps)
{
httpBinding.Security = new HttpBindingSecurity()
{
Mode = HttpBindingSecurityMode.Transport,
};
}
if (_clientCredentialType != HttpClientCredentialType.None)
{
if (httpBinding.Security == null || httpBinding.Security.Mode == HttpBindingSecurityMode.None)
{
// Basic over HTTP case
httpBinding.Security = new HttpBindingSecurity()
{
Mode = HttpBindingSecurityMode.TransportCredentialOnly,
};
}
httpBinding.Security.Transport.ClientCredentialType = _clientCredentialType;
}
if (UserNamePasswordValidator != null || X509CertificateValidator != null)
{
// those are the only two things that affect service credentials
return AddCredentialsToBindingParameters();
}
else
{
return null;
}
}
private BindingParameterCollection AddCredentialsToBindingParameters()
{
BindingParameterCollection bindingParameters = new BindingParameterCollection();
bindingParameters.Add(_credentials);
return bindingParameters;
}
//根据基地址创建对应Uri
private static Uri CreateBaseAddress(string baseAddress)
{
if (baseAddress == null)
{
throw Error.ArgumentNull("baseAddress");
}
return new Uri(baseAddress, UriKind.RelativeOrAbsolute);
}
}
三、HttpBiding、HttpSelfHostServer和消息处理管道的衔接
先根据指定的监听基地址创建一个HttpSelftHostConfiguration对象,然后,根据它创建HttpSelfHostServer,调用OpenAsync方法开启时候,HttpSelfHostServer会创建一个HttpBinding,并用指定的HttpSelfHostConfiguration对HttpBinding进行配置,然后,HttpBinding会根据监听基地址创建一个ChannelListener管道,对请求进行监听,请求到达时候,接收的二进制数据会经过解码后生成HttpMessage,其是对HttpRequestMessage的封装,然后,HttpSelfHostServer会从该HttpMessage提取出HttpRequestMessage,传递给WebAPI消息处理管道的其他处理器依次处理,处理完返回一个HttpResponseMessage对象,把其封装成HttpMessage,接着对其进行编码,通过传输层进行传输,返回给客户端。
另外,特别注意的是,Web API消息处理管道的最后一个消息处理器还是HttpRoutingDispatcher,其在HttpSelfHostServer创建时候,调用基类HttpServer的构造函数时候指定,而且在HttpRoutingDispatcher路由时候,由于路由数据没在HttpRequestMessage的属性字典中,所以要直接进行路由解析,获得的路由数据也会放在HttpRequestMessage的属性字典中,所以,后续的Controller创建等操作需要的路由数据都是从HttpRequestMessage的属性字典中获取。