ASP.NET Web API 框架研究 Self Host模式下的消息处理管道
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的属性字典中获取。
ASP.NET Web API 框架研究 Self Host模式下的消息处理管道的更多相关文章
- ASP.NET Web API 框架研究 Web Host模式下的消息处理管道
寄宿的作用是开启一个进程为Web API提供一个运行环境以解决持续监听.请求监听和响应回复,即将接收到的请求转换成HttpRequestMessage对象传入管道,并将管道生成并经过处理后的HttpR ...
- ASP.NET Web API 框架研究 ASP.NET Web API 路由
ASP.NET Web API 核心框架是一个独立的.抽象的消息处理管道,ASP.NET Web API有自己独立的路由系统,是消息处理管道的组成部分,其与ASP.NET路由系统有类似的设计,都能找到 ...
- ASP.NET Web API 框架研究 Controller实例的销毁
我们知道项目中创建的Controller,如ProductController都继承自ApiController抽象类,其又实现了接口IDisposable,所以,框架中自动调用Dispose方法来释 ...
- ASP.NET Web API 框架研究 Web Host模式路由及将请求转出到消息处理管道
Web Host 模式下的路由本质上还是通过ASP.NET 路由系统来进行路由的,只是通过继承和组合的方式对ASP.NET路由系统的内部的类进行了一些封装,产生自己专用一套类结构,功能逻辑基本都是一样 ...
- ASP.NET Web API 框架研究 核心的消息处理管道
ASP.NET Web API 的核心框架是一个由一组HttpMessageHandler有序组成的双工消息处理管道:寄宿监听到请求接受后,把消息传入该管道经过所有HttpMessageHandler ...
- ASP.NET Web API 框架研究 Action方法介绍
在根据请求解析出匹配的Controller类型并创建实例后,要在该Controller类型中的众多Action方法中选择与请求匹配的那一个,并执行,然后返回响应. Action方法,其元数据,主要包括 ...
- ASP.NET Web API 框架研究 服务容器 ServicesContainer
ServicesContainer是一个服务的容器,可以理解为—个轻量级的IoC容器,其维护着一个服务接口类型与服务实例之间的映射关系,可以根据服务接口类型获取对应的服务实例.构成ASP.NET We ...
- ASP.NET Web API 框架研究 ASP.NET 路由
ASP.NET Web API 如果采用Web Host方式来寄宿,在请求进入Web API 消息处理管道之前,就会用ASP.NET 自身的路由系统根据注册的路由表,解析出当前请求的HttpContr ...
- ASP.NET Web API 框架研究 IoC容器 DependencyResolver
一.概念 1.IoC(Inversion of Control),控制反转 即将依赖对象的创建和维护交给一个外部容器来负责,而不是应用本身.如,在类型A中需要使用类型B的实例,而B的实例的创建不是由A ...
随机推荐
- svn 回滚文件修改
取消对代码的修改分为两种情况: 第一种情况:改动没有被提交(commit). 这种情况下,使用svn revert就能取消之前的修改. svn revert用法如下: # svn revert [ ...
- nodejs TLS 只加密,未授权,进一步完善
const tls = require('tls'); const fs = require('fs'); const options = { key: fs.readFileSync('my_key ...
- BZOJ 1874 取石子游戏 - SG函数
Description $N$堆石子, $M$种取石子的方式, 最后取石子的人赢, 问先手是否必胜 $A_i <= 1000$,$ B_i <= 10$ Solution 由于数据很小, ...
- autohotkey快捷键
;已经基本修复了输入带shift的时候跟输入法中英文切换之间的冲突 SetStoreCapslockMode, off SetKeyDelay, ^CapsLock:: #UseHook ;用这个和下 ...
- linux 和 主机通信的另类方法
偶然发现,linux可以从github上直接下载代码.这样就能用windows写好代码,直接给linux来跑了.很方便. 当然是因为我还不会配置网络来让linux和windows通信.弄了一个下午也没 ...
- Mac网络命令 老命令重新学
网络与通信操作 命令名 功能描述 使用举例 telnet 远程登录 telnet hpc.sp.net.edu.cn rlogin 远程登录 rlogin hostname -l username r ...
- windows 8 update to windows 8.1
可以参考以下几个链接: http://blogs.windows.com/windows/b/appbuilder/archive/2013/07/24/windows-8-to-windows-8- ...
- 【Web】网页字体图标的使用
字体图标介绍 网页中图片有很多优点,但也有很多缺点,会增加文件的大小以及增加http请求.这时候就需要用的字体图标(iconfont).字体图标的优点,可以跟图片一样改变透明度.旋转等,本质上是文字, ...
- mysql里几个超时配置参数wait_timeout,net_read_timeout等
以下这些配置项单位都是秒,在mysql命令行中可以使用show global variables like '变量名';可查询配置值. connect_timeout:连接响应超时时间.服务器端在这个 ...
- python之函数篇3
一:函数的定义 1)函数的简单使用,无参函数 def f1(): # 定义函数指定函数名 print("hello") # 指定功能 f1() # 调用函数,才能执行函数体里面的功 ...