Asp.net Core 入門實戰
Asp.Net Core 是開源,跨平台,模塊化,快速而簡單的Web框架.
Asp.net Core官網的一個源碼合集,方便一次性Clone
目錄
持續更新,也可以通過我的網站訪問,歡迎探討交流
快速入門
安裝
查看dotnet sdk 版本
$ dotnet --version` 2.1.4
創建項目目錄
$ mkdir study $ cd study
使用 dotnet new
命令創建項目
$ dotnet new console -n Demo $ cd Demo
在 VS Code中打開Demo文件夾
一個最小的應用
打開 Program.cs
using System; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; namespace Demo { class Program { static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel() .UseStartup<Startup>() .Build(); host.Run(); } } class Startup { public void Configure(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Hello, World!"); }); } } }
安裝相關的Nuget包
$ dotnet add package Microsoft.AspNetCore.Hosting --version 2.0.1 $ dotnet add package Microsoft.AspNetCore.Server.Kestrel --version 2.0.1
下載依賴項
$ dotnet restore
運行
$ dotnet run
輸出結果:
Hosting environment: Production Content root path: C:\netcore\study\Demo\bin\Debug\netcoreapp2.0\ Now listening on: http://localhost:5000 Application started. Press Ctrl+C to shut down.
項目模板
根據指定的模板,創建新的項目、配置文件或解決方案。
$ dotnet new -h 使用情況: new [選項] 選項: -h, --help 顯示有關此命令的幫助。 -l, --list 列出包含指定名稱的模板。如果未指定名稱,請列出所有模板。 -n, --name 正在創建輸出的名稱。如果未指定任何名稱,將使用當前目錄的名 稱。 -o, --output 要放置生成的輸出的位置。 -i, --install 安裝源或模板包。 -u, --uninstall 卸載一個源或模板包。 --type 基於可用的類型篩選模板。預定義的值為 "project"、"item" 或 "other"。 --force 強制生成內容,即使該內容會更改現有文件。 -lang, --language 指定要創建的模板的語言。 模板 短名稱 語言 標記 -------------------------------------------------------------------------------- ------------------------ Console Application console [C#], F#, VB Common/Console Class library classlib [C#], F#, VB Common/Library Unit Test Project mstest [C#], F#, VB Test/MSTest xUnit Test Project xunit [C#], F#, VB Test/xUnit ASP.NET Core Empty web [C#], F# Web/Empty ASP.NET Core Web App (Model-View-Controller) mvc [C#], F# Web/MVC ASP.NET Core Web App razor [C#] Web/MVC/Razor Pages ASP.NET Core with Angular angular [C#] Web/MVC/SPA ASP.NET Core with React.js react [C#] Web/MVC/SPA ASP.NET Core with React.js and Redux reactredux [C#] Web/MVC/SPA ASP.NET Core Web API webapi [C#], F# Web/WebAPI global.json file globaljson Config NuGet Config nugetconfig Config Web Config webconfig Config Solution File sln Solution Razor Page page Web/ASP.NET MVC ViewImports viewimports Web/ASP.NET MVC ViewStart viewstart Web/ASP.NET Examples: dotnet new mvc --auth Individual dotnet new page --namespace dotnet new --help
使用dotnet new
選擇 console 模板 創建項目
$ dotnet new console -n Demo $ cd Demo
創建后的項目文檔結構是
.
├── Demo
│ ├── Demo.csproj
│ └── Program.cs
路由
啟用,配置路由
using System; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; namespace Demo { class Startup { public void ConfigureServices (IServiceCollection services) { //啟用路由 services.AddRouting (); } public void Configure (IApplicationBuilder app) { //配置路由 app.UseRouter (routes => { //根路由 (/) routes.MapGet("", context => { return context.Response.WriteAsync("Hello, World!"); }); //在根路由 (/) 上,配置 /hello Get請求的路由 routes.MapGet ("hello", context => { return context.Response.WriteAsync ("Got a Get request at /hello"); }); }); } } }
安裝相關的Nuget包
$ dotnet add package Microsoft.AspNetCore.Routing --version 2.0.1
下載依賴項
$ dotnet restore
運行
$ dotnet run
訪問 http://localhost:5000/hello ,輸出結果:
Got a Get request at /hello
對 /user 路由的 POST 請求進行響應:
routes.MapPost("hello", context => { return context.Response.WriteAsync ("Got a Post request at /hello"); })
對 /user 路由的 PUT 請求進行響應:
routes.MapPut("hello", context => { return context.Response.WriteAsync ("Got a Put request at /hello"); })
對 /user 路由的 DELETE 請求進行響應:
routes.MapDelete("hello", context => { return context.Response.WriteAsync ("Got a Dlete request at /hello"); })
靜態文件
啟用,配置靜態資源
創建 wwwroot文件夾
$ mkdir wwwroot $ cd wwwroot
復制 index.html 到 wwwroot目錄
$ mkdir images $ cd images
復制 favicon.png 到 images文件夾下
項目結構圖如下:
.
├── Demo │ ├── wwwroot │ │ ├── images │ │ │ ├── favicon.png │ │ ├── index.html │ ├── Demo.csproj │ └── Program.cs
更改 Program.cs,設置ContentRoot路徑為當前項目根目錄,啟用靜態資源
using System; using System.IO; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; namespace Demo { class Program { static void Main (string[] args) { Console.WriteLine (AppContext.BaseDirectory); var host = new WebHostBuilder () .UseKestrel () //設置 ContentRoot, ContentRoot是任何資源的根路徑,比如頁面和靜態資源 .UseContentRoot(Directory.GetCurrentDirectory()) .UseStartup<Startup>() .Build (); host.Run (); } } class Startup { public void Configure (IApplicationBuilder app) { //使用靜態資源,默認的目錄是 /wwwroot app.UseStaticFiles (); } } }
運行
http://localhost:5000/index.html
http://localhost:5000/images/favicon.png
配置其他文件夾
新建文件夾myfiles
$ mkdir myfiles $ cd myfiles
創建一個新的 index.html到myfiles文件夾,配置並使用myfiles文件夾
app.UseStaticFiles (new StaticFileOptions { FileProvider = new PhysicalFileProvider ( Path.Combine (Directory.GetCurrentDirectory (), "myfiles")), RequestPath = "/myfiles" });
運行
http://localhost:5000/myfiles/index.html
頁面渲染
Razor模板引擎
Asp.net Core自帶的模板引擎是 Razor模板引擎,傳統的Razor頁面以cshtml結尾, 閱讀Razor語法
遺憾的是Razor模板引擎在Asp.net Core中封裝在了MVC模塊之中,需要做一些擴展才可以單獨拿出來使用
創建模板頁面
創建 views文件夾
$ mkdir views $ cd views
創建 index.cshtml 頁面
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> </head> <body> <ul> @for (int i = 0; i < 5; i++) { <li>第@(i + 1)行</li> } </ul> </body> </html>
使用模板
using System; using System.IO; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; namespace Demo { class Program { static void Main(string[] args) { Console.WriteLine(AppContext.BaseDirectory); var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseStartup<Startup>() .Build(); host.Run(); } } class Startup { public void ConfigureServices(IServiceCollection services) { //啟用Mvc services.AddMvc(); //擴展類 services.AddSingleton<RazorViewRenderer>(); } public void Configure(IApplicationBuilder app) { app.UseRouter(routes => { //根路徑 / routes.MapGet("", context => { return context.Render("/views/index.cshtml"); } }); } } }
安裝相關的Nuget包
$ dotnet add package Microsoft.AspNetCore.Mvc --version 2.0.2
下載依賴項
$ dotnet restore
運行
$ dotnet run
結果
第1行
第2行
第3行
第4行
第5行
渲染數據
新增實體類 UserInfo
public class UserInfo { public string Name { get; set; } public int Age { get; set; } }
新增user.cshtml頁面
@using Demo; @model UserInfo <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> </head> <body> <ul> <li>姓名:@Model.Name</li> <li>年齡:@Model.Age</li> </ul> </body> </html>
更改Program.cs
app.UseRouter(routes =>
{
//根路徑 / routes.MapGet("", context => { return context.Render("/views/index.cshtml"); } routes.MapGet("/user", context => { return context.Render("/views/user.cshtml", new UserInfo() { Name = "張三", Age = 18 }); }); });
運行
$ dotnet run
結果
姓名:張三
年齡:18
請求數據
QueryString
var queryCollection = context.Request.Query; foreach (var item in queryCollection) { Console.WriteLine(item.Key + ":" + item.Value); }
Form
if (context.Request.ContentType.ToLower().Contains("application/x-www-form-urlencoded") { var formCollection = context.Request.Form; foreach (var item in formCollection) { Console.WriteLine(item.Key + ":" + item.Value); } }
Files
var fileCollections = context.Request.Form.Files; var rootPath = context.RequestServices.GetService<IHostingEnvironment>().ContentRootPath; foreach (var item in fileCollections) { var path = Path.Combine(rootPath,item.FileNa using (var stream = new FileStream(path, FileMode.Create)) { await item.CopyToAsync(stream); } Console.WriteLine(item.FileName + ":" + item.Length); }
Header
var headerCollection = context.Request.Headers; foreach (var item in headerCollection) { Console.WriteLine(item.Key + ":" + item.Value); }
Body
StreamReader reader = new StreamReader(context.Request.Body); string text = reader.ReadToEnd();
Cookies
var cookieCollection=context.Request.Cookies; foreach (var item in cookieCollection) { Console.WriteLine(item.Key + ":" + item.Value); }
錯誤和重定向
重定向
context.Response.Redirect("path")
狀態代碼頁
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using System; using System.IO; using System.Text.Encodings.Web; namespace Demo { class Program { static void Main(string[] args) { Console.WriteLine(AppContext.BaseDirectory); var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseStartup<Startup>() .Build(); host.Run(); } } class Startup { public void ConfigureServices(IServiceCollection services) { //啟用路由 services.AddRouting(); services.AddMvc(); services.AddSingleton<RazorViewRenderer>(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { //啟用狀態碼頁 app.UseStatusCodePages(); app.UseRouter(routes => { //根路徑 / routes.MapGet("", context => { return context.Render("/views/index.cshtml"); } }); } } }
自定義狀態碼頁
新增 404.cshtml
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> </head> <body> <h1> Oops! Page Not Found ! </h1> </body> </html>
配置和使用自定義的狀態碼頁
app.UseStatusCodePagesWithRedirects("/status/{0}"); app.UseRouter(routes => { //"" routes.MapGet("", context => { return context.Response.WriteAsync("root path"); }); routes.MapGet("status/{code}", (request, response, routeData) => { var statusCodePagesFeature = request.HttpContext.Features.Get<IStatusCodePagesFeature>(); var code = routeData.Values["code"]; if (statusCodePagesFeature != null && code!=null && code.ToString() == "404") { //跳轉到自定義的404頁面 return request.HttpContext.Render("/views/404.cshtml"); } else { return response.WriteAsync(HtmlEncoder.Default.Encode("狀態碼:"+ routeData.Values["code"])); } }); });
異常錯誤頁
開發異常頁面,Asp.net Core自帶
app.UseDeveloperExceptionPage();
新增測試路由,拋出空指針異常
env.EnvironmentName = EnvironmentName.Development;
if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/error"); } app.UseRouter(routes => { //"" routes.MapGet("", context => { return context.Response.WriteAsync("root path"); } //test routes.MapGet("test", context => { throw new Exception("空指針異常!!!"); }); });
訪問http://localhost:57393/test,得到下面的頁面
自定義異常頁面
env.EnvironmentName = EnvironmentName.Production;
if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { //自定義異常錯誤頁面 app.UseExceptionHandler("/error"); } app.UseRouter(routes => { //"" routes.MapGet("", context => { return context.Response.WriteAsync("root path"); } //test routes.MapGet("test", context => { throw new Exception("空指針異常!!!"); }); //自定義異常錯誤頁面 routes.MapGet("error", context => { context.Response.StatusCode = 500; context.Response.ContentType = "text/html"; var error = context.Features.Get<IExceptionHandlerFeature>(); if (error != null) { return context.Response.WriteAsync("<h1>" + HtmlEncoder.Default.Encode("發生了錯誤: " + error.Error.Messa + "</h1>"); } else { return context.Response.WriteAsync("<h1>" + HtmlEncoder.Default.Encode("Oops! 發生了錯誤,請聯系管理員")"</h1>"); } }); });
關於響應
會話
Session
新增登陸頁面login.cshtml
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> </head> <body> <form action="/login" method="post"> <div> <label>用戶名:</label> <input type="text" name="UserName" /> </div> <div> <label>密碼:</label> <input type="password" name="PassWord" /> </div> <div> <input type="submit" value="登陸" /> </div> </form> </body> </html>
添加並啟用Session,輸入用戶名和密碼,點擊登陸,保持Session數據,登陸成功跳轉到/home路由頁面,訪問Session數據,顯示當前登陸的用戶名
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using System; using System.IO; using System.Threading.Tasks; namespace Demo { class Program { static void Main(string[] args) { Console.WriteLine(AppContext.BaseDirectory); var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseStartup<Startup>() .Build(); host.Run(); } } class Startup { public void ConfigureServices(IServiceCollection services) { //啟用路由 services.AddRouting(); services.AddMvc(); services.AddSingleton<RazorViewRenderer>(); //增加Session services.AddSession(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseDeveloperExceptionPage(); // 啟用Session app.UseSession(); app.UseRouter(routes => { routes.MapGet("", (request, response, routeData) => { return request.HttpContext.Render("/views/login.cshtml"); }); routes.MapGet("home", context => { string userName = context.Session.GetString("userName"); if (!string.IsNullOrEmpty(userName)) { return context.Response.WriteAsync("User:" + userName); } else { context.Response.Redirect("/"); return Task.CompletedTask; } }); routes.MapPost("login", context => { var userName = context.Request.Form["UserName"].ToString(); var password = context.Request.Form["PassWord"].ToString(); if (userName.Equals("admin") && password.Equals("123456")) { context.Session.SetString("userName", password); context.Response.Redirect("/home"); return Task.CompletedTask; } else { throw new Exception("用戶名或密碼錯誤"); } }); }); } } }
日志
安裝Nuget包
$ dotnet add package Microsoft.Extensions.Logging --version 2.0.0 $ dotnet add package Microsoft.Extensions.Logging.Console --version 2.0.0
下載依賴項
$ dotnet restore
添加日志模塊
//啟用日志 services.AddLogging(builder=> { //使用控制台日志提供程序 builder.AddConsole(); });
創建ILogger實例
var logger = context.RequestServices.GetService<ILogger<Startup>> (); //記錄日志 logger.LogInformation("before hello world");
日志分類
日志的類別,可以是任意字符串,默認是完全限定的類名,例如: Demo.Startup
日志級別
logger.LogTrace(); logger.LogDebug(); logger.LogInformation(); logger.LogWarning(); logger.LogError(); logger.LogCritical();
日志過濾
給日志分類為 Demo.Startup的日志類別限定最小的日志級別是LogLevel.Information
services.AddLogging(builder=> {
builder.AddFilter("Demo.Startup", LogLevel.Information); builder.AddConsole(); });
訪問更多關於日志類別,級別,模板,過濾,作用域的說明日志類別
配置
添加配置
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; namespace Demo { class Program { static void Main(string[] args) { var configBuilder = new ConfigurationBuilder(); configBuilder.AddInMemoryCollection(new Dictionary<string, string>() { { "name","zhangsan"}, { "age","18"} }); var config = configBuilder.Build(); var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .UseConfiguration(config) .UseStartup<Startup>() .Build(); host.Run(); } } }
讀取配置
app.UseRouter(routes =>
{
routes.MapGet("", context => { var config = context.RequestServices.GetService<IConfiguration>(); string name = config["name"]; string age = config["age"]; return context.Response.WriteAsync("name:"+name+",Age:"+age); }); });
國際化
添加依賴的Nuget包
$ dotnet add package Microsoft.Extensions.Localization --version 2.0.1
下載依賴項
$ dotnet restore
資源文件命名規則
命名規則 {分類名稱}.{語言區域名稱}.resx
- 分類名稱:默認的分類名稱就是類型的FullName,在AssemblyName后的相對路徑,比如Demo.Startup,程序集的名稱Demo,就剩下了Startup
- 語言區域名稱: 語言區域名稱有zh-CN,en-US,更多
特殊條件下你也可以將資源文件和類定義文件放在同一個目錄,這樣資源文件的分類名稱就是{類名稱}.{語言區域名稱}.resx
添加資源文件
- 新建 myresources文件夾
- 新建 Startup.en-US.resx 新增項 name是sayhi,value是 HelloWorld
- 新建 Startup.zh-CN.resx 新增項 name是sayhi,value是 世界你好
添加本地化
services.AddLocalization(options =>
options.ResourcesPath = "myresources"; });
啟用本地化
var cultureList = new List<CultureInfo>() { new CultureInfo("en-US"), new CultureInfo("zh-CN") }; var options = new RequestLocalizationOptions() { SupportedCultures = cultureList, SupportedUICultures = cultureList, DefaultRequestCulture = new RequestCulture("zh-CN") }; //新建基於Query String的多語言提供程序 var provider = new QueryStringRequestCultureProvider(); provider.QueryStringKey = "culture"; provider.UIQueryStringKey = "uiculture"; //刪除所有多語言提供程序 options.RequestCultureProviders.Clear(); options.RequestCultureProviders.Add(provider);
資源文件讀取
var stringLocalizer = context.RequestServices.GetService<IStringLocalizer<Startup>>(); string sayhi = stringLocalizer["sayhi"];
訪問 http://localhost:57393/?culture=en-US 切換為英文版本,默認的語言是中文
多語言提供程序
- 通過Query String,[默認自帶]
- 通過Cookie,[默認自帶]
- 通過Accept-Language header,[默認自帶]
- 通過RouteData
模板格式化
string username="張三"; string localizedString = _localizer["Hello {0}!", username];