Asp.net Core Mvc基础-学习笔记

ASP.NET CORE MVC

1. 默认配置

  • 使用Kestrel Web Server

    • ASP.NET Core内置,跨平台
  • IIS集成

    • UseIIS(),UseIISIntegration()
  • Log

  • IConfiguration接口

2. 路由

  • Convertional Routing

    使用默认的方式builder.MapRoute("Default", "{Controller}/{Action}/{Id?}");寻找路由

  • Attribute Routing

    在Controller类或其中的方法上,添加路由信息

    namespace Tutorial.Web.Controllers
    {
        [Route("[controller]")]
        public class AboutController
        {
            public string Me()
            {
                return "Dave";
            }
            public string Company()
            {
                return "No Company";
            }
        }
    }
    

    [Route("[controller]")][Route("stringname/[controller]/[action]")]根据Controller的名字查找路由,也可以在方法名上定义

    [Route("routeName")]根据自己设定的routeName查找路由

3. 视图

在controller中传入st信息

public IActionResult Index()
{
    var st = new Student
    {
        Id = 1,
        FirstName = "Qilong",
        LastName = "Wu"
    };
    return View(st);
}

在index页面,可以使用@Model获取这个信息的值,注意@Model首字母大写

cshtml中的@model,首字母为小写,其代表一个指令,能让Razor视图正确构建代码

如,在Index.cshtml中首行写入

@model Tutorial.Web.Model.Student

则书写<h2>@Model</h2>就会有提示信息,提示从controller传过来的数据中都有哪些属性。

<h2>@Model.FirstName @Model.LastName @Model.Id</h2>

4. Model

输出的Model:ViewModel

输入的Model

用于输入的Model通常用来创建数据和修改数据,输入Model的方式有以下两种:

  • Form
  • 导航到含有Form的View

4.1 Form

使用Form提交Model,提交方式为post,如下例

<form method="post">
    <input type="text" name="FirstName" value="" />
    <input type="date" name="BirthDate" value="" />
</form>

其中input标签中的name属性值FirstName要和Model中的属性对应

更高级的写法是使用TagHelper的形式,可以自动匹配类型,如date类型。如果不放心可以再声明一遍

<input asp-for="FirstName" />
<input asp-for="BirthDate" type="date"/>

在Student类中添加枚举类型Gender,枚举类型使用TagHelper的方式如下:

<select asp-for="Gender" asp-items="Html.GetEnumSelectList<Tutorial.Web.Model.Gender>()">
</select>

在以post方式提交的时候,传入的参数为Student类型,asp.net mvc会想方设法把传入参数的属性补全,一般来说,id值自增就不传入id值,自动获取id值就会导致错误,于是使用post方式提交的时候,传入的参数使用重新定义的StudentCreateViewModel类。

//StudentCreateViewModel类
namespace Tutorial.Web.Model
{
    public class StudentCreateViewModel
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime BirthDate { get; set; }
        public Gender Gender { get; set; }
    }
}
//使用post方式提交,参数使用StudentCreateViewModel,
//然后使用repository服务将创建的学生信息添加到学生列表里。
[HttpPost]
public IActionResult Create(StudentCreateViewModel student)
{
    var newStudent = new Student
    {
        FirstName = student.FirstName,
        LastName = student.LastName,
        BirthDate = student.BirthDate,
        Gender = student.Gender
    };
    var newModel = repository.Add(newStudent);
    return View("Detail", newModel);
}

还需要注意一点的是,在StartUp中,要使用AddSingleton<>()的方式,在项目运行期间都是这一个实例,

不要使用AddScoped,AddScoped每次发出请求都是生成一个新的实例,会导致添加数据添加不上。

services.AddSingleton<IRepository<Student>, InMemoryRepository>();

还有一个问题:当添加成功之后,返回到detial页面,此时刷新网页,会发现id中自增1,这是因为刷新一次页面导致又发出了一个post请求,又产生了新的数据信息。可以使用重定向解决此问题。

//跳转到Detail页面需要传入id信息。
return RedirectToAction(nameof(Detail), new { id = newModel.Id });

4.2 Model验证

关于模型验证的详细内容,参见微软官方文档

使用post方法处理表单

第一步:首先要防止跨站请求伪造CSRF(Cross-site request forgery),需要在方法上方添加

[HttpPost]
[ValidateAntiForgeryToken]

第二步:将验证规则添加到数据模型

  • Required:必需属性
  • Display(Name = "xxx"):提示信息。如果不写则按照label标签中asp-for="xxx"的值
  • StringLength(60, MinimumLength = 3):规定长度
  • RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$"):验证属性是否满足自定义的正则表达式
  • [Range(0,100)]:范围限制
  • [DataType(DataType.Date)]指定日期类型,但是日期的格式不做规定。还可以对PasswordCurrency进行指定
  • [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]:指定日期格式
  • CreditCardEmailAddressPhoneurl等:验证属性是否具有这种格式

如下例

public class StudentCreateViewModel
{
    [Required]
    [Display(Name = "名")]
    public string FirstName { get; set; }

    [Display(Name = "姓")]
    public string LastName { get; set; }

    [Display(Name = "出生日期")]
    public DateTime BirthDate { get; set; }

    [Display(Name = "性别")]
    public Gender Gender { get; set; }
}

第三步:验证错误UI

在使用到Model的view视图中进行错误消息显示

可以在每个input框旁边添加一个span标签以显示错误信息

<span asp-validation-for="FirstName"></span>

还可以使用如下代码,对所有的错误验证信息进行输出

    <div asp-validation-summary="All"></div>
或  <div asp-validation-summary="ModelOnly"></div>

其中ModelOnly进队模型中出现的错误验证信息进行显示

第四步:在controller处理

使用if(ModelState.isValid),如果验证通过,执行后续的步骤

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(StudentCreateViewModel student)
{
    if (ModelState.IsValid)
    {
        var newStudent = new Student
        {
            FirstName = student.FirstName,
            LastName = student.LastName,
            BirthDate = student.BirthDate,
            Gender = student.Gender
        };
        var newModel = repository.Add(newStudent);
        //return View("Detail", newModel);
        return RedirectToAction(nameof(Detail), new { id = newModel.Id });

    }
    else
    {
        ModelState.AddModelError(string.Empty, "Model level Error!");
        return View();
    }
}

其中视图页面的例子如下

@using Tutorial.Web.Model
@model StudentCreateViewModel

<h1>创建学生</h1>

<form method="post">
    <div>
        <div>
            <label asp-for="FirstName"></label>
            <input asp-for="FirstName" />
            <span asp-validation-for="FirstName"></span>
        </div>
        <div>
            <label asp-for="LastName"></label>
            <input asp-for="LastName" />
            <span asp-validation-for="LastName"></span>
        </div>
        <div>
            <label asp-for="BirthDate"></label>
            <input asp-for="BirthDate" type="date" />
            <span asp-validation-for="BirthDate"></span>
        </div>
        <div>
            <label asp-for="Gender"></label>
            <select asp-for="Gender" asp-items="Html.GetEnumSelectList<Tutorial.Web.Model.Gender>()">
            </select>
        </div>
    </div>
    <div asp-validation-summary="ModelOnly"></div>

    <button type="submit" name="save">保存</button>
</form>

5. 集成EntityFramework Core

微软官方文档可以查看更详细的信息。

用于对象关系映射ORM

支持的数据库有:

  • MSSQL localDB(在windows下开发,如使用visual studio时)
  • PostgreSQL(在Linux下开发)
  • MySQL/MariaDB
  • Oracle
  • DB2
  • SQLite
  • In Memory

第一步:在appsettings.json文件中添加数据库链接字符串

"ConnectionStrings": {
    "DefaultConnection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=Tutorial;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
    "CustomConnectionXXX": "Data Source=..."
}

可以添加多个链接,需要用的时候指定名称就行了。

如果使用Visual studio开发,使用的数据库是MSSQLLocalDB,我们只需要指定Initial Catalog的值就行了,生成的数据表就会存到这个文件夹里。

第二步:创建数据库上下文

需要一个数据库上下文类来协调 Student 模型的 EF Core 功能(创建、读取、更新和删除)。 数据库上下文派生自 Microsoft.EntityFrameworkCore.DbContext 并指定要包含在数据模型中的实体。

使用以下代码添加Data/DataContext.cs文件

namespace Tutorial.Web.Data
{
    public class DataContext: DbContext
    {
        public DataContext (DbContextOptions<DataContext> options): base(options)
        {
        }
        public DbSet<Student> Students { get; set; }
    }
}

前面的代码为实体集创建DbSet<Student>属性。 在实体框架术语中,实体集通常与数据表相对应。 实体对应表中的行。

DbSet Class用于TEntity实例的查询和保存,对应于DbSet的LINQ查询会被转换成数据库查询

第三步:注册数据库上下文

使用依赖注入的方式,需要在Startup.cs文件中添加服务。

要从appsettings.json文件中获取字符串,可以使用Startup()构造函数中使用IConfiguration服务,然后在ConfigureServices()使用该IConfiguration服务

public class Startup
{
    private readonly IConfiguration configuration;

    public Startup(IConfiguration configuration)
    {
        this.configuration = configuration;
    }
    public void ConfigureServices(IServiceCollection services)
    {
        //获取数据库链接的两种方式
        //var connectionString =configuration["ConnectionStrings:DefaultConnection"];
        string connectionString = configuration.GetConnectionString("DefaultConnection");
        
        services.AddDbContext<DataContext>(options =>
        {
            options.UseSqlServer(connectionString);
        });
        
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        services.AddSingleton<IRepository<Student>, InMemoryRepository>();
    }

第四步:迁移

迁移是可用于创建更新数据库以匹配数据模型的一组工具。使用visual studio中的Package Manage Console。从“工具”菜单中选择“NuGet 包管理器”>“包管理器控制台 (PMC)” 。

使用如下命令:

Add-Migration InitialDB

然后就会生成 Migrations/{timestamp}_InitialDB.cs 迁移文件,InitialDB 参数是迁移名称, 可以使用任何名称。改文件中的Up 方法创建 Movie 表,并将 Id 配置为主键。 Down 方法可还原 Up 迁移所做的架构更改。

然后输入命令

Update-Database

将数据库更新到上一个命令创建的最新迁移。 此命令执行上述文件中的 Up 方法 。

这样的话,数据表就创建好了

第五步:使用数据库

新建EfCoreRepository服务,实现IRepository接口

namespace Tutorial.Web.Services
{
    public class EfCoreRepository : IRepository<Student>
    {
        private readonly DataContext _context;

        public EfCoreRepository(DataContext context)
        {
            _context = context;
        }

        public IEnumerable<Student> GetAll()
        {
            return _context.Students.ToList();
        }

        public Student GetById(int id)
        {
            return _context.Students.Find(id);
        }

        public Student Add(Student newModel)
        {
            _context.Students.Add(newModel);
            _context.SaveChanges();
            return newModel;
        }
    }
}

将以上服务注册到Startup,使用AddScoped()方法,防止多线程问题。

services.AddScoped<IRepository<Student>, EfCoreRepository>();

然后就可以用了

6. 布局

6.1 _ViewStart.cshtml

在每个视图或页面之前运行的代码应置于 _ViewStart.cshtml 文件中。其直接放在Views文件夹下

如果_ViewStart.cshtml 文件中写了如下代码,则为Views文件夹下的所有页面都采用_Layout布局

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

也可以简写成

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

每个页面也可以具体指定要引用哪个布局

6.2 _ViewImports.cshtml

视图和页面可以使用 Razor 指令来导入命名空间并使用依赖项注入。

_ViewImports.cshtml 文件可以放在任何文件夹中,在这种情况下,它只会应用于该文件夹及其子文件夹中的页面或视图。 从根级别开始处理 _ViewImports 文件,然后处理在页面或视图本身的位置之前的每个文件夹。 可以在文件夹级别覆盖根级别指定的 _ViewImports 设置。

_ViewImports 文件支持以下指令:

  • @addTagHelper
  • @removeTagHelper
  • @tagHelperPrefix
  • @using
  • @model
  • @inherits
  • @inject

示例文件

@using WebApplication1.Models
@using Microsoft.AspNetCore.Identity
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

6.3 _Layout.cshtml

布局文件

其他文件可以用@RenderBody()来使用此布局文件

布局也可以通过调用 RenderSection 来选择引用一个或多个节

@RenderSection("Scripts", required: false)

6.4 Partial View

分部视图:复用View代码

ASP.NET Core 中的分部视图

6.5 View Component

视图组件与分部视图类似,但它们的功能更加强大。

ASP.NET Core 中的视图组件

7. 安装前端库

在项目文件下添加npm配置文件package.json,内容如下

{
  "version": "1.0.0",
  "name": "tutorial",
  "private": true,
  "devDependencies": {
  },
  "dependencies": {
    "bootstrap": "4.4.1",
    "jquery": "3.3.1",
    "jquery-validation": "1.19.0",
    "jquery-validation-unobtrusive": "3.2.10"
  }
}

点击保存之后,依赖项里就会多出依赖的包。

其中jquery-validationjquery-validation-unobtrusive用于前端验证

显示项目的隐藏文件,会发现其实依赖库是安装在了项目文件夹下node_modules文件夹中,而不是wwwroot文件夹中,那么如何伺服这些静态文件呢。

参见 asp.net core 系列之静态文件

可以在Startup.cs中指定,添加如下语句

app.UseStaticFiles(new StaticFileOptions
	{
		RequestPath = "/node_modules",
		FileProvider = new PhysicalFileProvider(Path.Combine(env.ContentRootPath,"node_modules"))
	});

打开_layout.cshtml页面,添加如下代码

<link href="~/node_modules/bootstrap/dist/css/bootstrap.css" rel="stylesheet" />

也可以使用CDN,如BootCDN

一般建议在开发环境下使用本地的库,其他环境下使用CDN库

<!--开发环境-->
<environment include="Development">
    <link href="~/node_modules/bootstrap/dist/css/bootstrap.css" rel="stylesheet" />
</environment>
<!--非开发环境-->
<environment exclude="Development">
	<link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap-grid.css" rel="stylesheet"></environment>

8. ASP.NET CORE IDENTITY

参见官方文档:ASP.NET Core 身份验证概述

  • 身份认证和授权系统
  • 成员管理
  • 默认使用MSSQL
  • 支持外部的Provider

使用ASP.NET CORE IDENTITY

  1. 需要登录注册的view

    Register页面和Login页面

  2. AccountController

    实现注册、登录、登出

    • 跳转到注册或登录页面——GET
    • 提交注册或登录信息——POST
    • 登出——POST
  3. Model

    使用LoginViewModel和RegisterViewModel

ASP.NET CORE IDENTITY重点类

  1. UserManager< IdentityUser>
  2. SignInManager< IdentityUser>

配置identity服务

在startup类中的ConfigureServices方法中添加如下代码

//Identity
services.AddDbContext<IdentityDbContext>(options =>
{
	options.UseSqlServer(connectionString, b => b.MigrationsAssembly("Tutorial.Web"));
});
services.AddDefaultIdentity<IdentityUser>().AddEntityFrameworkStores<IdentityDbContext>();
services.Configure<IdentityOptions>(options =>
{
	// 密码设置,为了简单起见,先不对密码做任何限制。
	options.Password.RequireDigit = false;
	options.Password.RequireLowercase = false;
	options.Password.RequireNonAlphanumeric = false;
	options.Password.RequireUppercase = false;
	options.Password.RequiredLength = 0;
	options.Password.RequiredUniqueChars = 0;

	// Lockout settings.
	options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
	options.Lockout.MaxFailedAccessAttempts = 5;
	options.Lockout.AllowedForNewUsers = true;

	// User settings.
	options.User.AllowedUserNameCharacters =
		"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
	options.User.RequireUniqueEmail = false;
});

通过调用UseAuthentication来启用标识。 UseAuthentication 将身份验证中间件添加到请求管道。

在Startup类中的Configure方法中,app.UseMve()之前添加app.UseAuthentication();

迁移

由于有多个DbContext,所以需要使用-Context指定具体的Context

Add-Migration InitialIdentity -Context IdentityDbContext
Update database -Context IdentityDbContext

在视图中判断是否登录

@inject SignInManager<IdentityUser> SignInManager
    ......
<nav class="navbar navbar-light bg-light">
	<a class="navbar-brand" href="#">Navbar</a>
	@if (SignInManager.IsSignedIn(User))
	{
		<form asp-controller="Account" asp-action="Logout" method="post" id="LogoutForm">
			<ul class="navbar-nav mr-auto">
				<li class="nav-item">
					<a href="javascript:document.getElementById('LogoutForm').submit()">登出</a>
				</li>
			</ul>
		</form>
	}
	else
	{
		<ul class="navbar-nav mr-auto">
			<li class="nav-item">
				<a asp-controller="Account" asp-action="Register">注册</a>
			</li>
			<li class="nav-item">
				<a asp-controller="Account" asp-action="Login">登陆</a>
			</li>
		</ul>
	}
</nav>
    ......

用户登录后才能创建学生,需要在HomeController类中的两个Create方法上面添加[Authorize]

[Authorize]
[HttpGet]
public IActionResult Create()
{
    return View();
}
发布了108 篇原创文章 · 获赞 22 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/GoSantiago/article/details/103605106