• .NET Core中的异常处理中间件的用法
  • 发布于 2个月前
  • 238 热度
    0 评论
我们使用了空模版来创建应用程序,为了获取应用程序异常我们需要添加处理异常的中间件。运行应用程序,浏览器中输入https://localhost:7034/game浏览器将输入No Response Generated

这是一个异常但是并没有说明引发异常的具体原因,为了让用户看到看到一些友好的异常消息,像 404 资源不存在,401没有访问权限,403资源不可用,因此我们必须启用异常处理。在ASP.NET Core里面处理异常通常在Program类中添加ExceptionHandler中间件,添加UseDeveloperExceptionPage方法来生成HTML错误信息。

app.UseDeveloperExceptionPage();
我们也可以使用app.UseStatusCodePages()中间件处理响应状态码在400到599的错误,但是没有响应体,在Program.cs类中添加下面两行代码。
app.UseDeveloperExceptionPage();
app.UseStatusCodePages();
在运行之前我们需要注释掉ResponseEditingMiddleware中间件。现在重新运行应用程序,输入https://localhost:7034/Game你将会看到下面错误页面:

上面的异常是由于资源不存在而引发的,下面展示当action方法中的代码发生异常时将会发生什么情况。我们在HomeController内添加一个Exception方法,这个方法抛出NullReferenceException一个
public IActionResult Exception() 
{
   // 堆代码 duidaima.com
   throw new NullReferenceException();
}
重新运行应用程序并且访问如下url-https://localhost:7034/-Home/Exception,你会看到更多的异常详细信息和堆栈信息


 Development 和 Production 环境中异常处理
我们也可以在生产环境中添加一个action方法,每当错误发生时调用这个方法,在HomeController下创建一个Error方法
public IActionResult Error()
{
    return View();
}
接下来在Views->Home文件夹下添加一个Error的Razor视图
<h2>Some Problem</h2>
<p>We got some problem, please visit back after sometime.</p>
现在我们想要达到如下要求:
1 当应用程序运行在生产环境下时,用户将看到简单的文本消息并让通知用户发生了错误,让稍后在访问
2 当应用程序运行在开发环境时,用户能看到整个错误消息的调用堆栈
我们现在更新一下Program.cs类
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    // The default HSTS value is 30 days.
    app.UseHsts();
}
else
{
    app.UseDeveloperExceptionPage();
    app.UseStatusCodePages();
}
UseExceptionHandler中间件捕获ASP.NET Core应用程序发生的异常,app.UseExceptionHandler("/Home/Error")设置错误路径为/Home/Error,如果发生错误,会调转到该action。为了测试,设置ASPNETCORE_ENVIRONMENT为Production,运行应用程序并访问/Home/Exception页面,这次你将会看到Error视图被呈现。


 ASP.NET Core appsettings.json
appsettings.json包含数据库链接的键值对,全局变量和另外一些ASP.NET Core应用程序的配置,appsettings.json 文件位于应用程序的根目录下,文件的内容如下:
{
    "Key1": "Microsoft",
    "Key2": "Dot Net"
}
这里Key1和Key2是键,Microsoft和DotNet是他们的值,我们在项目文件中创建一个appsettings.json的文件,如下所示:


从appsettings.json中获取值
在appsettings.json文件中,我想要存储中间件配置信息,例如:是否启用该中间件,因此我们在文件中添加如下代码:
 "Middleware": {
    "EnableContentMiddleware": true,
    "EnableShortCircuitMiddleware": true,
    "EnableRequestEditingMiddleware": true,
    "EnableResponseEditingMiddleware": false
  }
该节点中包含了4个key,我们给每个key赋值为bool类型,在Program.cs类中读取他们的值,如果这些值为true我们将注册对应的中间件。我们可以看到EnableResponseEditingMiddleware提供的值为false剩下的全部是true,因此除了ResponseEditingMiddle-ware中间件剩余的全部注册。在Program类中我们可以使用app.Configuration()方法读取配置文件,在Programe类中添加下面代码,从appsettings读取配置并注册对应的中间件。
if (Convert.ToBoolean(app.Configuration["Middleware:EnableResponseEditingMiddleware"])) {
    app.UseMiddleware < ResponseEditingMiddleware > ();
}
if (Convert.ToBoolean(app.Configuration["Middleware:EnableRequestEditingMiddleware"])) {
    app.UseMiddleware < RequestEditingMiddleware > ();
}
if (Convert.ToBoolean(app.Configuration["Middleware:EnableShortCircuitMiddleware"])) {
    app.UseMiddleware < ShortCircuitMiddleware > ();
}
if (Convert.ToBoolean(app.Configuration["Middleware:EnableContentMiddleware"])) {
    app.UseMiddleware < ContentMiddleware > ();
}
我们也可以使用GetSection和GetValue方法读取对应的值,代码如下:
if ((app.Configuration.GetSection("Middleware")?.GetValue<bool>("EnableResponseEditingMiddleware")).Value)
{
    app.UseMiddleware<ResponseEditingMiddleware>();
}
if ((app.Configuration.GetSection("Middleware")?.GetValue<bool>("EnableRequestEditingMiddleware")).Value)
{
    app.UseMiddleware<RequestEditingMiddleware>();
}
if ((app.Configuration.GetSection("Middleware")?.GetValue<bool>("EnableShortCircuitMiddleware")).Value)
{
    app.UseMiddleware<ShortCircuitMiddleware>();
}
if ((app.Configuration.GetSection("Middleware")?.GetValue<bool>("EnableContentMiddleware")).Value)
{
    app.UseMiddleware<ContentMiddleware>();
}
在Controller & View 中访问appsettings.json
我们可以把IConfiguration注入到控制器的构造函数中实现对appsettings.json文件的访问
public class SomeController : Controller
{
    private IWebHostEnvironment _env;
    private IConfiguration _config;
    public SomeController(IWebHostEnvironment hostingEnvironment, IConfiguration configuration)
    {
        _env = hostingEnvironment;
        _config = configuration;
    }
    public IActionResult Index()
    {
        bool contentMiddleware = Convert.ToBoolean(_config["Middleware:EnableContentMiddleware"]);
        return View();
    }
}
在大型项目中,在appsettings.json中可能有上百个实体类,这种情况下比较好的方法是从appsettings.json中提取需要的节点并将其转化为实体类,然后使用实体类访问配置文件中的键值对,整个步骤过程如下:
1 创建一个类包含appsettings.json节点
2 在Programe类中使用Configure<T>注册该类
3 使用IOptions<T>在控制器中获取该类
因此,在appsettings.json添加新的节点包含API相关的信息,在Controller中只使用这个API相关的节点
"Middleware": {
    "EnableContentMiddleware": true,
    "EnableShortCircuitMiddleware": true,
    "EnableRequestEditingMiddleware": true,
    "EnableResponseEditingMiddleware": false
  },
  "APIEndpoints": {
    "Name": "OpenWeatherMap",
    "Url": "http://api.openweathermap.org/",
    "IsSecured": true
  }
现在创建一个类包含APIEndpoints节点下Key的值,你必须保证这个类的属性和section内部节点的名字是一样的,我们在Models文件夹下创建一个MyWebApi.cs类
public class MyWebApi
{
    public string Name { get; set; }
    public string Url { get; set; }
    public bool IsSecured { get; set; }
}
现在,进入Program.cs类并且使用Configure<T>注册这个类
builder.Services.Configure<MyWebApi>(builder.Configuration.GetSection("APIEndpoints"));
在Controller中引用Microsoft.Extensions.Options命名空间,在构造函数中添加IOptions<MyWebApi>依赖,现在我们可以在Controller中读取appsettings.json文件存储的值
using AspNetCore.Configuration.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
namespace AspNetCore.Configuration.Controllers
{
    public class ReadController : Controller
    {
        private readonly IOptions<MyWebApi> _options;
        public ReadController(IOptions<MyWebApi> options)
        {
            _options = options;
        }
        public IActionResult Index()
        {
            var apiName=_options.Value.Name;
            var url=_options.Value.Url;
            var secured=_options.Value.IsSecured;
            return View();
        }
    }
}
在View中注入IOptions<T>
我们可以使用下面指令在View中注入IOptions<MyWebApi>
@using Microsoft.Extensions.Options;
@using AspNetCore.Configuration.Models;
@inject IOptions<MyWebApi> settings;

<p>@settings.Value.Name</p>
<p>@settings.Value.Url</p>
<p>@settings.Value.IsSecured</p>
一旦注入IOptions我们就可以在View中使用MyWebAPI,我们可以通过@settings.Value.PropertyName访问属性
Developmen和Production设置不同appsettings.json
如前所述,appsettings.json用来存储数据库的连接字符串,有如下场景:程序员在开发应用程序的过程中将应用程序的开发环境和生产环境的appsettings.json文件进行分离,如果在开发环境,将链接到开发环境中的数据库,如果在生产环境中,将链接到生产环境的数据库。我们可以通过创建2个新的appsettings文件来做这个功能,第一个文件的名字为appsettings.Production.json,第二个文件的名字为appsettings.Development.json

框架会根据当前环境自动遍历appsettings的文件,如果是Development环境时使用appsettings.Development.json,如果是Production环境时使用

appsettings.Production.json

首先添加一个新的文件appsettings.Production.json并且设置字串连接指向生产环境数据库
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=SQL6003.site4now.net;Database=DB_A3CE39_y11;User Id=DB_PCE39_y11_admin;Password=admin@123Ezfx;"
  }
}
接着在根目录下添加另外一个appsettings.json文件,名字为development.json,你可以在该文件中设置开发环境的连接字符串
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=vaio;Database=Goldentaurus;Trusted_Connection=True;"
  }
}
新添加的文件在解决方案中被隐藏起来,你可以右击appsettings.json文件前面的箭头可以查看到这两个文件
如下图所示:

为了获取这个链接字符串,我们在Programe类中添加如下代码:
builder.Services.Configure<Connections>(builder.Configuration.GetSection("ConnectionStrings"));
下一步,创建Connections.cs类将包含连接字符串:
public class Connections
{
    public string DefaultConnection { get; set; }
}
接着在控制器的构造函数中添加IOptions<Connections>的依赖,可以获取数据库字符串连接
using AspNetCore.Configuration.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
namespace AspNetCore.Configuration.Controllers
{
    public class ConnectionController : Controller
    {
        private IOptions<Connections> _options;
        public ConnectionController(IOptions<Connections> options)
        {
            _options = options;
        }
        public IActionResult Index()
        {
            var connections = _options.Value.DefaultConnection;
            return View();
        }
    }
}
当应用程序运行在开发环境中会读取appsettings.Devel-opment.json的配置。
当应用程序运行在生产环境中会读取appsettings.Produ-ction.json的配置。


用户评论