Asp.Net Core学习笔记:(二)视图、模型、持久化、文件、错误处理、日志
TagHelper
入门
优点:根据参数自动生成,不需要手写超链接,类似Django模板里面的url命令。
在ViewImport中添加TagHelper
@addTagHelper *,Microsoft.AspNetCore.Mvc.TagHelpers
比如,链接TagHelper使用
<a class="btn btn-outline-primary"
asp-controller="student" asp-action="get"
asp-route-id="@student.Id">
查看
</a>
缓存破坏的TagHelper
<img src="~/images/banner.jpg" alt="Alternate Text" asp-append-version="true" />
环境 TagHelper
在开发环境中使用本地css文件,在非开发环境下使用的是CDN的css文件。
注:integrity
是用来做完整性检查的,保证CDN提供文件的完整和安全。
<environment include="Development">
<link href="~/lib/twitter-bootstrap/css/bootstrap.css" rel="stylesheet" />
</environment>
<environment exclude="Development">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
</environment>
为了防止CDN加载失败页面无法显示,可以加上fallback相关属性,第一个是失败时加载的文件,第二个是不检查这个文件的完整性
asp-fallback-href="~/lib/twitter-bootstrap/css/bootstrap.css"
asp-suppress-fallback-integrity="true"
表单 Tag Helper
直接贴上一个布局的代码,把class样式都去掉了,保留最基本代码。
确实是很方便的,和Django、jinja2之类的模板比完全不输。
@model Student
<form asp-controller="student" asp-action="create">
<label asp-for="Name"></label>
<input asp-for="Name" />
<label asp-for="Email"></label>
<input asp-for="Email" />
<label asp-for="ClassName"></label>
<select asp-for="ClassName" asp-items="Html.GetEnumSelectList<ClassNameEnum>()"></select>
<button type="submit">提交</button>
</form>
模型绑定
将Http请求中的数据绑定到控制器方法上对应参数的顺序:
- Form Values (Post表单数据)
- Route Values (路由中的值)
- Query String (Get的查询字符串)
模型验证
1.设置模型
首先在Model中加入验证属性,如:
public class Student
{
public int Id { get; set; }
[Display(Name = "姓名"), MaxLength(4, ErrorMessage = "名字长度不能超过四个字")]
[Required(ErrorMessage = "请输入名字!")]
public string Name { get; set; }
[Display(Name = "班级")]
public ClassNameEnum ClassName { get; set; }
[Required(ErrorMessage = "请输入邮箱!")]
[Display(Name = "邮箱")]
[RegularExpression(@"^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$", ErrorMessage = "邮箱格式不正确")]
public string Email { get; set; }
}
常用的模型验证方法
- Required
- Range:指定允许的最小值和最大值
- MinLength
- MaxLength
- Compare:比较两个属性,比如密码和确认密码
- RegularExpression:正则匹配
2.在控制器中加入验证代码
使用ModelState.IsValid
来验证模型属性是否正确
[HttpPost]
public IActionResult Create(Student student)
{
if (ModelState.IsValid)
{
var newStudent = _studentRepository.Add(student);
return RedirectToAction("details", new { id = newStudent.Id });
}
return View();
}
3.使用TagHelper在网页上显示错误信息
例子如下:
<div class="text-danger" asp-validation-summary="All"></div>
<div class="form-group row">
<label asp-for="Name" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Name" class="form-control" />
<span class="text-danger" asp-validation-for="Name"></span>
</div>
</div>
EF Core入门
首先实现DbContext
public class AppDbContext:DbContext
{
// 将应用程序的配置传递给DbContext
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
// 对要使用到的每个实体都添加 DbSet<TEntity> 属性
// 通过DbSet属性来进行增删改查操作
// 对DbSet采用Linq查询的时候,EFCore自动将其转换为SQL语句
public DbSet<Student> Students { get; set; }
}
注册DbContext连接池
services.AddDbContextPool<AppDbContext>(options => options.UseSqlServer(_configuration.GetConnectionString("StudentDBConnection")));
其中,本地SqlServer数据库的配置,在appserttings.json
中:
"ConnectionStrings": {
"StudentDBConnection": "server=(localdb)\\MSSQLLocalDB;database=StudentDB;Trusted_Connection=true"
}
实现仓储
public class SqlStudentRepository : IStudentRepository
{
private readonly AppDbContext _context;
public SqlStudentRepository(AppDbContext context)
{
_context = context;
}
public Student Add(Student student)
{
_context.Students.Add(student);
_context.SaveChanges();
return student;
}
public Student Delete(int id)
{
var student = _context.Students.Find(id);
if (student != null)
{
_context.Students.Remove(student);
_context.SaveChanges();
}
return student;
}
public IEnumerable<Student> GetAll() => _context.Students;
public Student GetById(int id) => _context.Students.Find(id);
public Student Update(Student updatedStudent)
{
var student = _context.Students.Attach(updatedStudent);
student.State = EntityState.Modified;
_context.SaveChanges();
return updatedStudent;
}
}
EF Core 常用命令
在nuget控制台中,不区分大小写
- Get-Help about_enti:显示帮助,
about_enti
全名很长可以只写前面的 - Add-Migration:添加迁移记录
- Update-Database:更新数据库
添加种子数据
重写DbContext
的OnModelCreating
方法
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<Student>().HasData(
new Student { Id = 1, Name = "小米" },
);
}
为了避免DbContext
代码太乱,也可以使用扩展方法的方式:
public static class ModelBuilderExtensions
{
public static void InsertSeedData(this ModelBuilder mBuilder)
{
mBuilder.Entity<Student>().HasData(
new Student { Id = 1, Name = "小米" },
);
}
}
领域模型与数据库架构
- 使用迁移功能同步领域模型和数据库架构
- 使用
add-migration
添加迁移记录 - 使用
remove-migration
删除最近一条记录 - 使用
update-database 迁移记录名称
可以回滚至任意一次迁移
文件上传
定义ViewModel
要上传的字段采用 IFormFile
类型
public class StudentCreateViewModel
{
public int Id { get; set; }
// 省略无关代码...
[Display(Name = "图片")]
public IFormFile Photo { get; set; }
}
编写视图
修改cshtml
视图文件,修改模型绑定:
@model StudentCreateViewModel
加入上传文件的表单项
<div class="form-group row">
<label asp-for="Photo" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<div class="custom-file">
<input asp-for="Photo" class="form-control custom-file-input" />
<label class="custom-file-label">请选择图片</label>
</div>
</div>
</div>
为了选择文件后能显示出文件名还要编写js:
$(document).ready(function () {
$('.custom-file-input').on('change', function () {
var fileName = $(this).val().split('\\').pop();
$(this).next('.custom-file-label').html(fileName);
});
});
编写控制器
通过构造函数注入 HostingEnvironment
public StudentController(IStudentRepository studentRepository, HostingEnvironment hostingEnvironment)
{
_studentRepository = studentRepository;
_hostingEnvironment = hostingEnvironment;
}
处理文件上传和保存的逻辑
[HttpPost]
public IActionResult Create(StudentCreateViewModel model)
{
if (ModelState.IsValid)
{
var uniqueFileName = "";
if (model.Photo != null)
{
var uploadDir = Path.Combine(
_hostingEnvironment.WebRootPath,
"uploads", "images");
uniqueFileName = Guid.NewGuid().ToString() +
"_" + model.Photo.FileName;
model.Photo.CopyTo(new FileStream(
Path.Combine(uploadDir, uniqueFileName),
FileMode.Create));
}
var student = new Student
{
Name = model.Name,
Email = model.Email,
ClassName = model.ClassName,
PhotoPath = uniqueFileName,
};
var newStudent = _studentRepository.Add(student);
return RedirectToAction("details",
new { id = newStudent.Id });
}
return View();
}
多文件上传
和单文件差不多
ViewModel
增加 List<IFormFile>
类型字段
[Display(Name = "图库")]
public List<IFormFile> Gallery { get; set; }
修改视图
<div class="form-group row">
<label asp-for="Gallery" class="col-sm-2 col-form-label"></label>
<div class="col-sm-10">
<div class="custom-file">
<input asp-for="Gallery" multiple id="gallery-input" class="form-control custom-file-input" />
<label id="gallery-label" class="custom-file-label">请选择图片 可以一次选择多张</label>
</div>
</div>
</div>
js代码:
$('#gallery-input').on('change', function () {
var label = $(this).next('#gallery-label');
var files = $(this)[0].files;
if (files.length > 1) {
label.html(`你已经选择了${files.length}个文件`);
} else if (files.length == 1) {
label.html(files[0].name);
}
});
修改控制器代码
// 处理多文件上传
if (model.Gallery != null && model.Gallery.Count > 0)
{
foreach(var photo in model.Gallery)
{
uniqueFileName = Guid.NewGuid().ToString() + "_"
+ photo.FileName;
photo.CopyTo(new FileStream(Path.Combine(uploadDir,
uniqueFileName),FileMode.Create));
}
}
错误处理
添加错误处理页面
在 Startup.cs
中设置中间件:
app.UseStatusCodePagesWithReExecute("/error/{0}");
推荐用 UseStatusCodePagesWithReExecute
而不是 UseStatusCodePagesWithRedirects
,前者在管道内执行执行错误跳转url,后者会重定向到该url,导致http错误状态码变成新页面的正常执行的200码了。
接着编写错误控制器:
public class ErrorController : Controller
{
[Route("Error/{statusCode}")]
public IActionResult Index(int statusCode)
{
var statusCodeResult = HttpContext.Features.
Get<IStatusCodeReExecuteFeature>();
var viewModel = new ErrorViewModel
{
Path = statusCodeResult.OriginalPath,
QueryString = statusCodeResult.
OriginalQueryString,
};
switch (statusCode)
{
case 404:
viewModel.Message = "页面未找到";
break;
}
return View("Error", viewModel);
}
}
对了,我还定义了ViewModel:
public class ErrorViewModel
{
public int Code { get; set; }
public string Message { get; set; }
public string Path { get; set; }
public string QueryString { get; set; }
}
视图代码就不贴了,无非就是显示ViewModel里的这些错误信息~
设置全局异常跳转
添加中间件
app.UseExceptionHandler("/exception");
编写处理用的控制器,这里需要添加AllowAnonymous
注解,允许用户在未登录的时候访问到这个异常页面,保证无论如何可以显示出异常页面。
[AllowAnonymous]
[Route("exception")]
public IActionResult ExceptionHandler()
{
var exception = HttpContext.Features.
Get<IExceptionHandlerPathFeature>();
var viewModel = new ExceptionViewModel
{
Path = exception.Path,
Message = exception.Error.Message,
StackTrace = exception.Error.StackTrace,
};
return View("Exception", viewModel);
}
另外,ViewModel定义如下:
public class ExceptionViewModel
{
public string Path { get; set; }
public string Message { get; set; }
public string StackTrace { get; set; }
}
日志记录
AspNetCore里面自带了一套日志系统,默认已经注册到了服务容器里了,只要在控制器的构造函数里注入就可以使用了,比如:
public class ErrorController : Controller
{
private ILogger<ErrorController> _logger;
public ErrorController(ILogger<ErrorController> logger)
{
this._logger = logger;
}
}
默认的日志只会记录到控制台或者调试输出,不过我们为了实现更多功能,比如记录到文件或者推送到日志服务器,我们需要使用第三方的日志组件。这里我用的是NLog。
首先要安装NLog.Web.AspNetCore
这个包。
之后在Program.cs
里引入nlog服务:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureLogging((hostingContext, logging) =>
{
// 保留官方的代码中的默认日志程序
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
logging.AddEventSourceLogger();
// 引入 nlog
logging.AddNLog();
}).UseStartup<Startup>();
保留官方默认日志程序那里,要看AspNetCore的源代码,本文用的是2.2版本,在github看,地址如下:
https://github.com/dotnet/aspnetcore/blob/v2.2.8/src/DefaultBuilder/src/WebHost.cs
然后,为了使用nlog,需要创建一个配置文件,在项目根目录创建 NLog.config
:
关于配置文件的说明可以参考:https://github.com/NLog/NLog/wiki
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
throwConfigExceptions="true">
<targets>
<target name="f1" xsi:type="File" fileName="Logs\nlog-all-${shortdate}.log"/>
<target name="n1" xsi:type="Network" address="tcp://localhost:4001"/>
<target name="c1" xsi:type="Console" encoding="utf-8"
error="true"
detectConsoleAvailable="true" />
<target name="c2" xsi:type="ColoredConsole" encoding="utf-8"
useDefaultRowHighlightingRules="true"
errorStream="true"
enableAnsiOutput="true"
detectConsoleAvailable="true"
DetectOutputRedirected="true">
</target>
</targets>
<rules>
<logger name="*" maxLevel="Debug" writeTo="c2" />
<logger name="*" minLevel="Info" writeTo="f1" />
</rules>
</nlog>
之后在程序中就可以正常使用日志功能了。比如:
var viewModel = new StatusCodeViewModel
{
Code = statusCode,
Path = statusCodeResult.OriginalPath,
QueryString = statusCodeResult.OriginalQueryString,
};
_logger.LogWarning(viewModel.ToString());
还有可以在appsettings.json
里面配置日志等级和命名空间过滤,跟在NLog.conf
里面配置效果是一样的。例如:
"Logging": {
"LogLevel": {
"Default": "Warning",
"StudyManagement.Controllers.ErrorController":
"Warning"
}
},
欢迎交流
程序设计实验室专注于互联网热门新技术探索与团队敏捷开发实践,在公众号「程序设计实验室」后台回复 linux、flutter、c#、netcore、android、kotlin、java、python 等可获取相关技术文章和资料,同时有任何问题都可以在公众号后台留言~
- 博客园:https://www.cnblogs.com/deali/
- 打代码直播间:https://live.bilibili.com/11883038
- 知乎:https://www.zhihu.com/people/dealiaxy
Asp.Net Core学习笔记:(二)视图、模型、持久化、文件、错误处理、日志的更多相关文章
- Asp.Net Core学习笔记:入门篇
Asp.Net Core 学习 基于.Net Core 2.2版本的学习笔记. 常识 像Django那样自动检查代码更新,自动重载服务器(太方便了) dotnet watch run 托管设置 设置项 ...
- ASP.NET Core 学习笔记 第一篇 ASP.NET Core初探
前言 因为工作原因博客断断续续更新,其实在很早以前就有想法做一套关于ASP.NET CORE整体学习度路线,整体来说国内的环境的.NET生态环境还是相对比较严峻的,但是干一行爱一行,还是希望更多人加入 ...
- ASP.NET Core 学习笔记 第四篇 ASP.NET Core 中的配置
前言 说道配置文件,基本大多数软件为了扩展性.灵活性都会涉及到配置文件,比如之前常见的app.config和web.config.然后再说.NET Core,很多都发生了变化.总体的来说技术在进步,新 ...
- ASP.NET Core学习之二 菜鸟踩坑
对于像我这样没接触过core的人,坑还是比较多的,一些基础配置和以前差别很大,这里做下记录 一.Startup 1.注册服务 // This method gets called by the run ...
- Asp.net Core学习笔记
之前记在github上的,现在搬运过来 变化还是很大的,感觉和Nodejs有点类似,比如中间件的使用 ,努力学习ing... 优点 不依赖IIS 开源和跨平台 中间件支持 性能优化 无所不在的依赖注入 ...
- ASP.NET Core 学习笔记 第三篇 依赖注入框架的使用
前言 首先感谢小可爱门的支持,写了这个系列的第二篇后,得到了好多人的鼓励,也更加坚定我把这个系列写完的决心,也能更好的督促自己的学习,分享自己的学习成果.还记得上篇文章中最后提及到,假如服务越来越多怎 ...
- ASP.NET Core 学习笔记 第五篇 ASP.NET Core 中的选项
前言 还记得上一篇文章中所说的配置吗?本篇文章算是上一篇的延续吧.在 .NET Core 中读取配置文件大多数会为配置选项绑定一个POCO(Plain Old CLR Object)对象,并通过依赖注 ...
- Asp.net core 学习笔记 ( Data protection )
参考 : http://www.cnblogs.com/xishuai/p/aspnet-5-identity-part-one.html http://cnblogs.com/xishuai/p/a ...
- Asp.net core 学习笔记 SignalR
refer : https://kimsereyblog.blogspot.com/2018/07/signalr-with-asp-net-core.html https://github.com/ ...
- Asp.net core (学习笔记 路由和语言 route & language)
https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/routing?view=aspnetcore-2.1 https://doc ...
随机推荐
- 将文件转成byte[]文件属组
/** * * @Description : 读取文件数组 * @Method_Name : fileBuff * @param filePath * @return * @throws IOExce ...
- SQL系列总结——基础篇(二)
表达式:是符号与运算符的组合,一个常量,变量.列或标量函数就是一个简单的表达式,复杂的表达式由两个或更多的简单表达式通过运算符连接起来组成.运算符同样的遵循优先级的原则.作用,让SQL的 ...
- Vue结合Element UI实战
创建工程 1. 创建一个名为hello-vue的工程 vue init webpack hello-vue 2. 安装依赖 需要安装 vue-router.element-ui.sass-loader ...
- JVM调优之垃圾定位、垃圾回收算法、垃圾处理器对比
谈垃圾回收器之前,要先讲讲垃圾回收算法,以及JVM对垃圾的认定策略,JVM垃圾回收器是垃圾回收算法的具体实现,了解了前面的前置知识,有利于对垃圾回收器的理解. 什么是垃圾? 垃圾,主要是指堆上的对象, ...
- Java 容器系列总结
为什么要使用集合 当我们需要保存一组类型相同的数据的时候,我们应该是用一个容器来保存,这个容器就是数组,但是,使用数组存储对象具有一定的弊端, 因为我们在实际开发中,存储的数据的类型是多种多样的,于是 ...
- CPU的后记,程序员的未来之计
渔家傲 塞下秋来风景异,衡阳雁去无留意.四面边声连角起,千嶂里,长烟落日孤城闭. 浊酒一杯家万里,燕然未勒归无计.羌管悠悠霜满地.人不寐,将军白发征夫泪. 作者:良知犹存 转载授权以及围观:欢迎添 ...
- Linux换行符和Windows换行符的区别与转换
不同系统文本文件的行尾换行符不同: Windows为一个回车'\r'(CR或^M)和一个换行'\n'(NL或LF)(括号内是其它显示方法) Linux为一个换行'\n' Mac为一个 ...
- 掌握数位dp
最近遇到了数位dp题目,于是就屁颠屁颠的跑过来学习数位dp了~ "在信息学竞赛中,有这样一类问题:求给定区间中,满足给定条件的某个D 进制数或此类数的数量.所求的限定条件往往与数位有关,例如 ...
- Codeforces Global Round 7 D1. Prefix-Suffix Palindrome (Easy version)(字符串)
题意: 取一字符串不相交的前缀和后缀(可为空)构成最长回文串. 思路: 先从两边取对称的前后缀,之后再取余下字符串较长的回文前缀或后缀. #include <bits/stdc++.h> ...
- HDU6661 Acesrc and String Theory【SA】
Acesrc and String Theory Problem Description Acesrc is a famous string theorist at Nanjing Universit ...