直接使用Visual Studio的新建项目–Web–Net
.net core
跨平台,跨架构(X86/X64/ARM),支持命令行,部署灵活,兼容性强(向下),开源
.net framework
.net core 3.1是FW4.8的下一代,3.1的下一版是 .NET 5.0
.net core 3.X
支持winform和WPF(不是很完善)
ASP.NET CORE 3.0
EF CORE 3.0
云原生和微服务 NC的核心
体积小,速度快,占用少,水平扩展
3.1:
适合WEB开发
ASP.NET CORE 3.1
启动流程,主机配置,依赖注入,服务,中间件,应用配置,多环境,日志,路由,异常处理,静态文件,部署等。
是ASP.NET的重新设计,体系结构已经改变,是以后的核心。
新功能:
Blazor
gRPC,高性能远程过程调用框架,微服务间的内部调用
开发环境 VS2019 16.4
主机:
负责web应用程序的启动和生产周期管理,配置服务器和请求处理
主机配置日志
主机是封装了应用资源的对象
创建主机生产器>配置主机>创建主机>运行主机
Kestrel:
跨平台的适用与ASP.NET的WEB服务器,角色类似IIS,但不是IIS
HTTP/HTTPS
功能少,不支持反向代理
配置主机:
组件配置
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
// 配置web主机
// 启用Kestrel
// 启用IIS集成
// 环境变量(DOTNET开头)
// 加载命令行参数
// 加载应用配置
// 配置默认的日志组件
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
//组件配置 Config开头
webBuilder.ConfigureKestrel((context,options)=>options.Limits.MaxRequestBodySize=20480000);//20M
//webBuilder.ConfigureLogging((context, builder) => { builder.});
// 主机配置项,Use开头的函数
webBuilder.UseStartup<Startup>();
// 环境变量<硬编码<应用配置<命令行
webBuilder.UseUrls("http://*:8081"); // 硬编码
});
}
Program.cs
webBuilder.UseUrls("http://*:8081"); // 硬编码
// 配置WEB应用所需要的服务和中间件
public class Startup
{
// 可选的, 可注册的 服务
public void ConfigureServices(IServiceCollection services)
{
// 服务容器
// IoC容器【控制反转Inversion of Control】【升级版的工厂模式,映射依赖/管理对象的生成和生存周期】
// 注册类型,请求实例,功能解析【类所依赖的对象】
// 目的为了:依赖注入【一系列的工具和手段,目的是松耦合,可维护/可测试的代码和程序】
// 默认已经为我们注册了一些服务,所以称为服务容器
// 添加对控制器和API相关功能的支持,但是不支持视图和页面
services.AddControllers();
// 添加对控制器/API/视图相关功能的支持 ASP.NET CORE 3.X MVC模板默认使用
services.AddControllersWithViews();
// 这里是APS.NET CORE 2.X
services.AddMvc();
//跨域
services.AddCors();
// 以上都是内置的服务,还可以添加第三方的 EF CORE/日志框架/Swagger
// 注册自定义服务
// 服务生存期 类型生成期
// 注册自定义的服务时,必须要选择一个生存周期
// 有几种生存周期,瞬时/作用域/线程单例/全局单例
// services.AddSingleton(); // 注册单例
// services.AddSingleton(); // 注册单例 ,一个一个注册,也可以批量
// 服务,生存期,配置
// 自带的不好用,可以换成第三方的
// 依赖注入框架很多:第三方:Unity/Autofac/Ninject/
// 自定义的服务
// 比较规范的封装,
//扩展类和方法,参数带Action<类>,类构造函数(services)执行,类内定义各种执行函数
services.AddMessage(builder => builder.UserEmail());
}
// 配置中间件, 中间件组成管道
// 必须的,中间件就是处理HTTP请求和响应的
// 管道的源码,模拟实现管道
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); });
});
}
}
app.UseHttpsRedirection();
app.UseStaticFiles(); // 启用静态文件 中间件
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); });
});
Services> MyMessage
public interface IMyMessage
{
void send();
}
public class MyEmailMessage:IMyMessage
{
public void send()
{
// 这是邮件服务
}
}
public class MySmsMessage:IMyMessage
{
public void send()
{
// 这是短信发送服务
}
}
Extensions
public class MyMessageServiceExtension
{
public static void AddMessage(
this IServiceCollection services,
Action<MyMessageServiceBuilder> config
)
{
//services.AddSingleton<>();
var builder = new MyMessageServiceBuilder(services);
config(builder);
}
}
public class MyMessageServiceBuilder
{
public IServiceCollection ServiceCollection { get; set; }
public MyMessageServiceBuilder(IServiceCollection services)
{
ServiceCollection = services;
}
public void UserEmail()
{
ServiceCollection.AddSingleton<IMyMessage, MyEmailMessage>();
}
public void UserSms()
{
ServiceCollection.AddSingleton<IMyMessage, MySmsMessage>();
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace CorsDomainDemo
{
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.AddCors(options =>
//options.AddPolicy("cors",
//p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials()));
//允许一个或多个来源可以跨域
//services.AddCors(options =>
//{
// options.AddPolicy("CustomCorsPolicy", policy =>
// {
// // 设定允许跨域的来源,有多个可以用','隔开
// policy.WithOrigins("http://localhost:21632", "http://localhost:24661")
// .AllowAnyHeader()
// .AllowAnyMethod()
// .AllowCredentials();
// });
//});
// 读取配置文件内容
OptionConfigure(services);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IOptions<CorsOptions> corsOptions)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
// 允许所有跨域,cors是在ConfigureServices方法中配置的跨域策略名称
//app.UseCors("cors");
// 设定特定ip允许跨域 CustomCorsPolicy是在ConfigureServices方法中配置的跨域策略名称
//app.UseCors("CustomCorsPolicy");
// 设置允许所有来源跨域
//app.UseCors(options =>
//{
// options.AllowAnyHeader();
// options.AllowAnyMethod();
// options.AllowAnyOrigin();
// options.AllowCredentials();
//});
// 设置只允许特定来源可以跨域
//app.UseCors(options =>
//{
// options.WithOrigins("http://localhost:3000", "http://127.0.0.1"); // 允许特定ip跨域
// options.AllowAnyHeader();
// options.AllowAnyMethod();
// options.AllowCredentials();
//});
// 利用配置文件实现
CorsOptions _corsOption = corsOptions.Value;
// 分割成字符串数组
string[] hosts = _corsOption.url.Split('|');
// 设置跨域
app.UseCors(options =>
{
options.WithOrigins(hosts);
options.AllowAnyHeader();
options.AllowAnyMethod();
options.AllowCredentials();
});
app.UseHttpsRedirection();
app.UseMvc();
}
private void OptionConfigure(IServiceCollection services)
{
services.Configure<CorsOptions>(Configuration.GetSection("AllowedHosts"));
}
}
}
public void ConfigureServices(IServiceCollection services)
{
// 配置跨域处理,允许所有来源
services.AddCors(options =>
options.AddPolicy("cors",
p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials()));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IOptions<CorsOptions> corsOptions)
{
// 允许所有跨域,cors是在ConfigureServices方法中配置的跨域策略名称
app.UseCors("cors");
app.UseHttpsRedirection();
app.UseMvc();
}
public void ConfigureServices(IServiceCollection services)
{
//允许一个或多个来源可以跨域
services.AddCors(options =>
{
options.AddPolicy("CustomCorsPolicy", policy =>
{
// 设定允许跨域的来源,有多个可以用','隔开
policy.WithOrigins("http://localhost:21632", "http://localhost:24661")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IOptions<CorsOptions> corsOptions)
{
// 设定特定ip允许跨域 CustomCorsPolicy是在ConfigureServices方法中配置的跨域策略名称
//app.UseCors("CustomCorsPolicy");
app.UseHttpsRedirection();
app.UseMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IOptions<CorsOptions> corsOptions)
{
// 设置允许所有来源跨域
//app.UseCors(options =>
//{
// options.AllowAnyHeader();
// options.AllowAnyMethod();
// options.AllowAnyOrigin();
// options.AllowCredentials();
//});
// 设置只允许特定来源可以跨域
//app.UseCors(options =>
//{
// options.WithOrigins("http://localhost:3000", "http://127.0.0.1"); // 允许特定ip跨域
// options.AllowAnyHeader();
// options.AllowAnyMethod();
// options.AllowCredentials();
//});
app.UseHttpsRedirection();
app.UseMvc();
}
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": {
"url": "http://localhost:21632|http://localhost:24663"
}
}
StartUp.cs
public void ConfigureServices(IServiceCollection services)
{
// 读取配置文件内容
OptionConfigure(services);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IOptions<CorsOptions> corsOptions)
{
// 利用配置文件实现
CorsOptions _corsOption = corsOptions.Value;
// 分割成字符串数组
string[] hosts = _corsOption.url.Split('|');
// 设置跨域
app.UseCors(options =>
{
options.WithOrigins(hosts);
options.AllowAnyHeader();
options.AllowAnyMethod();
options.AllowCredentials();
});
app.UseHttpsRedirection();
app.UseMvc();
}
private void OptionConfigure(IServiceCollection services)
{
services.Configure<CorsOptions>(Configuration.GetSection("AllowedHosts"));
}
public IActionResult GetWorker(string workNo){
Response.Headers.Add("Access-Control-Allow-Origin", "*"); // 用于回传跨域的头
return Json(new{Msg="OK",data=[]});
}
public void ConfigureServices(IServiceCollection services)
{
// 跨域
services.AddCors(options =>
{
options.AddPolicy("any", builder =>
{
builder.WithMethods("GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS")
//.AllowCredentials()//指定处理cookie
.AllowAnyOrigin(); //允许任何来源的主机访问
});
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IOptions<CorsOptions> corsOptions)
{
app.UseAuthorization();
// 使用跨域,要在app.UseAuthorization() 后面
app.UseCors("any");
}
// 设置跨域
app.UseCors(options =>
{
//options.WithOrigins("http://*", "*").AllowAnyHeader().AllowAnyMethod().AllowCredentials(); // 允许特定ip跨域
options.SetIsOriginAllowed(_=>true).AllowAnyHeader().AllowAnyMethod().AllowCredentials(); // 允许所有ip
});
ASP.NET Core 路由,认证,会话,缓存,都是有管道来处理,中间, MVC webapi 都是建立在某个特殊的中间件之上。
MVC 路由的中间件,请求地址和控制器之间的映射,实例化控制器,执行Action,以及渲染View等一些列功能
编写中间件,扩展请求管道,可以在ASP.NET Core的基础之上,创建自己的WEB框架。
两个职责:
1.选择是否将请求传递到管道的下一个中间件
2.在管道中的下一个中间件的前后执行工作
每一个中间件都有权力做出决定是否将请求传递到下一个中间件,也可以直接做出响应,促使管道短路,不再向下传,结束请求,直接响应,原路返回,层层。
ASP.NET MVC 过滤器:
权限验证,过滤器和中间件很相似?
都是AOP(面向切面编程)的产物,定位和关注点不一样
过滤器:如何实现功能,是功能。【是中间件的好处之一】
中间件:管道模型中的重要组成部分。担负的是整个管道的请求到相应的处理流程。
布置管道:
在管道里布置中间件。
// 配置中间件, 中间件组成管道
// 必须的,中间件就是处理HTTP请求和响应的
// 管道的源码,模拟实现管道
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// IApplicationBuilder app 用于添加中间件到管道
// Use( 委托 ) 有next,表示可以传到下一个中间件
app.Use(async (context, next) =>
{
await context.Response.WriteAsync("Hello Middleware");
await next(); // 执行下一个中间件
await context.Response.WriteAsync("Hello Middleware end"); // 执行完下一个中间件回来的后再执行
});
// app.Run( Action) 中间件 就是一个委托,终端中间件,一般放在最后;如果没有Run则会返回错误
app.Run(async context => { await context.Response.WriteAsync("Hello"); });
}
一般将中间件封装起来,不使用 Use和Run在配置内编写。
中间件是有顺序的。
添加中间件的顺序就是调用中间件的顺序。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// 环境名称 Development
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage(); // 开发人员异常页面中间件
}
// 终结点(端点) 路由中间件, 用于路由请求匹配
// ASP.NET CORE 2.X 没有这个
// ASP.NET CORE 3.X
app.UseRouting(); //
// 还可以添加一些其他的中间件
// 终结点中间件 必须要与 UseRouting(),成对出现, 用于路由请求匹配的配置
// 是配置,配置中间件和路由器的之间关系,映射
// 终结点中间件 可以理解为 MVC的 /控制器/action
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); });
});
}
约定:
具有类型RequestDelegate的参数公共构造函数
名为 invoke 或者 invokeAsync 的方法。
public class MyTestMiddleware
{
private RequestDelegate _next;
public MyTestMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvorkAsync(HttpContext httpContext)
{
// 在这里写中间件的业务代码
// HTTP请求处理
await _next(httpContext);
// HTTP 响应处理
}
}
自定义中间件使用:
app.UseMiddleware<MyTestMiddleware>();
或者使用扩展类方法
public static class CustonMiddlewareExtensions
{
public static IApplicationBuilder UseMyTestMiddleware(this IApplicationBuilder app){
return app.UseMiddleware<MyTestMiddleware>();
}
}
// StartUp 内使用 app.UseMyTestMiddleware();
ASP.NET Core 源代码:
GitHub Clone下来
双击运行 restore.cmd, 命令行文件运行,还原源代码环境
没有提供整个解决文案的sln
SRC文件夹,【http】文件夹内源码,管道。
先运行 startvs.cmd , 再打开sln
默认为“wwwroot”文件夹,存在Project/wwwroot
修改配置文件夹名:
program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
//webBuilder.UseUrls("http://127.0.0.1:8099");
//webBuilder.UseContentRoot("public"); // 修改静态文件夹名称
});
Startup.cs
app.UseHttpsRedirection();
app.UseStaticFiles(); // 启用静态文件 中间件
app.UseRouting();
app.UseAuthorization();
Nuget 包管理器(.NET)
ASP.NET MVC 捆绑和压缩
JS和CSS,请求,
捆绑,可以把一堆JS绑到一个文件里
1.优化系统,Nuget 安装: BuildBundlerMinifier
2.新增一个** bundleconfig.json**
捆绑+压缩:
[
{
"outputFileName":"wwwroot/css/site.css",
"inputFiles":[
"wwwroot/site.css",
"wwwroot/index.css"
]
}
]
捆绑,不压缩:
{
"outputFileName":"wwwroot/css/site.css",
"inputFiles":[
"wwwroot/site.css",
"wwwroot/index.css"
],
"minify":{
"enabled":false
}
}
NuGet安装 Microsoft.Extensions.Http.Polly 扩展:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient(); // 注册IHttpClientFactory
}
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
// using System.Text.Json; // 使用 Objs = await JsonSerializer.DeserializeAsync>(responseStream);
namespace Helper
{
/*
使用:
*/
public class HttpClientHelper
{
private static readonly object LockObj = new object();
private static HttpClient client;
public string Msg{get;private set;} // 返回信息
public HttpClientHelper()
{
GetInstance();
this.Msg="OK";
}
public static HttpClient GetInstance()
{
if (client == null)
{
lock (LockObj)
{
if (client == null)
{
client = new HttpClient();
}
}
}
return client;
}
///
/// 基础调用,所有方法都最终调用此函数
///
public async Task<HttpResponseMessage> PostAsync(Func<HttpResponseMessage> func)
{
try
{
return await Task.Run(func);
}
catch(Exception ex)
{
string str = ex.StackTrace;
Msg = "Error : "
+ str.Substring(str.LastIndexOf("\\") + 1, str.Length - str.LastIndexOf("\\") - 1)
+ "--" + ex.Message;
return new HttpResponseMessage(){
Content = new StringContent(Msg)
};
}
}
public async Task<HttpResponseMessage> PostMultiFDAsync(string url,Dictionary<string,string> contents)
{
return await PostAsync(()=>{
using(var formDC = new MultipartFormDataContent()){
foreach(string key in contents.Keys){ // 添加请求的内容参数
formDC.Add(new StringContent(contents[key]),key);
}
return client.PostAsync(url,formDC).Result;
}
});
} // end OnGet
public async Task<HttpResponseMessage> PostUrlFDAsync(string url,Dictionary<string,string> contents)
{
return await PostAsync(()=>{
var ls = new List<KeyValuePair<string, string>>();
foreach(string key in contents.Keys){ // 添加请求的内容参数
ls.Add(new KeyValuePair<string, string>(key,contents[key]));
}
HttpContent hc = new FormUrlEncodedContent(ls);
return client.PostAsync(url,hc).Result;
});
}
public async Task<HttpResponseMessage> GetAsync(string url){
try
{
var response = await client.GetAsync(url); // 客户端发送请求
Msg =await response.Content.ReadAsStringAsync();
return response;
}catch(Exception ex)
{
string str = ex.StackTrace;
Msg = "Error : "
+ str.Substring(str.LastIndexOf("\\") + 1, str.Length - str.LastIndexOf("\\") - 1)
+ "--" + ex.Message;
return new HttpResponseMessage(){
Content = new StringContent(Msg)
};
}
}
}
}
using System;
using System.Collections.Generic;
namespace Helper
{
public class SWEmailHelper
{
#region 邮件预警
public static string SendEmail(string MAIL_URL, Dictionary<string,string> dic)
{
// return WCF_Use_Helper.WCF_SDTEmail_Helper.SendMail(to, cc, subject, content);
var caller = new HttpClientHelper();
var ret = caller.PostMultiFDAsync(MAIL_URL,dic);
return ret.Result.Content.ReadAsStringAsync().Result;
}
public static string MailMsgMaker(string date,string msg,string sysUrl,string sysName)
{
string txt = @"
日期 提示信息 网址
"+ date + " "+msg+ " "+sysName+"
";
return txt;
}
public static string SendEmail_main(string emailAPIUrl,List<string> tos,List<string> ccs, string title, string msg,string sysUrl,string sysName)
{
string to = "";
string cc = "";
foreach(var s in tos){
to+=s+";";
}
foreach(var s in ccs){
cc+=s+";";
}
string content = MailMsgMaker(DateTime.Now.ToString("yyyy-MM-dd"),msg,sysUrl,sysName);
content = Convert.ToBase64String(System.Text.UTF8Encoding.UTF8.GetBytes(content)); //内容是html,有非法字符,需要转化后传输
Dictionary<string,string> dic = new Dictionary<string, string>();
dic.Add("to",to);
dic.Add("cc",cc);
dic.Add("title",title);
dic.Add("content",content);
var rlt = SendEmail(emailAPIUrl,dic);
return rlt;
}
#endregion
}
}
【发布】–【框架依赖/独立】【可移植/Linux/Win64/win86】
【框架依赖】【可移植】
依赖系统以安装的.NET Core库,包含自己的代码和第三方的依赖项
包含.dll,dotnet执行
优点:
不必先预先定义应用运行的目标操作系统,生产的可执行文件,通用的PE文件格式,.NET Core都可以执行
部署包很小
降低磁盘控件
如果运行时更新了,只需要更新操作系统
缺点:
系统上.NET Core版本>=应用的版本
【独立】【Linux/Win64/win86】
不依赖系统的.NET Core
代码+第三方库+.NET Core库+可执行文件.EXE/.DLL
优点:
保证应用是可执行的
缺点:
不可移植,只能对应的目标系统
文件较大
【框架依赖】【Linux/Win64/win86】
依赖系统的 .NET Core , 只能运行在特定的系统。
https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/publish-to-iis?view=aspnetcore-5.0&tabs=visual-studio
IIS需要先安装 AspNetCoreModule,下载链接
进程管理器(IIS/windows服务),收到请求的时候启用应用,并且在应用发生故障时负责重启
性能比进程外要高,直接把请求给应用(不使用kestrel)
通过反向代理将请求转发给应用
多了一层转发,环回适配器(网络接口,)
ASP.NET Core Kestrel 只是性能高,但 功能太弱,不易暴露出去
执行文件夹的web.config:
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<aspNetCore ... hostingModel="inprocess" />
system.webServer>
location>
configuration>