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();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章