依照老周的良好作风, 开始之前先说点题外话
前面的博文中, 老周介绍过自定义 MVC 视图的搜索路径, 即向 ViewLocationFormats 列表添加相应的内容, 其实, 对 Razor Page 模型, 也可以向 PageViewLocationFormats 列表添加相应的搜索路径, 比如 /MyPages/{1}/{0}.cshtml 其中, 0 是视图名, 1 是页面名称比如这样
- services.AddMvc().AddRazorOptions(opt =>
- {
- opt.ViewLocationFormats ...
- opt.PageViewLocationFormats ...
- });
然而, 我们知道, 基于 Razor 的 web Page 模型是以页面为单位的, 也就是说路径路由是直接指向页面的 (不包含. cshtml 扩展名), 即不需要 MVC 模型的路由方式所以, 我们并不需要修改 PageViewLocationFormats 中的内容许多时候, 我们只要告诉应用程序在哪个目录下查找 Page 就行了
默认的搜索位置是 /Pages 目录, 我们可以通过以下代码来修改
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddMvc().AddRazorPagesOptions(opt =>
- {
- opt.RootDirectory = "/UI";
- });
- }
以上代码写在 Startup 类中, 这个应该明白吧 RootDirectory 就是用来指定应用程序查找 Razor 页面的根目录路径, 此处我指写了 /UI, 所以, 在我的项目中, 我只要建一个 UI 目录, 然后各类 Razor 页就往里面放就行了
========================================================================================
好了, 题外话扯完了, 开始说正题吧今天咱们聊聊有关异常处理的破事吧, 也可以说是错误处理, 反正就这个意思, 你理解就好, 专业名词不必较劲, 只有那些吃饱了撑着的学术人才才会跟名词较劲
老办法, 咱们结合示例来讲述, 这样各位观众不会乏味
大家知道, 娱乐产品肾 Phone 已经成为流行玩具, 近年来, 购买肾 Phone 不一定只能用货币, 比较典型的一种支付方式是卖肾买 Phone 说实话, 现在许多国产娱乐产品也很便宜, 配置也不错, 几百块钱就能玩得刷刷响了, 割肾真没什么必要
为了方便人们以肾换 Phone , 老周特意开发了一个在线卖肾系统大致流程是这样的, 如果你有闲置的肾, 可以打开主页, 输入你的一些信息, 然后报个价, 其他用户看见后, 如果觉得合理, 就认购此肾
为了使操作流程更简单, 易上手, 轻入门, 该平台只需要输入姓名和肾的价格即可参加报价
大致的页面代码如下
- <form method="post">
- <div class="form-group">
- <label for="name"> 姓名:</label>
- <input type="text" class="form-control" name="name"/>
- </div>
- <div class="form-group">
- <label for="price"> 价格:</label>
- <input type="number" name="price" class="form-control"/>
- </div>
- <div class="form-group">
- <button type="submit" class="btn btn-success w-100"> 提 交 </button>
- </div>
- </form>
Razor 页面很像我们以前玩过的 aspx 页面, 每个页面都配套一个隐藏代码文件 Razor 页也会配有一个页面模型类, 注意这个模型类要从 PageModel 派生, 不是 Page 类, 别搞错了, Page 类只是作为生成 HTML 代码的基类, 我们的 .cshtml 文件在预编译后, 是隐式继承自 RazorPage 类的除非你要开发自己的标记语言, 否则你不必理会这些类
记住了, 与 Razor 页关联的模型类是从 PageModel 类派生的, 比如, 本例中, 当有人填写了闲置肾的相关信息后, 以 POST 方式提交, 这是候, 如果页面模型类中包含了名字为 OnPostOnPostAsync 的方法时, 就会自动调用如果想把我们上面那个 form 中的 name 和 price 的值传递给方法, 直接让 OnPost 方法的参数与 form 中的元素名称相同就可以了
- public class IndexModel : PageModel
- {
- public IActionResult OnPost(string name, decimal price)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new Exception("你怎么不留下姓名啊, 卖肾又不是丢人的事");
- }
- if(price <= 0.0M)
- {
- throw new Exception("靠! 你的肾这么不值钱吗? 还免费送, 包邮不?");
- }
- return RedirectToPage("/Success");
- }
- }
OnPost 不是 PageModel 基类的方法, 而是我们自己写的, 只是代码约定, Asp.net Core 里面用到很多代码约定, 它在运行的时候会查找这些特定的名字
上面代码中, 还对传递进来的 form 值进行验证, 如果不符合要求, 会抛出异常
一般来说, 在 Startup 类的 Configure 方法中, 我们会判断一下, 如果应用程序处于开发阶段, 为了方便测试, 应该加入这些代码
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
这样, 我们在测试时能看到详细的异常信息
但是, 在实际便用时, 我们不能公开这么详细的信息, 这样容易勾起人们的犯罪冲动所以, 一般会添加一个页面, 专门用来显示错误信息比如:
- @page
- <div class="card">
- <div class="card-header bg-danger">
- <span class="text-light"> 错误 </span>
- </div>
- <div class="card-body">
- <span class="card-text"> 唉, 真抱歉你提交的肾不符合国际标准, 没人要的 </span>
- </div>
- </div>
然后我们要在 Startup.Configure 方法中配置一下
app.UseExceptionHandler("/Error");
加上这一行后, 当发生异常时, 就会跳转到 /Error 页面
不过, 你也许会觉得, 虽然不能公开异常信息, 但一些必要的描述应该要的, 不然, 用户不知道发生了啥事我们可以通过 HttpContext 的 Features 集合获取一个用来处理异常的 Feature, 它的原型接口是 IExceptionHandlerFeature, 我们不必关心它的实现类型是谁, 只要访问它的 Error 属性就能得到关联的 Exception 实例
因此, 我们的错误页可以改一下
- @page
- @using Microsoft.AspNetCore.Diagnostics
- @{
- IExceptionHandlerFeature exf = HttpContext.Features.Get<IExceptionHandlerFeature>();
- Exception ex = exf?.Error;
- }
- <div class="card">
- <div class="card-header bg-danger">
- <span class="text-light"> 错误 </span>
- </div>
- <div class="card-body">
- @if (ex == null)
- {
- <span class="card-text"> 唉, 真抱歉你提交的肾不符合国际标准, 没人要的 </span>
- }
- else
- {
- <span class="card-text">@ex.Message</span>
- }
- </div>
- </div>
通过以下代码获得异常实例的引用
- IExceptionHandlerFeature exf = HttpContext.Features.Get<IExceptionHandlerFeature>();
- Exception ex = exf?.Error;
这样就可以在页面上显示异常的描述信息了
可能你又想到了, 我不想输出个页面, 我只想返回一些简单的文本, 那么, 你在 Startup.Configure 中可以这样写
- app.UseExceptionHandler(x =>
- {
- x.Run(async context =>
- {
- var ex = context.Features.Get<Microsoft.AspNetCore.Diagnostics.IExceptionHandlerFeature>()?.Error;
- string msg = ex == null ? "发生错误" : ex.Message;
- context.Response.ContentType = "text/plain;charset=utf-8";
- await context.Response.WriteAsync(msg);
- });
- });
里面的变量 x 就是当前的 IApplicationBuilder , 与传递给 Configure 方法的 app 参数类型一样, 这时候我们可以用 Reponse 的方法返回自定义的文本
好了, 今天的内容就介绍到这儿吧, 其实异常处理还有一种方法使用 Filter, 这个咱们留到下一篇博文再和大伙分享
来源: https://www.cnblogs.com/tcjiaan/p/8461408.html