如何建立微信公众号怎么申请,如何做好网站针对搜索引擎的seo,怎么开自己的网站,网站建设原则包括哪些内容动手写一个简版 asp.net coreIntro之前看到过蒋金楠老师的一篇 200 行代码带你了解 asp.net core 框架#xff0c;最近参考蒋老师和 Edison 的文章和代码#xff0c;结合自己对 asp.net core 的理解 #xff0c;最近自己写了一个 MiniAspNetCore #xff0c;写篇文章总结一… 动手写一个简版 asp.net coreIntro之前看到过蒋金楠老师的一篇 200 行代码带你了解 asp.net core 框架最近参考蒋老师和 Edison 的文章和代码结合自己对 asp.net core 的理解 最近自己写了一个 MiniAspNetCore 写篇文章总结一下。HttpContextHttpContext 可能是最为常用的一个类了 HttpContext 是请求上下文包含了所有的请求信息以及响应信息以及一些自定义的用于在不同中间件中传输数据的信息来看一下 HttpContext 的定义:public class HttpContext
{public IServiceProvider RequestServices { get; set; }public HttpRequest Request { get; set; }public HttpResponse Response { get; set; }public IFeatureCollection Features { get; set; }public HttpContext(IFeatureCollection featureCollection){Features featureCollection;Request new HttpRequest(featureCollection);Response new HttpResponse(featureCollection);}
}
HttpRequest 即为请求信息对象包含了所有请求相关的信息HttpResponse 为响应信息对象包含了请求对应的响应信息RequestServices 为 asp.net core 里的 RequestServices代表当前请求的服务提供者可以使用它来获取具体的服务实例Features 为 asp.net core 里引入的对象可以用来在不同中间件中传递信息和用来解耦合下面我们就来看下 HttpRequest 和 HttpResponse 是怎么实现的HttpRequest:public class HttpRequest
{private readonly IRequestFeature _requestFeature;public HttpRequest(IFeatureCollection featureCollection){_requestFeature featureCollection.GetIRequestFeature();}public Uri Url _requestFeature.Url;public NameValueCollection Headers _requestFeature.Headers;public string Method _requestFeature.Method;public string Host _requestFeature.Url.Host;public Stream Body _requestFeature.Body;
}
HttpResponse:public class HttpResponse
{private readonly IResponseFeature _responseFeature;public HttpResponse(IFeatureCollection featureCollection){_responseFeature featureCollection.GetIResponseFeature();}public bool ResponseStarted _responseFeature.Body.Length 0;public int StatusCode{get _responseFeature.StatusCode;set _responseFeature.StatusCode value;}public async Task WriteAsync(byte[] responseBytes){if (_responseFeature.StatusCode 0){_responseFeature.StatusCode 200;}if (responseBytes ! null responseBytes.Length 0){await _responseFeature.Body.WriteAsync(responseBytes);}}
}
Features上面我们提供我们可以使用 Features 在不同中间件中传递信息和解耦合由上面 HttpRequest/ HttpResponse 的代码我们可以看出来 HttpRequest 和 HttpResponse 其实就是在 IRequestFeature 和 IResponseFeature 的基础上封装了一层真正的核心其实是 IRequestFeature/ IResponseFeature 而这里使用接口就很好的实现了解耦可以根据不同的 WebServer 使用不同的 RequestFeature/ ResponseFeature来看下 IRequestFeature/ IResponseFeature 的实现public interface IRequestFeature
{Uri Url { get; }string Method { get; }NameValueCollection Headers { get; }Stream Body { get; }
}
public interface IResponseFeature
{public int StatusCode { get; set; }NameValueCollection Headers { get; set; }public Stream Body { get; }
}
这里的实现和 asp.net core 的实际的实现方式应该不同asp.net core 里 Headers 同一个 Header 允许有多个值asp.net core 里是 StringValues 来实现的这里简单处理了使用了一个 NameValueCollection 对象上面提到的 Features 是一个 IFeatureCollection 对象相当于是一系列的 Feature 对象组成的来看下 FeatureCollection 的定义public interface IFeatureCollection : IDictionaryType, object { }
public class FeatureCollection : DictionaryType, object, IFeatureCollection
{
}
这里 IFeatureCollection 直接实现 IDictionaryType,object 通过一个字典 Feature 类型为 KeyFeature 对象为 Value 的字典来保存为了方便使用可以定义两个扩展方法来方便的Get/Setpublic static class FeatureExtensions
{public static IFeatureCollection SetTFeature(this IFeatureCollection featureCollection, TFeature feature){featureCollection[typeof(TFeature)] feature;return featureCollection;}public static TFeature GetTFeature(this IFeatureCollection featureCollection){var featureType typeof(TFeature);return featureCollection.ContainsKey(featureType) ? (TFeature)featureCollection[featureType] : default(TFeature);}
}
Web服务器上面我们已经提到了 Web 服务器通过 IRequestFeature/ IResponseFeature 来实现不同 web 服务器和应用程序的解耦web 服务器只需要提供自己的 RequestFeature/ ResponseFeature 即可为了抽象不同的 Web 服务器我们需要定义一个 IServer 的抽象接口定义如下public interface IServer
{Task StartAsync(FuncHttpContext, Task requestHandler, CancellationToken cancellationToken default);
}
IServer 定义了一个 StartAsync 方法用来启动 Web服务器StartAsync 方法有两个参数一个是 requestHandler是一个用来处理请求的委托另一个是取消令牌用来停止 web 服务器示例使用了 HttpListener 来实现了一个简单 Web 服务器 HttpListenerServer 定义如下public class HttpListenerServer : IServer
{private readonly HttpListener _listener;private readonly IServiceProvider _serviceProvider;public HttpListenerServer(IServiceProvider serviceProvider, IConfiguration configuration){_listener new HttpListener();var urls configuration.GetAppSetting(ASPNETCORE_URLS)?.Split(;);if (urls ! null urls.Length 0){foreach (var url in urls.Where(u u.IsNotNullOrEmpty()).Select(u u.Trim()).Distinct()){// Prefixes must end in a forward slash (/)// https://stackoverflow.com/questions/26157475/use-of-httplistener_listener.Prefixes.Add(url.EndsWith(/) ? url : ${url}/);}}else{_listener.Prefixes.Add(http://localhost:5100/);}_serviceProvider serviceProvider;}public async Task StartAsync(FuncHttpContext, Task requestHandler, CancellationToken cancellationToken default){_listener.Start();if (_listener.IsListening){Console.WriteLine(the server is listening on );Console.WriteLine(_listener.Prefixes.StringJoin(,));}while (!cancellationToken.IsCancellationRequested){var listenerContext await _listener.GetContextAsync();var featureCollection new FeatureCollection();featureCollection.Set(listenerContext.GetRequestFeature());featureCollection.Set(listenerContext.GetResponseFeature());using (var scope _serviceProvider.CreateScope()){var httpContext new HttpContext(featureCollection){RequestServices scope.ServiceProvider,};await requestHandler(httpContext);}listenerContext.Response.Close();}_listener.Stop();}
}
HttpListenerServer 实现的 RequestFeature/ ResponseFeatuepublic class HttpListenerRequestFeature : IRequestFeature
{private readonly HttpListenerRequest _request;public HttpListenerRequestFeature(HttpListenerContext listenerContext){_request listenerContext.Request;}public Uri Url _request.Url;public string Method _request.HttpMethod;public NameValueCollection Headers _request.Headers;public Stream Body _request.InputStream;
}
public class HttpListenerResponseFeature : IResponseFeature
{private readonly HttpListenerResponse _response;public HttpListenerResponseFeature(HttpListenerContext httpListenerContext){_response httpListenerContext.Response;}public int StatusCode { get _response.StatusCode; set _response.StatusCode value; }public NameValueCollection Headers{get _response.Headers;set{_response.Headers new WebHeaderCollection();foreach (var key in value.AllKeys)_response.Headers.Add(key, value[key]);}}public Stream Body _response.OutputStream;
}
为了方便使用为 HttpListenerContext 定义了两个扩展方法就是上面 HttpListenerServer 中的 GetRequestFeature/ GetResponseFeaturepublic static class HttpListenerContextExtensions
{public static IRequestFeature GetRequestFeature(this HttpListenerContext context){return new HttpListenerRequestFeature(context);}public static IResponseFeature GetResponseFeature(this HttpListenerContext context){return new HttpListenerResponseFeature(context);}
}
RequestDelegate在上面的 IServer 定义里有一个 requestHandler 的 对象在 asp.net core 里是一个名称为 RequestDelegate 的对象而用来构建这个委托的在 asp.net core 里是 IApplicationBuilder这些在蒋老师和 Edison 的文章和代码里都可以看到这里我们只是简单介绍下我在 MiniAspNetCore 的示例中没有使用这些对象而是使用了自己抽象的 PipelineBuilder 和原始委托实现的asp.net core 里 RequestDelegate 定义public delegate Task RequestDelegate(HttpContext context);
其实和我们上面定义用的 FuncHttpContext,Task 是等价的IApplicationBuilder 定义/// summary
/// Defines a class that provides the mechanisms to configure an applications request pipeline.
/// /summary
public interface IApplicationBuilder
{/// summary/// Gets or sets the see crefT:System.IServiceProvider / that provides access to the applications service container./// /summaryIServiceProvider ApplicationServices { get; set; }/// summary/// Gets the set of HTTP features the applications server provides./// /summaryIFeatureCollection ServerFeatures { get; }/// summary/// Gets a key/value collection that can be used to share data between middleware./// /summaryIDictionarystring, object Properties { get; }/// summary/// Adds a middleware delegate to the applications request pipeline./// /summary/// param namemiddlewareThe middleware delegate./param/// returnsThe see crefT:Microsoft.AspNetCore.Builder.IApplicationBuilder /./returnsIApplicationBuilder Use(FuncRequestDelegate, RequestDelegate middleware);/// summary/// Creates a new see crefT:Microsoft.AspNetCore.Builder.IApplicationBuilder / that shares the see crefP:Microsoft.AspNetCore.Builder.IApplicationBuilder.Properties / of this/// see crefT:Microsoft.AspNetCore.Builder.IApplicationBuilder /./// /summary/// returnsThe new see crefT:Microsoft.AspNetCore.Builder.IApplicationBuilder /./returnsIApplicationBuilder New();/// summary/// Builds the delegate used by this application to process HTTP requests./// /summary/// returnsThe request handling delegate./returnsRequestDelegate Build();
}
我们这里没有定义 IApplicationBuilder使用了简化抽象的 IAsyncPipelineBuilder定义如下public interface IAsyncPipelineBuilderTContext
{IAsyncPipelineBuilderTContext Use(FuncFuncTContext, Task, FuncTContext, Task middleware);FuncTContext, Task Build();IAsyncPipelineBuilderTContext New();
}
对于 asp.net core 的中间件来说 上面的 TContext 就是 HttpContext替换之后也就是下面这样的public interface IAsyncPipelineBuilderHttpContext
{IAsyncPipelineBuilderHttpContext Use(FuncFuncHttpContext, Task, FuncHttpContext, Task middleware);FuncHttpContext, Task Build();IAsyncPipelineBuilderHttpContext New();
}
是不是和 IApplicationBuilder 很像如果不像可以进一步把 FuncHttpContext,Task 使用 RequestDelegate 替换public interface IAsyncPipelineBuilderHttpContext
{IAsyncPipelineBuilderHttpContext Use(FuncRequestDelegate, RequestDelegate middleware);RequestDelegate Build();IAsyncPipelineBuilderHttpContext New();
}
最后再将接口名称替换一下public interface IApplicationBuilder1
{IApplicationBuilder1 Use(FuncRequestDelegate, RequestDelegate middleware);RequestDelegate Build();IApplicationBuilder1 New();
}
至此就完全可以看出来了这 IAsyncPipelineBuilderHttpContext 就是一个简版的 IApplicationBuilderIAsyncPipelineBuilder 和 IApplicationBuilder 的作用是将注册的多个中间件构建成一个请求处理的委托中间件处理流程更多关于 PipelineBuilder 构建中间件的信息可以查看 让 .NET 轻松构建中间件模式代码 了解更多WebHost通过除了 Web 服务器之外还有一个 Web Host 的概念可以简单的这样理解一个 Web 服务器上可以有多个 Web Host就像 IIS/nginx (Web Server) 可以 host 多个站点可以说 WebHost 离我们的应用更近所以我们还需要 IHost 来托管应用public interface IHost
{Task RunAsync(CancellationToken cancellationToken default);
}
WebHost 定义public class WebHost : IHost
{private readonly FuncHttpContext, Task _requestDelegate;private readonly IServer _server;public WebHost(IServiceProvider serviceProvider, FuncHttpContext, Task requestDelegate){_requestDelegate requestDelegate;_server serviceProvider.GetRequiredServiceIServer();}public async Task RunAsync(CancellationToken cancellationToken default){await _server.StartAsync(_requestDelegate, cancellationToken).ConfigureAwait(false);}
}
为了方便的构建 Host对象引入了 HostBuilder 来方便的构建一个 Host定义如下public interface IHostBuilder
{IHostBuilder ConfigureConfiguration(ActionIConfigurationBuilder configAction);IHostBuilder ConfigureServices(ActionIConfiguration, IServiceCollection configureAction);IHostBuilder Initialize(ActionIConfiguration, IServiceProvider initAction);IHostBuilder ConfigureApplication(ActionIConfiguration, IAsyncPipelineBuilderHttpContext configureAction);IHost Build();
}
WebHostBuilderpublic class WebHostBuilder : IHostBuilder
{private readonly IConfigurationBuilder _configurationBuilder new ConfigurationBuilder();private readonly IServiceCollection _serviceCollection new ServiceCollection();private ActionIConfiguration, IServiceProvider _initAction null;private readonly IAsyncPipelineBuilderHttpContext _requestPipeline PipelineBuilder.CreateAsyncHttpContext(context {context.Response.StatusCode 404;return Task.CompletedTask;});public IHostBuilder ConfigureConfiguration(ActionIConfigurationBuilder configAction){configAction?.Invoke(_configurationBuilder);return this;}public IHostBuilder ConfigureServices(ActionIConfiguration, IServiceCollection configureAction){if (null ! configureAction){var configuration _configurationBuilder.Build();configureAction.Invoke(configuration, _serviceCollection);}return this;}public IHostBuilder ConfigureApplication(ActionIConfiguration, IAsyncPipelineBuilderHttpContext configureAction){if (null ! configureAction){var configuration _configurationBuilder.Build();configureAction.Invoke(configuration, _requestPipeline);}return this;}public IHostBuilder Initialize(ActionIConfiguration, IServiceProvider initAction){if (null ! initAction){_initAction initAction;}return this;}public IHost Build(){var configuration _configurationBuilder.Build();_serviceCollection.AddSingletonIConfiguration(configuration);var serviceProvider _serviceCollection.BuildServiceProvider();_initAction?.Invoke(configuration, serviceProvider);return new WebHost(serviceProvider, _requestPipeline.Build());}public static WebHostBuilder CreateDefault(string[] args){var webHostBuilder new WebHostBuilder();webHostBuilder.ConfigureConfiguration(builder builder.AddJsonFile(appsettings.json, true, true)).UseHttpListenerServer();return webHostBuilder;}
}
这里的示例我在 IHostBuilder 里增加了一个 Initialize 的方法来做一些初始化的操作我觉得有些数据初始化配置初始化等操作应该在这里操作而不应该在 Startup 的 Configure 方法里处理这样 Configure 方法可以更纯粹一些只配置 asp.net core 的请求管道这纯属个人意见没有对错之分这里 Host 的实现和 asp.net core 的实现不同有需要的可以深究源码在 asp.net core 2.x 的版本里是有一个 IWebHost 的在 asp.net core 3.x 以及 .net 5 里是没有 IWebHost 的取而代之的是通用主机 IHost 通过实现了一个 IHostedService 来实现 WebHost 的Run运行示例代码public class Program
{private static readonly CancellationTokenSource Cts new CancellationTokenSource();public static async Task Main(string[] args){Console.CancelKeyPress OnExit;var host WebHostBuilder.CreateDefault(args).ConfigureServices((configuration, services) {}).ConfigureApplication((configuration, app) {app.When(context context.Request.Url.PathAndQuery.StartsWith(/favicon.ico), pipeline { });app.When(context context.Request.Url.PathAndQuery.Contains(test),p { p.Run(context context.Response.WriteAsync(test)); });app.Use(async (context, next) {await context.Response.WriteLineAsync($middleware1, requestPath:{context.Request.Url.AbsolutePath});await next();}).Use(async (context, next) {await context.Response.WriteLineAsync($middleware2, requestPath:{context.Request.Url.AbsolutePath});await next();}).Use(async (context, next) {await context.Response.WriteLineAsync($middleware3, requestPath:{context.Request.Url.AbsolutePath});await next();});app.Run(context context.Response.WriteAsync(Hello Mini Asp.Net Core));}).Initialize((configuration, services) {}).Build();await host.RunAsync(Cts.Token);}private static void OnExit(object sender, EventArgs e){Console.WriteLine(exiting ...);Cts.Cancel();}
}
在示例项目目录下执行 dotnet run并访问 http://localhost:5100/:仔细观察浏览器 console 或 network 的话会发现还有一个请求浏览器会默认请求 /favicon.ico 获取网站的图标因为我们针对这个请求没有任何中间件的处理所以直接返回了 404在访问 /test可以看到和刚才的输出完全不同因为这个请求走了另外一个分支相当于 asp.net core 里 Map/ MapWhen 的效果另外 Run 代表里中间件的中断不会执行后续的中间件More上面的实现只是我在尝试写一个简版的 asp.net core 框架时的实现和 asp.net core 的实现并不完全一样如果需要请参考源码上面的实现仅供参考上面实现的源码可以在 Github 上获取 https://github.com/WeihanLi/SamplesInPractice/tree/master/MiniAspNetCoreasp.net core 源码https://github.com/dotnet/aspnetcoreReferencehttps://www.cnblogs.com/artech/p/inside-asp-net-core-framework.htmlhttps://www.cnblogs.com/artech/p/mini-asp-net-core-3x.htmlhttps://www.cnblogs.com/edisonchou/p/aspnet_core_mini_implemention_introduction.html让 .NET 轻松构建中间件模式代码让 .NET 轻松构建中间件模式代码二https://github.com/WeihanLi/SamplesInPractice/tree/master/MiniAspNetCore