ASP.NET Core与RESTful API学习笔记 三、ASP.NET Core核心特性
启动与宿主
应用程序的启动
当ASP.NET Core应用程序启动时,首先配置并运行其宿主(Host),宿主启动、初始化应用程序,并管理其生命周期。
/// <summary>
/// Porgram类是ASP.NET Core应用程序的入口
/// </summary>
public class Program
{
/// <summary>
/// 程序启动时,从Porgram类的Main方法开始执行
/// </summary>
/// <param name="args"></param>
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
// 配置应用程序的启动
webBuilder.UseStartup<Startup>();
});
}
调用WebHost类的CreateDefaultBuilder()方法,会返回IWebHostBuilder类型的对象包含的主要默认选项如下:
- 配置Kestrel服务器为默认Web服务器负责处理Web请求和响应;
- 使用当前目录作为应用程序的内容目录(ContentRoot),应用程序在内容目录中查找内容文件;
- 从以ASPNETCORE_开头的环境变量(如ASPNETCORE_ENVIRONMENT)中以及命令行参数中加载配置项;
- 从appsettings.json、appsettings.{Environment}.json、用户机密(仅开开发环境)、环境变更和命令行参数等位置加载应用配置;
- 配置日志功能,默认添加控制台输出与调试输出;
- 如果应用程序被托管在IIS中,启动IIS集成,它会配置应用程序的主机地址和端口,并允许捕获启动错误等。
CreateDefaultBuilder()方法中所包含的默认配置能够通过IWebHostBuilder接口提供的扩展方法进处修改或增加,如ConfigureAppConfiguration、ConfigureKestrel、ConfigureLogging、UseEnviroment、UseContentRoot和UseUrls等。
Kestrel
Kestrel是轻量级、托管的、开源且跨平台的Web服务器。作为ASP.NET Core的组成部分,能够使ASP.NET Core应用程序运行在任何平台(如Windwos和Linux)。
如上图所示,Kestrel服务器会在ASP.NET Core的进程内运行,负责处理HTTP请求与响应。
在实际生产环境部署应用程序时,可以使用主流的Web服务器(如IIS、Apache、Nginx等)放在Kestrel之前作为反向代理服务器,使HTTP请求与响应经过并由反向代理服务器再传给Kestrel服务器,这种方式增加了应用程序的安全性,也可提供负载均衡,过滤请求和URL重定向等功能。如下图所示:
Startup类
IWebHostBuilder接口有多个扩展方法,UseStartup()方法其中很重要的一个,它主要向应用程序提供用于配置启动的类,而指定的这个类应具有以下两个方法:
- ConfigureServices():用于向ASP.NET Core的依赖注入容器添加服务;(可选)
- Configure():用于添加中间件,配置请求管道。(必选)
这两个方法都会在运行时被调用,且在应用程序生命周期内只执行一次。在程序启动时,先执行ConfigureServices()方法(如果有),再执行Configure()。
以下是一个典型的Startup类:
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.AddControllers();
}
// 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();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
中间件
中间件(Middleware)简介
链接转载 中间件详解及项目实战
中间件就是处理HTTP请求和响应的组件,本质上就是一段用来处理请求和响应的代码。多个中间件之间的链式关系使之形成了管道(Pipeline)。请求按顺序通过管道由每一个中间件处理,而对于响应也会遍历进来时所经过的中间件,顺序与请求相反。工作原理如下图所示:
ASP.NET Core中内置了多个中间件,主要包括MVC、认证、错误、静态文件、HTTPS重定向和跨域资源共享(Cross-Origin Resource Sharing,CORS)等,也允许向管道中添加自定义中间件。
添加中间件
上一节提到Startup类的Configure()方法,就是添加中间件的地方。通过调用IApplictionBuilder接口中以Use开头的扩展方法,即可添加内置的中间件。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseExceptionHandler("/Home/Error");
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
上述代码中的每个以Use开头的方法会逐一并顺序地向管道添加相应的中间件,中间件的添加顺序将决定HTTP响应遍历它们的顺序。每一个中间件都可以终止请求管道。
除了使用内置的中间件外,也可以通过IApplictionBuilder接口的Use和Run方法添加中间件,如下示例使用Use方法和Run方法添加了两个中间件:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
//省略
#region "使用Use方法添加一个中间件"
app.Use(async (context, next) =>
{
var timer = System.Diagnostics.Stopwatch.StartNew();
Console.WriteLine("----中间件A:开始, {0}----", timer.ElapsedMilliseconds);
await next();
Console.WriteLine("----中间件A:结束, {0}---", timer.ElapsedMilliseconds);
});
#endregion
#region "使用Run方法添加一个中间件"
app.Run(async (context) =>
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("---- REQUEST ----");
sb.AppendLine($"Host:{context.Request.Host}");
sb.AppendLine($"Method:{context.Request.Method}");
sb.AppendLine($"Path:{context.Request.Path}");
sb.AppendLine($"Protocol:{context.Request.Protocol}");
foreach (var item in context.Request.Headers)
{
sb.AppendLine($" {item.Key}:{item.Value}");
}
await Task.Delay(800);
await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(sb.ToString()), 0, sb.Length);
});
#endregion
}
与Run方法不同的是,Use方法在处理完请求还会将请求传入下一个中间件,并由它继续处理。
app.Run(async (context) => {...});
app.Use(async (context, next) => {...});
除了Run和Use方法外,IApplicationBuilder接口还提供了Map、MapWhen、UseWhen方法,它们可以指定条件,并在条件满足时创建新的分支管道,同时在新的分支上添加执行中间件。UseWhen创建的分支在执行完后会继续回到原来的管道上,而Map、MapWhen则不会。 Map会根据是否匹配指定的请求路径来决定是否在一个新的分支上继续执行后续的中间件,并且在新分支上执行完成后,不再回到原来的管道上。
自定义中间件
自定义中间件需要至少一个特定的构造函数和一个名为Invoke的方法。如下实现了一个让应用程序仅接受GET和POST方法的中间件:
// 过滤应用程序仅接受GET和POST方法的中间件
public class HttpMethodFilterMiddleware
{
// 表示管道中的下一个中间件
private readonly RequestDelegate _next;
public HttpMethodFilterMiddleware(RequestDelegate requestDelegate, IHostingEnvironment environment)
{
this._next = requestDelegate;
}
public Task Invoke(HttpContext context)
{
var requestMethod = context.Request.Method.ToUpper();
if (requestMethod == HttpMethods.Get || requestMethod == HttpMethods.Post)
{
return this._next(context);
}
else
{
context.Response.StatusCode = 400;
context.Response.Headers.Add("X-AllowHTTPVerb", new[] { "GET,POST" });
string s = "仅允许 GET、POST 方法";
byte[] sByte = Encoding.UTF8.GetBytes(s.ToString());
context.Response.Body.WriteAsync(sByte, 0, sByte.Length);
return Task.CompletedTask;
}
}
}
接下来在Startup类的Configure()方法中添加中间件:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// 添加HTTP方法过滤中间件
app.UseMiddleware<HttpMethodFilterMiddleware>();
..省略..
}
为了方便的使用自定义中间件,可以为它创建一个扩展方法:
// 自定义中间件使用扩展方法类
public static class CustomMiddlewareExtensions
{
// HttpMethodFilterMiddleware自定义中间件的使用扩展方法
public static IApplicationBuilder UseHttpMethodFilterMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<HttpMethodFilterMiddleware>();
}
}
使用扩展方法添加中间件:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// 添加HTTP方法过滤中间件
app.UseHttpMethodFilterMiddleware();
..省略..
}
依赖注入
依赖注入简介
应用程序通常由多个组件构成,组件与组件之间往往存在依赖关系。为解决组件之间引用关系的耦合,就需要用到依赖倒置原则,这个原则指明高层不应直接依赖低层,两都均依赖抽象(或接口),如下图:
常用的依赖注入方式有构造函数注入、属性依赖注入、方法依赖注入。也可创建一个专门的类负责管理创建所需要的类,并创建它所有可能要用到的依赖,这个类就是依赖注入容器(Dependency Injection Container),也可称之为控制反转容器(Inversion of Control Container,IoC容器)。
ASP.NET Core中的依赖注入
在ASP.NET Core中,所有被放入依赖注入容器的类型或组件称为服务。容器中服务有两种:框架服务(是ASP.NET Core框架组成部分)和应用服务(由用户放到容器中的服务)。
为了使用服务,首先要向容器添加服务,可以在Startup类的ConfigureServices()方法中使用IServiceCollection 实例对象的Add()方法添加服务。
MVC
若要在应用程序中使用MVC,则需要在Startup类中添加服务和中间件。
路由
在ASP.NET Core MVC中,路由负责从请求的URL中获取信息来定位或映射到对应的Controller与Action。
注意:任何一个MVC应用程序应至少定义一个路由。
要创建路由,首先要添加与路由相关的服务,然后再配置路由中间件。对于ASP.NET Core MVC,定义路由的方法有两种:
- 基于约定的路由:根据一些约定创建路由,在应用程序的Startup类中来定义;路由约定本质上是一个URL模板信息,如下:
template: "{controller}/{action}/{id?:int}"
default: new {controller="Home", action="Index"}
其中,在大括号{}中的部分是路由参数,每个参数都有一个名称,充当占位符的作用,参数与参数之间用 ‘/’ 号分隔。对于参数名controller和action是ASP.NET Core MVC特定的,分别匹配到Controller和Action。模板中的最一个参数id,会向action所映射方法的同名参数传值。?指定这个参数是是一个可选参数,URL模板的可选参数只能放在最后。在指定参数时,也可以为参数设置约束和限制,如指定类型约束(例如:int、bool、datetime、decimal、long、float、guid等)、限制长度(例如:length(min,max)、maxlength(value)、minlength(value)等)、限制参数取值范围(例如:range(min,max)等)、还可以指定正则表达式(例如:{para:regex(^d{15}|\d{18}$)})。
- 特性路由:使用C#特性对Controller和Action指定其路由信息,即RouteAttribute。它能够为每个Controller和Action显示地设置路由信息,只要在Controller类和Action方法添加 [Route(“”)] 特性即可。[Route]特性除了使用固定值,还使用[controller]和[action]分别代替固定值。
[Route("Home")]或[Route("Home/Index")]
[Route("[controller]")]或[Route("[controller]/[action]")]
在开发Web API应用程序时,通常使用HTTP特性,如HttpGet、HttpPost、HttpPut、HttpPatch、HttpDelete、HttpOption、HttpHead,分对应HTTP方法。
最后,需要说明,基于约定的路由和特性路由可以同时存在,但是如果已为一个Action指定了特性路由,则基于约定的路由在这个Action上就不起作用了。
Controller和Action
一个Controller类包含一个或多个Action函数方法。
根据约定,Controller类放在应用程序根目录下的Controllers目录中,并且它继承于Microsoft.AspNetCore.Mvc.ControllerBase抽象类(建议,可以更好地使用抽象类的一些用于返回结果的方法)。此外,在类的命名上,应以Controller结尾(非强制约定)。
如果没有按上面的约定定义Controller类,可为类添加[Controller]特性,仍然能作为控制器处理。
模型绑定
将HTTP请求中的数据映射到Action中参数的过程称为模型绑定(Model Binding)。ASP.NET Core MVC主要从3种数据源来为Action的以数参数提供数据:
- From值:HTTP POST请求时表单中的数据;
- 路由值:通过路由系统解析得到;
- 查询字符串:从URL中的查询字符串中获取。
另外,通过模型绑定的特性能够为Action的参数显示指定不同的绑定数据来源:
- [FromHeader]特性:从HTTP请求的Header中获取参数的值;
- [FromQuery]特性:从查询字符串中获取参数的值;
- [FromServices]特性:从依赖注入容器中获取参数的值;
- [FromRoute]特性:从路由中获取参数的值;
- [FromForm]特性:从表单中获取该参数的值;
- [FromBody]特性:从HTTP请求的消息正文中获取参数的值;
另外,还有以下两个特性用于指明参数是否必须使用绑定: - [BindRequire]特性:在进行模型绑定时,如绑定不成功,则产生ModelState错误;
- [BindNever]特性:在进行模型绑定时,忽略此参数;
从ASP.NET Core 2.1开始,添加了**[ApiController]**特性,会为Action推断参数的来源,因此在很多情况下不需要显示指定参数的模型绑定特性。
模型验证
模型验证是指数据被使用之前的验证过程,它发生在模型绑定之后。在ASP.NET Core中,使用数据注解(Data annotation)特性,即为Model类的属性添加需要的数据注解特性即可。
// 在Model类的属性添加需要的数据注解特性
public class DataObject
{
[Required(ErrorMessage="Id不能为空")]
public int Id{get; set;}
[Required(ErrorMessage="Name不能为空"), MaxLength(32)]
[MinLength(10, ErrorMessage="Name不能少于10个字符")]
public string Name{get; set;}
}
// 在Action,对数据进行验证。如果没有满足验证要求,请求结果返回400 Bad Request状态码
public ActionResult Post([FromBody] DataObject obj)
{
if(ModelState.IsVaild)
{
........
return Ok();
}
else
{
return BadRequest(ModelState);
}
}
当为一个Controller类应用[ApiController]特性时,对于此控制器中用于处理POST或PUT请的Action,会自动添加模型验证的功能,并且在没有通过验证时返回BadRequest(ModelState)。
在ASP.NET Core提供了两种创建自定义验证的方法:
- 一种是创建新的特性类,并使它继承自VaildationAttribute类;定义好新的特性后,就可以像其他验证特性一样使用了。
/// <summary>
/// 自定义验证特性:字符串属性中不能包含空格
/// </summary>
public class NoSpaceAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value != null && value.ToString().Contains(" "))
{
return new ValidationResult("字符串不能包含空格");
}
else
{
return ValidationResult.Success;
}
}
}
- 另一种是待验证的Model类实现IValidatableObject接口的唯一方法Validate()方法。在Validate()方法中对所有需要自定义验证的属性进行验证,并使用yield 关键字返回一个或多个ValidationResult对象,其中包括错误消息以及涉及此错误的属性名称列表。
public class DataObject : IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Name != null && Name.ToString().Contains(" "))
{
yield return new ValidationResult("名称不允许包含空格", new[] { nameof(Name) });
}
}
[Required(ErrorMessage = "Id不允许为空值"), Range(0, 99999999)]
public int Id { get; set; }
[Required(ErrorMessage = "Name不允许为空值")]
[MinLength(6, ErrorMessage = "Name不能少于6个字符")]
//[NoSpace(ErrorMessage = "Name不允许包含空格")]
public string Name { get; set; }
}
过滤器
过滤器与中间件相似,能够在某些功能前后执行,由此而形成一个管道。ASP.NET Core提供了以下5种过滤器:
- Authorization过滤器:最先执行,用于判断用户是否授权,如未授权则直接结束当前请求。这种过滤器实现了IAsyncAuthorizationFilter或IAuthorizationFilter接口。
- Resource过滤器:在执行其他过滤器(在Authorization过滤器之后执行)之前和之后执行,在Action之前执行,可以用来对请求进行判断,并根据条件来决定是否继续执行Action。这种过滤器实现了IAsyncResourceFilter或IResourceFilter接口。
- Action过滤器:在Action执行的前后执行,它在模型绑定后执行。这种过滤器实现了IAsyncActionFilter或IActionFilter接口。
- Exception过滤器:用于捕获异常的过滤器。这种过滤器实现了IAsyncExceptionFilter或IExceptionFilter接口。
- Result过滤器:在IActionResult执行的前后执行,使用它能够控制Action的执行结果,比如格式化结果等。需要注意的是,它只有在Action执行完成后才会运行。这种过滤器实现了IAsyncResultFilter或IResultFilter接口。
示例:定义一个Action全局过滤器用于验证Action参数
/// <summary>
/// Action 过滤器(全局):对传给Action的参数进行判断,如果未通过验证则直接返回HTTP请求,
/// 并在HTTP响应中添加一个新Header项X-ParameterValidation,用于说明是否通过验证
/// 使用前需要先在Startup类的ConfigureServices()方法注册到MVC的Filter集合中。
/// 示例:services.AddMvc(option => option.Filters.Add<ActionParameterValidationFilter>());
/// </summary>
public class ActionParameterValidationFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
// 获取要执行的Action描述信息,包括参数列表
var descriptor = context.ActionDescriptor as ControllerActionDescriptor;
// 遍历参数列表
foreach(var para in descriptor.Parameters)
{
// 如果参数是字符串类型,则验证参数值是否包含SCRIPT
if (typeof(string) == para.ParameterType)
{
var argument = context.ActionArguments[para.Name];
if (argument.ToString().ToUpper().Contains("SCRIPT"))
{
context.HttpContext.Response.Headers["X-ParameterValidation"] = "Fail";
// 在过滤器中,只要为ActionExecutingContext对象的Result属性赋一个非空值,就会中断过滤器的处理管道
context.Result = new BadRequestObjectResult($"不能包含SCRIPT:{para.Name}");
break;
}
}
}
if (context.Result == null)
{
var resultContext = await next();
context.HttpContext.Response.Headers["X-ParameterValidation"] = "Success";
}
}
}
上面的Action过滤器注册后会影响到应用程序中的每个Action,这种方式是全局的。如果要仅为一个或少数几个Action添加过滤器,则不能使用这种方式,而要使用特性。通过创建继承ActionFilterAttribute类的自定义过滤器特性类,就可以在Action上添加过滤器特性。
如果在自定义过滤器中需要引入其它组件或服务,可以通过构造函数注入的方式将所依赖的服务注入到过滤器。具体如下:
public class ActionParameterValidationFilter : IAsyncActionFilter
{
public IDataService DataSrv { get;}
// 构造函数注入依赖的数据
public ActionParameterValidationFilter(IDataService dataSrv)
{
this.DataSrv = dataSrv;
}
}
public void ConfigureServices(IServiceCollection services)
{
...
// 添加组件和服务到容器中
services.AddScoped<IDataService >();
services.AddScoped<ActionParameterValidationFilter >();
}
// 使用[ServiceFilter]添加过滤器特性,解决过滤器特性中引用其他依赖的问题
[ServiceFilter(typeof(ActionParameterValidationFilter))]
public ActionResult Get(string key, int top)
{
}
使用[ServiceFilter]特性和[TypeFilter]特性解决过滤器特性中引用其他依赖的问题。注意:使用[ServiceFilter]特性时要求过滤器实例已添加到容器中,而使用[TypeFilter]则不需要。
配置
访问配置文件
在项目中创建一个JSON配置文件(Files目录下): customConfig.json
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=_CHANGE_ME;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"FontFamily": "Arial",
"FontSize": 16
}
用ConfigurationBuilder类访问JSON配置文件,如下:
/// <summary>
/// Porgram类是ASP.NET Core应用程序的入口
/// </summary>
public class Program
{
/// <summary>
/// 程序启动时,从Porgram类的Main方法开始执行
/// </summary>
/// <param name="args"></param>
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
// 添加自定义配置文件
var configBuilder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(@"Files\customConfig.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.AddInMemoryCollection()
.AddCommandLine(args);
webBuilder.UseConfiguration(configBuilder.Build());
// 配置应用程序的启动
webBuilder.UseStartup<Startup>();
});
}
除了访问JSON配置文件外,还可以访问XML文件、INI文件、集合对象和系统环境变量等。
当指定多个配置源时,系统会按照顺序加载每个源中的配置项;后加载的配置项会覆盖前面加载的配置项;配置项键名不区分大小写;
自定义配置源
使用自定义配置源可以读取特定格式配置文件的内容。ASP.NET Core不支持对web.config或app.config这种传统类型的配置文件访问,因此就需要创建自定义配置源来读取,最终向配置系统提供配置项。如下代码实现了此类configs配置文件的访问:
- 创建ConfigConfigurationSource 类
public class ConfigConfigurationSource : FileConfigurationSource
{
public ConfigConfigurationSource(string path)
{
Path = path;
ReloadOnChange = true;
Optional = true;
FileProvider = null;
}
public override IConfigurationProvider Build(IConfigurationBuilder builder)
{
FileProvider = FileProvider ?? builder.GetFileProvider();
return new ConfigConfigurationProvider(this);
}
}
- 创建ConfigConfigurationProvider类
public class ConfigConfigurationProvider : FileConfigurationProvider
{
public ConfigConfigurationProvider(ConfigConfigurationSource source)
:base(source)
{
}
public override void Load(Stream s)
{
try
{
Data = ReadConfigFile(s);
}
catch
{
throw new Exception("读取配置信息失效,配置文件格式不正确");
}
base.Load();
}
private IDictionary<string, string> ReadConfigFile(Stream s)
{
var data = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var doc = new XmlDocument();
doc.Load(s);
var appSettings = doc.SelectNodes("/configuration/appSetting/add");
foreach(XmlNode child in appSettings)
{
data[child.Attributes["key"].Value] = child.Attributes["value"].Value;
}
return data;
}
}
- 创建ConfigConfigurationExtensions类,扩展方法
public static class ConfigConfigurationExtensions
{
public static IConfigurationBuilder AddConfig(this IConfigurationBuilder builder, string path)
{
return builder.Add(new ConfigConfigurationSource(path));
}
}
- 应用程序启动时加载web.config配置文件,向配置系统提供配置项
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
// 添加自定义配置文件
var configBuilder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddConfig("web.config")
.AddJsonFile(@"Files\customConfig.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.AddInMemoryCollection()
.AddCommandLine(args);
webBuilder.UseConfiguration(configBuilder.Build());
// 配置应用程序的启动
webBuilder.UseStartup<Startup>();
});
重新加载配置
ASP.NET Core的配置系统是只读的。
在添加配置源时指定reloadOnChange属性;
或调用config.Reload()方法。
强类型对象
有时,希望将多个配置项映射为具有同名属性的.NET对象,访问对象属性比直接访问配置方便。例如:前面的customConfig.json文件,可以创建一个包含同样信息的类来表示其中的配置信息。如下:
public class CustomConfig
{
public string FontFamily { get; set; }
public int FontSize { get; set; }
public ConnectionStringsSetting ConnectionStrings { get; set; }
public class ConnectionStringsSetting
{
public string DefaultConnection { get; set; }
}
}
将配置信息映射到CustomConfig类,同时也会将IOptions< CustomConfig>对象放入到当前应用程序的依赖注入容器中。
public class Startup
{
......
public void ConfigureServices(IServiceCollection services)
{
// 添加配置服务到依赖注入容器
services.Configure<CustomConfig>(Configuration);
......
}
......
}
最后,可以在Controller中注入CustomConfig对象,并通过它的Value属性获取IOptions< T >所包含的对象内容。
[Route("[controller]")]
public class ValueController : ControllerBase
{
private readonly CustomConfig _customConfig;
public ValueController(IOptions<CustomConfig> options)
{
_customConfig = options.Value;
}
[HttpGet]
public IEnumerable<string> Get()
{
List<string> result = new List<string>();
result.Add(_customConfig.FontFamily);
result.Add(_customConfig.FontSize.ToString());
result.Add(_customConfig.ConnectionStrings.DefaultConnection);
return result;
}
}
日志
ASP.NET Core框架内部集成了日志功能,主要由以下重要的接口组成:
- ILogger:包括实际执行记录日志操作的方法;
- ILoggerProvider:用于创建ILogger对象;
- ILoggerFactory:通过ILoggerProvider对象创建ILogger对象。
当ASP.NET Core应用程序过行时,日志组件会被添加到其依赖注入容器中,因此只要在合适的位置将ILogger对象注入进来,即可使用它来记录日志。
[ApiController]
[Route("[controller]")]
public class DataObjectController : ControllerBase
{
private readonly ILogger<DataObjectController> _logger;
private readonly CustomConfig _customConfig;
public DataObjectController(IOptions<CustomConfig> options, ILogger<DataObjectController> logger)
{
_customConfig = options.Value;
_logger = logger;
}
......
}
错误处理
异常处理
在ASP.NET Core中,有两个用来处理异常的中间件:
- DeveloperExceptionPageMiddleware:仅用于开发环境的异常处理中间件;
- ExceptionHandlerMiddlerware:非开发环境的异常处理中间件;
上面两个方法都应该在Configure方法的一开始就添加进来:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
if (env.IsDevelopment())
{
// 开发环境的异常处理中间件
app.UseDeveloperExceptionPage();
}
else
{
// 非开发环境的异常处理中间件
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
context.Response.ContentType = "text/plain;charset=uft-8";
string strError = "请求发生错误";
await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(strError), 0, strError.Length);
});
});
}
......
}
错误码处理
使用StatusCodePagesMiddleware中间件能够自定义关于错误状态码的细节。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
// 自定义状态码显示结果
app.UseStatusCodePages(async context =>
{
var statusCode = context.HttpContext.Response.StatusCode;
context.HttpContext.Response.ContentType = "text/plain;charset=uft-8";
var errorMessage = $"请求发生错误,状态码{statusCode}";
await context.HttpContext.Response.Body.WriteAsync(Encoding.UTF8.GetBytes(errorMessage), 0, errorMessage.Length);
});
......
}