本随笔只是个人学习记录。
一、Model的数据注解
开发asp.netcore 程序时(asp.netcore mvc 或这 asp.netcore webapi),在model 上使用数据注解,可以根据注解对客户端传递进来的数据进行过滤。具体为,当客户端数据传递到具体的action时,在进入action 方法体内部之前,对客户端传递进来的参数,根据数据注解进行验证,验证通过则执行action,不通过则返回。
部分内置的数据注解特性如下(来源官网):
更多特性,移步官网:https://docs.microsoft.com/zh-cn/dotnet/api/system.componentmodel.dataannotations?view=netcore-3.1
数据注解参考官网:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/validation?view=aspnetcore-3.1
https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/first-mvc-app/validation?view=aspnetcore-3.1
二、POST重复提交
在asp.netcore mvc中,提交数据到post 方法后,如果只是返回了某个view,而此时url并没有发生改变。再此刷新,会产生重复提交。为了防止重复提交,需要使用重定向。
创建学生时,保存数据前,地址是 :https://localhost:5001/Home/Create
保存数据后,地址还是:https://localhost:5001/Home/Create,此时,如果刷新网页,会造成 上一步骤的数据重复提交到 Create 进行保存。此时,会产生两条一样的数据保存到数据库,而这个过程,是不经意产生的,并不是我们想要的。
如果使用重定向,则如下图所示,此时地址已经改变,数据不会发生post 重复提交,这里即是不会提交到Create
三、XSS 跨站脚本攻击
跨站脚本攻击,在html页面表单的input输入框中,输入脚本,比如html、js脚本,asp.netcore后台程序不经过处理则进行存储。取出数据显示时,会自动执行所保存的脚本,破坏应用程序。虽然前端可以对输入的数据进行编码,但终究不安全,需要在后台进行编码。
四 此随笔关键代码片段:(源码太多,我只贴出关键代码片段)
Model Student 代码:
namespace MVCTemplate.Models { public class Student { public int Id { get; set; } //数据注解 data anotation [MinLength(3), Required,Display(Name ="FirstName")] public string FirstName { get; set; } [Required,Display(Name ="LastName")] public string LastName { get; set; } [Required,Display(Name ="性别")] public Gender Gender { get; set; } [Required,Display(Name ="生日")] public DateTime Birthday { get; set; } } public enum Gender { 女=0, 男=1, 其他=2 } }
前端 HTML代码片段:
@model MVCTemplate.Models.Student <h1>创建学生</h1> <div> <form method="post"> <label asp-for="FirstName"></label> <input asp-for="@Model.FirstName" /> <label asp-for="LastName"></label> <input asp-for="@Model.LastName" /> <label asp-for="Birthday"></label> <input asp-for="@Model.Birthday" type="date" /> <label asp-for="Gender"></label> <select asp-for="@Model.Gender" asp- items="@Html.GetEnumSelectList<Gender>()"></select> <button type="submit">保存数据</button> </form> </div>
后台 代码片段:
public class HomeController : Controller { private readonly ILogger<HomeController> _logger; private readonly IRepository<Student> _repository; private readonly HtmlEncoder _htmlEncoder; /// <summary> /// 采用构造函数注入 /// </summary> /// <param name="logger"></param> /// <param name="repository"></param> public HomeController(ILogger<HomeController> logger, IRepository<Student> repository, HtmlEncoder htmlEncoder) { _logger = logger; _repository = repository; _htmlEncoder = htmlEncoder;//htmlEoncoder,不需要再 ConfigureServices中手动注册,运行时会自动注册。 } [HttpPost] //post 请求 [ValidateAntiForgeryToken]//用于防止跨站请求伪造,针对post的Action,一定要加上这个。 public IActionResult Create(Student stuModel) { //数据注解,mvc框架会在匹配参数时,根据数据注解来判断传递进来的参数是否合法(符合要求),如果不合法, //则不会进入到方法内。即数据注解会在参数匹配时进行拦截。 if (!ModelState.IsValid) { return Content("数据输入有误"); } //对数据进行编码,防止XSS攻击。 比如会将输入的js脚本的<>这两个符号编码成>,< stuModel.FirstName = _htmlEncoder.Encode(stuModel.FirstName); //return new ObjectResult(stuModel);//返回json数据 _repository.Add(stuModel); //带上 stuModel 返回页面detail。这种方式,容易产生重复提交post.因为返回后的地址不变,刷新页面,会重新提交post请求。 //为了避免post重复提交,应该使用重定向 // return View("Detail",stuModel); return RedirectToAction(nameof(Detail), stuModel); } public IActionResult Detail(Student stuModel) { return View(stuModel); } /* * 其他代码,太多了,这里就不贴出来了。 */ }