ASP.NET Core框架学习
参考:
蒋金楠:
- 书籍《ASP.NET Core 3框架揭秘》
- ASP.NET Core 3框架揭秘
- 阅读了这三篇文章,你也就基本理解了ASP.NET Core MVC框架的工作原理
梁桐铭:书籍《深入浅出 ASP.NET Core》、 博客园:从零开始学 ASP.NET Core 与 EntityFramework Core
System.Configuration 命名空间 :包含提供用于处理配置数据的编程模型的类型
- Configuration 类 :表示适用于特定计算机、应用程序或资源的配置文件。 此类不能被继承
ASP.NET Core框架的本质
参考:200行代码,7个对象——让你了解ASP.NET Core框架的本质 –重点
第一个对象:HttpContext (Http上下文类)
HttpContext:封装有关单个HTTP请求的所有特定于HTTP的信息。
- HttpRequest:表示单个HTTP请求的传入端。
-
HttpResponse:表示单个HTTP请求的传出端。
- Headers:获取请求标头
- Body:获取或设置请求正文Stream
- StatusCode:获取或设置HTTP响应代码。
第二个对象:RequestDelegate(请求委托)
RequestDelegate:可以处理HTTP请求的功能。
第三个对象:Middleware(中间件)
中间件:中间件是一种装配到应用管道以处理请求和响应的软件。 每个组件:
- 选择是否将请求传递到管道中的下一个组件。
- 可在管道中的下一个组件前后执行工作。
中间件在ASP.NET Core被表示成一个Func<RequestDelegate, RequestDelegate>对象,也就是说它的输入和输出都是一个RequestDelegate。
第四个对象:ApplicationBuilder(应用程序建造者)
ApplicationBuilder:应用程序建造者
第五个对象:Server(服务)
IServer:代表服务器接口
第六个对象:WebHost(Web主机)
WebHost:提供方便的方法来创建具有预配置默认值的IWebHost和IWebHostBuilder实例
真实框架中是在Program类中调用ConfigureWebHostDefaults方法,此方法是用IHostBuilder接口声明的,此接口内有个 IHost 接口
第七个对象:WebHostBuilder(Web主机建造者)
WebHostBuilder: Web主机建造者
真实框架中是在Program类中调用ConfigureWebHostDefaults方法,此方法是用IHostBuilder接口声明的
asp.net core框架中的Http
Http基础
参考:
.NET Core中使用HttpClient的正确姿势(三种使用方式)
http是什么:
http是一个简单的请求-响应协议,它通常运行在TCP之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以ASCII码形式给出;而消息内容则具有一个类似MIME的格式。这个简单模型是早期Web成功的有功之臣,因为它使开发和部署非常地直截了当。
常用方法:GET 查询 、POST 修改、PUT 增加、DELETE 删除
http报文结构:
Request请求报文
-
Request line:请求行,由三部分组成:请求方法,请求URL(不包括域名)
- Request header:请求头部,由关键字/值对组成,每行一对,例如:
- blank line空行
- request-body请求数据:GET没有请求数据,POST有
具体如下
POST /user HTTP/1.1 //请求行 Host: www.user.com Content-Type: application/x-www-form-urlencoded Connection: Keep-Alive User-agent: Mozilla/5.0. //以上是首部行 (此处必须有一空行) //空行分割header和请求内容 name=world 请求体
相应报文
- 状态行:由三部分组成:服务器HTTP协议版本,响应状态码,状态码的文本描述
- 状态码:由3位数字组成,第一个数字定义了响应的类别
- 空行
- 响应体
MVC和WebApi的Http
他们两个的控制器都会继承
Razor的Http
类中继承
- PageModel,里面有
Blazor Server的Http
启动流程简单说明
参考:
ASP.NET Core管道详解[5]: ASP.NET Core应用是如何启动的?[上篇]
ASP.NET Core管道详解[6]: ASP.NET Core应用是如何启动的?[下篇]
流程简单说明如下:
- 创建asp.net core项目后默认是一个应用台控制程序,入口是Program类的Main方法
- 在Main方法内调用了一个创建主机的方法CreateHostBuilder,此方法做了下面两件事
- CreateDefaultBuilder:创建通用主机, (该方法属于Microsoft.Extensions.Hosting下的 Host 类)
-
ConfigureWebHostDefaults, (该方法属于Microsoft.Extensions.Hosting下的 GenericHostBuilderExtensions 类)
- 使用默认值配置IHostBuilder来托管Web应用程序,指定Startup类
- 创建web主机后,就有监控、请求、响应等功能,然后交给对应的中间件来处理
Program类代码如下:
public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); }
以上是流程简单说明,更详细看下面的主机、托管、启动类、配置等
主机
ASP.NET Core 应用在启动时构建主机, 主机是封装了应用的所有资源,例如:
- HTTP 服务器实现
- 中间件组件
- 日志
- 依赖关系注入 (DI) 服务
- 配置
有两种主机,推荐使用通用主机
创建主机
Host.CreateDefaultBuilder:创建通用主机,使用预配置默认值初始化 HostBuilder 类的新实例。
默认生成主机的设置 :
- 将内容根目录设置为由 GetCurrentDirectory 返回的路径。
-
通过以下项加载主机配置:
- 前缀为
DOTNET_
的环境变量。 - 命令行参数。
- 前缀为
-
通过以下对象加载应用配置:
- appsettings.json.
- appsettings.{Environment}.json。
- 应用在
Development
环境中运行时的用户机密。 - 环境变量。
- 命令行参数。
-
添加以下日志记录提供程序:
- 控制台
- 调试
- EventSource
- EventLog(仅当在 Windows 上运行时)
- 当环境为“开发”时,启用范围验证和依赖关系验证。
属于Microsoft.Extensions.Hosting下的Host类,重点看CreateDefaultBuilder方法,如下:
// Microsoft.Extensions.Hosting.Host using System.IO; using System.Reflection; using System.Runtime.InteropServices; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.EventLog; public static class Host { public static IHostBuilder CreateDefaultBuilder() { return CreateDefaultBuilder(null); } public static IHostBuilder CreateDefaultBuilder(string[] args) { HostBuilder hostBuilder = new HostBuilder(); hostBuilder.UseContentRoot(Directory.GetCurrentDirectory()); hostBuilder.ConfigureHostConfiguration(delegate(IConfigurationBuilder config) { config.AddEnvironmentVariables("DOTNET_"); if (args != null) { config.AddCommandLine(args); } }); hostBuilder.ConfigureAppConfiguration(delegate(HostBuilderContext hostingContext, IConfigurationBuilder config) { IHostEnvironment hostingEnvironment = hostingContext.HostingEnvironment; config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).AddJsonFile("appsettings." + hostingEnvironment.EnvironmentName + ".json", optional: true, reloadOnChange: true); if (hostingEnvironment.IsDevelopment() && !string.IsNullOrEmpty(hostingEnvironment.ApplicationName)) { Assembly assembly = Assembly.Load(new AssemblyName(hostingEnvironment.ApplicationName)); if (assembly != null) { config.AddUserSecrets(assembly, optional: true); } } config.AddEnvironmentVariables(); if (args != null) { config.AddCommandLine(args); } }).ConfigureLogging(delegate(HostBuilderContext hostingContext, ILoggingBuilder logging) { bool num = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); if (num) { logging.AddFilter<EventLogLoggerProvider>((LogLevel level) => level >= LogLevel.Warning); } logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); logging.AddConsole(); logging.AddDebug(); logging.AddEventSourceLogger(); if (num) { logging.AddEventLog(); } }).UseDefaultServiceProvider(delegate(HostBuilderContext context, ServiceProviderOptions options) { bool validateOnBuild = (options.ValidateScopes = context.HostingEnvironment.IsDevelopment()); options.ValidateOnBuild = validateOnBuild; }); return hostBuilder; } }
View Code
托管Web应用程序
属于Microsoft.Extensions.Hosting下的GenericHostBuilderExtensions类的方法,如下:
ConfigureWebHostDefaults (IHostBuilder, Action<IWebHostBuilder>):使用默认值配置IHostBuilder来托管Web应用程序,一般通过方法参数IWebHostBuilder.UseStartup指定启动类UseStartup
以下默认值应用于IHostBuilder:
- 从前缀为
ASPNETCORE_
的环境变量加载主机配置。 - 使用应用的托管配置提供程序将 Kestrel 服务器设置为 web 服务器并对其进行配置。
- 添加主机筛选中间件。
- 如果
ASPNETCORE_FORWARDEDHEADERS_ENABLED
等于true
,则添加转接头中间件。 - 支持 IIS 集成。
查看ConfigureWebHostDefaults方法的源码发现:方法内调用了Microsoft.AspNetCore下的WebHost.ConfigureWebDefaults方法
// Microsoft.Extensions.Hosting.GenericHostBuilderExtensions using System; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; public static class GenericHostBuilderExtensions { public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure) { return builder.ConfigureWebHost(delegate(IWebHostBuilder webHostBuilder) { WebHost.ConfigureWebDefaults(webHostBuilder); configure(webHostBuilder); }); } }
Microsoft.AspNetCore下的 WebHost 类的下的ConfigureWebDefaults方法源码,如下:
// Microsoft.AspNetCore.WebHost using System; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.HostFiltering; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.StaticWebAssets; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; internal static void ConfigureWebDefaults(IWebHostBuilder builder) { builder.ConfigureAppConfiguration(delegate(WebHostBuilderContext ctx, IConfigurationBuilder cb) { if (ctx.HostingEnvironment.IsDevelopment()) { StaticWebAssetsLoader.UseStaticWebAssets(ctx.HostingEnvironment, ctx.Configuration); } }); builder.UseKestrel(delegate(WebHostBuilderContext builderContext, KestrelServerOptions options) { options.Configure(builderContext.Configuration.GetSection("Kestrel")); }).ConfigureServices(delegate(WebHostBuilderContext hostingContext, IServiceCollection services) { services.PostConfigure(delegate(HostFilteringOptions options) { if (options.AllowedHosts == null || options.AllowedHosts.Count == 0) { string[] array = hostingContext.Configuration["AllowedHosts"]?.Split(new char[1] { \';\' }, StringSplitOptions.RemoveEmptyEntries); options.AllowedHosts = ((array != null && array.Length != 0) ? array : new string[1] { "*" }); } }); services.AddSingleton((IOptionsChangeTokenSource<HostFilteringOptions>)new ConfigurationChangeTokenSource<HostFilteringOptions>(hostingContext.Configuration)); services.AddTransient<IStartupFilter, HostFilteringStartupFilter>(); if (string.Equals("true", hostingContext.Configuration["ForwardedHeaders_Enabled"], StringComparison.OrdinalIgnoreCase)) { services.Configure(delegate(ForwardedHeadersOptions options) { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; options.KnownNetworks.Clear(); options.KnownProxies.Clear(); }); services.AddTransient<IStartupFilter, ForwardedHeadersStartupFilter>(); } services.AddRouting(); }).UseIIS() .UseIISIntegration(); }
View Code
启动类Startup
启动类中就是两个方法,一个是依赖注入和服务注册ConfigureServices,一个是中间件配置Configure
- ConfigureServices (IServiceCollection services):依赖注入和服务注册,例如数据库上下文、控制器、其他的依赖注入等
- Configure (IApplicationBuilder app, IWebHostEnvironment env):用于指定应用响应 HTTP 请求的方式。 可通过将中间件组件添加到 IApplicationBuilder 实例来配置请求管道,例如路由、认证、跨越等
生成项目的Startup 类的代码一般如下:
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace WebRazor { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); }); } } }
View Code
配置
概念
ASP.NET Core 中的配置是使用一个或多个配置提供程序执行的。 配置提供程序使用各种配置源从键值对读取配置数据:
- 设置文件,例如 appsettings.json
- 环境变量
- Azure Key Vault
- Azure 应用程序配置
- 命令行参数
- 已安装或已创建的自定义提供程序
- 目录文件
- 内存中的 .NET 对象
读取自定义的Json配置文件
参考:文件配置提供程序
在Program类的后面添加ConfigureAppConfiguration,红色的代码,如下:
备注:可以查看上面创建主机的方法CreateDefaultBuilder的源码,里面就有这个 ConfigureAppConfiguration的方法的具体实现。
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }) .ConfigureAppConfiguration((hostingContext, config) => { config.AddJsonFile("appsettings22.json", optional: true, reloadOnChange: true); });
模型验证
参考:模型验证
认证与授权
参考: 关于WEB Service&WCF&WebApi实现身份验证之WebApi篇
认证
全局认证
单页面SAP认证
使用 SPA 标识–.net core3.0起
授权
角色授权
策略授权
ASP.NET Core 中的区域Areas
文件
参考:
jQuery实现jQuery-form.js实现异步上传文件
.NET和.NET Core Web APi FormData多文件上传对比
上传方式
-
表单提交
-
FormData
上传文件时同时提交数据
- js做两个提交,提交数据后再上传文件(简单,推荐)
- 使用layui的上传组价
- 使用jqeury.Form.js
ASP.NET Core MVC
工作原理:
启动入口
Global.asax ==》Startup.cs
Global.asax 下面是一个MvcApplication类集成,该类继承System.Web.HttpApplication,类下面是一个Application_Start方法,Application_Start方法中包含注册区域、注册全局的Filter、注册路由、合并压缩、打包工具Combres。
HttpApplication类:定义对 ASP.NET 应用程序内所有应用程序对象公用的方法、属性和事件。 此类是用户在 Global.asax 文件中定义的应用程序的基类
简单的流程
用户请求是通过【视图V】进入==》通过【控制器C】处理==》从【模型M】获取数据==》最后返回给【视图V】。
也知道ASP.NET MVC中的约定可以通过修改配置来修改。
疑问
控制器、视图、模型是怎么根据约定来传递数据的
当时有一个疑问困惑好久,就是数据是怎么通过控制器中发送到视图的?
虽然知道能根据控制器内的方法来识别对应的视图名称,但是控制器方法的return是怎么把模型数据发送到视图的@model,当时记得自己折腾好久都没整明白,百度搜索找到很少相关,最后看到《ASP.NET MVC 5框架揭秘》这本书才知道是和视图引擎ViewEngine有关,视图引擎会把控制器绑定的数据和视图关联起来
MVC中的http
是怎么使用http通信的???
修改约定
只搜索cshtml,默认会搜索多种视图文件
在Global.asax中增加:
/配置视图搜索位置、文件类型,以提高性能
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomLocationViewEngine());
然后把CustomLocationViewEngine.cs文件放到App_Start中
配置类/注册类:
Global.asax 下面类的方法内的注册信息是怎么被框架加载识别的?
ps:框架已经锁定好MvcApplication、Application_Start的名称的了,如果更改名称就会报错,也就是说只要在框架制定的方法中添加方法,框架在第一次启动时会自动加载Application_Start内的方法。
IIS 服务器
IIS基础
参考:
使用 IIS 在 Windows 上托管 ASP.NET Core
IIS运行原理 + 三个重要组件
-
协议侦听器(HTTP.sys)
- 协议侦听器接收特定于协议的请求,将其发送到IIS进行处理,然后将响应返回给请求者。例如,当客户端浏览器从Internet请求网页时,HTTP侦听器HTTP.sys接收请求并将其发送到IIS进行处理。IIS处理请求后,HTTP.sys将响应返回到客户端浏览器。
- 协议侦听器接收特定于协议的请求,将其发送到IIS进行处理,然后将响应返回给请求者。例如,当客户端浏览器从Internet请求网页时,HTTP侦听器HTTP.sys接收请求并将其发送到IIS进行处理。IIS处理请求后,HTTP.sys将响应返回到客户端浏览器。
-
万维网发布服务(WWW服务)
-
在IIS中,WWW服务不再管理工作进程。而是,WWW服务是HTTP侦听器HTTP.sys的侦听器适配器。作为侦听器适配器,WWW服务主要负责配置HTTP.sys,在配置更改时更新HTTP.sys以及在请求进入请求队列时通知WAS。
此外,WWW服务继续收集网站的计数器。由于性能计数器仍然是WWW服务的一部分,因此它们是HTTP特定的,不适用于WAS。
-
- Windows进程激活服务(WAS)
-
WAS中的配置管理:
-
在启动时,WAS从ApplicationHost.config文件中读取某些信息,并将该信息传递到服务器上的侦听器适配器。侦听器适配器是在WAS和协议侦听器(例如HTTP.sys)之间建立通信的组件。侦听器适配器收到配置信息后,便会配置其相关的协议侦听器,并准备使侦听器侦听请求。
对于WCF,侦听器适配器包括协议侦听器的功能。因此,将基于WAS的信息配置WCF侦听器适配器(例如NetTcpActivator)。配置NetTcpActivator后,它将侦听使用net.tcp协议的请求。有关WCF侦听器适配器的更多信息,请参见MSDN上的WAS激活体系结构。
-
-
WAS流程管理:
- WAS管理HTTP和非HTTP请求的应用程序池和辅助进程。当协议侦听器接收到客户端请求时,WAS将确定工作进程是否正在运行。如果应用程序池已经具有正在处理请求的工作进程,则侦听器适配器将请求传递到工作进程进行处理。如果应用程序池中没有工作进程,则WAS将启动一个工作进程,以便侦听器适配器可以将请求传递给它进行处理。
-
项目部署到 IIS 步骤
安装.net core托管捆绑包,下载对应版本:当前 .NET Core 托管捆绑包安装程序(直接下载)
双击应用程序成,改为:无托管代码
如果是64位的:鼠标右键=》设置应用程序池默认设置,启用32位应用程序这里要改为Lalse
标识设置为有权限的账号
IIS 调式
IIS调式有两种方式
- VS自带IIS服务调式
- 在项目中直接使用IISWEB服务器调式
用VS自带IIS调式时给其他电脑访问
参考:解决报错HTTP Error 400. The request hostname is invalid
- 显示隐藏文件,在项目根目录下找到找到.vs\ config\ applicationhost.config
- 用vs打开该文件,找到web项目名称的节点的bindings,复制带有localhost的binding节点,然后把localhost改为本机IP地址
- 项目启动后检查IIS服务的网站
- 关闭本机电脑防火墙,或者在入站规则设置好端口
Kestrel 服务器
参考: 官方文档Kestrel
http证书
要用dotnet run启动项目
注意:此方法只是http能使用,而且需要网卡在使用的状态(因为是),https还是不能使用,还需要继续深入了解https
在项目文件下用命令(dotnet run)启动时,大部分浏览器(IE不会)会报错:ERR_HTTP2_INADEQUATE_TRANSPORT_SECURITY
端口要根据实际设置,可以查看Properties文件夹下的文件launchSettings.json
Program类要添加下面红色代码,这里只是端口的方式,还有更多方式(如TCPSocket、Limits等),可以参考官方文档,
或者参考官方教程的示例代码,文件夹路径:aspnetcore\fundamentals\servers\kestrel\samples\3.x\KestrelSample
端口如果不设置可以用0,就是随机端口
using System.Net; //要引用的命名空间 public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>() .ConfigureKestrel(serverOptions => { serverOptions.Listen(IPAddress.Loopback, 5001); //端口为0就是随机端口 }); });
然后项目文件的地址栏中输入cmd进入命令窗口,然后通过命令启动项目:dotnet run,注入,不能直接用vs启动,否则还是不能访问
然后只能用红色框内的地址访问:http://127.0.0.1:5001/,(注意:用本机的https://localhost访问还是会失败)
发布后的生产环境
好像说需要安装https证书,具体还要验证。
不过按上面设置后,发布后也能正常使用,安全问题???
视图组件
参考:
视图组件封装:.Net MVC&&datatables.js&&bootstrap做一个界面的CRUD有多简单–邹琼俊
测试、调试和疑难解答
.NET中的测试
Razor Pages 单元测试
测试控制器
测试中间件
远程调试
快照调试
Visual Studio 中的快照调试
集成测试
负载测试和压力测试
故障排除和调试
Logging
Azure 和 IIS 疑难解答
Azure 和 IIS 错误参考