七天學會ASP.NET MVC (六)——執行緒問題、異常處理、自定義URL
本文參考自:http://www.codeproject.com/Articles/1002109/Learn-MVC-Project-in-days-Day-6
轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。
本節又帶了一些常用的,卻很難理解的問題,本節從檔案上傳功能的實現引出了執行緒使用,介紹了執行緒飢餓的解決方法,異常處理方法,瞭解RouteTable自定義路徑 。
系列文章
目錄
實驗27——新增批量上傳選項
關於實驗27
實驗27存在的問題
解決方法
實驗28——解決執行緒飢餓問題
實驗29——異常處理—顯示自定義錯誤頁面
關於實驗29
理解實驗29中的限制
實驗30—異常處理—日誌異常
關於實驗30
理解RouteTable
理解Asp.net MVC 請求週期
實驗31—實現使用者友好URLs
關於實驗31
總結
實驗27——新增批量上傳選項
在實驗27中,我們將提供一個選項,供使用者選擇上傳Employee記錄檔案(CSV格式)。
我們會學習以下知識:
1. 如何使用檔案上傳控制元件
2. 非同步控制器
1. 建立 FileUploadViewModel
在ViewModels資料夾下新建類“FileUploadViewModel”,如下:
1: public classFileUploadViewModel: BaseViewModel
2: {
3: public HttpPostedFileBase fileUpload {get; set ;}
4: }
HttpPostedFileBase 將通過客戶端提供上傳檔案的訪問入口。
2. 建立 BulkUploadController 和Index action 方法
新建 controller“BulkUploadController”,並實現Index Action 方法,如下:
1: public class BulkUploadController : Controller
2: {
3: [HeaderFooterFilter]
4: [AdminFilter]
5: public ActionResult Index()
6: {
7: return View(new FileUploadViewModel());
8: }
9: }
Index方法與 HeaderFooterFilter 和 AdminFilter屬性繫結。HeaderFooterFilter會確保頁首和頁尾資料能夠正確傳遞到ViewModel中,AdminFilter限制非管理員使用者的訪問。
3.建立上傳View
建立以上Action方法的View。View名稱應為 index.cshtml,且存放在“~/Views/BulkUpload”資料夾下。
4. 設計上傳View
在View中輸入以下內容:
1: @using WebApplication1.ViewModels
2: @model FileUploadViewModel
3: @{
4: Layout = "~/Views/Shared/MyLayout.cshtml";
5: }
6:
7: @section TitleSection{
8: Bulk Upload
9: }
10: @section ContentBody{
11: <div>
12: <a href="/Employee/Index">Back</a>
13: <form action="/BulkUpload/Upload" method="post" enctype="multipart/form-data">
14: Select File : <input type="file" name="fileUpload" value="" />
15: <input type="submit" name="name" value="Upload" />
16: </form>
17: </div>
18: }
如上,FileUploadViewModel中屬性名稱與 input[type="file"]的名稱類似,都稱為“fileUpload”。我們在Model Binder中已經講述了名稱屬性的重要性,注意:在表單標籤中,有一個額外的屬性是加密的,會在實驗結尾處講解。
5. 建立業務層上傳方法
在 EmployeeBusinessLayer中新建方法 UploadEmployees,如下:
1: public void UploadEmployees(List<Employee> employees)
2: {
3: SalesERPDAL salesDal = new SalesERPDAL();
4: salesDal.Employees.AddRange(employees);
5: salesDal.SaveChanges();
6: }<employee>
7: </employee>
6. 建立Upload Action 方法
建立Action 方法,並命名為 “BulkUploadController”,如下:
1: [AdminFilter]
2: public ActionResult Upload(FileUploadViewModel model)
3: {
4: List<Employee> employees = GetEmployees(model);
5: EmployeeBusinessLayer bal = new EmployeeBusinessLayer();
6: bal.UploadEmployees(employees);
7: return RedirectToAction("Index","Employee");
8: }
9:
10: private List<Employee> GetEmployees(FileUploadViewModel model)
11: {
12: List<Employee> employees = new List<Employee>();
13: StreamReader csvreader = new StreamReader(model.fileUpload.InputStream);
14: csvreader.ReadLine(); // Assuming first line is header
15: while (!csvreader.EndOfStream)
16: {
17: var line = csvreader.ReadLine();
18: var values = line.Split(',');//Values are comma separated
19: Employee e = new Employee();
20: e.FirstName = values[0];
21: e.LastName = values[1];
22: e.Salary = int.Parse(values[2]);
23: employees.Add(e);
24: }
25: return employees;
26: }
AdminFilter會繫結到Upload action方法中,限制非管理員使用者的訪問。
7. 建立BulkUpload連結
開啟 “Views/Employee”資料夾下的 AddNewLink.cshtml 檔案,輸入BulkUpload連結,如下:
<a href="/Employee/AddNew">Add New</a> <a href="/BulkUpload/Index">BulkUpload</a>
8.執行
8.1 建立一個樣本檔案來測試,如圖所示
8.2 執行,點選BulkUpload連結
選擇檔案並點選確認
關於實驗 27
為什麼在實驗27中不需要驗證?
在該選項中新增客戶端和伺服器端驗證需要讀者自行新增的,以下是新增驗證的提示:
- 伺服器端驗證可使用Data Annotations。
- 客戶端驗證可利用客戶端的資料解釋和執行jQuery的驗證。必須手動設定自定義資料屬性,因為並沒有將Htmlhelper 方法設定為檔案輸入。
- 客戶端驗證可編寫JavaScript 程式碼,通過點選按鈕來實現。這個方法並不是很難,由於檔案輸入是由輸入控制元件完成,值可以在JavaScript中獲取及驗證 。
什麼是 HttpPostedFileBase?
HttpPostedFileBase將通過客戶端提供檔案上傳的訪問入口,Model Binder 會在Post請求期間更新 FileUploadViewModel類中的所有屬性值。我們在FileUploadViewModel內部只有一個屬性,Model Binder會通過客戶端設定它實現檔案上傳。
是否會提供多檔案的輸入控制元件?
是,有兩種方法可以實現:
1. 建立多檔案輸入控制元件,每個控制元件有唯一的名稱,FileUploadViewModel類會為每個控制元件建立 HttpPostedFileBase型別的屬性,每個屬性名稱應該與控制元件名稱匹配。
2. 建立多檔案輸入控制元件,每個控制元件有相同的名稱,建立型別的List列表,代替建立多個HttpPostedFileBase型別的屬性。
enctype="multipart/form-data" 是用來做什麼的?
該屬性指定了post 資料的編碼型別,預設屬性值是”application/x-www-form-urlencoded“
例1—登入窗體會給伺服器傳送以下Post 請求
1: POST /Authentication/DoLogin HTTP/1.1
2: Host: localhost:8870
3: Connection: keep-alive
4: Content-Length: 44
5: Content-Type: application/x-www-form-urlencoded
6: ...
7: ...
8: UserName=Admin&Passsword=Admin&BtnSubmi=Login
所有輸入值會被作為傳送的值的一部分,以”key/value“的形式傳送。
當 enctype="multipart/form-data" 屬性被加入Form標籤中,以下post 請求會被髮送到伺服器。
1: POST /Authentication/DoLogin HTTP/1.1
2: Host: localhost:8870
3: Connection: keep-alive
4: Content-Length: 452
5: Content-Type: multipart/form-data; boundary=----WebKitFormBoundarywHxplIF8cR8KNjeJ
6: ...
7: ...
8: ------WebKitFormBoundary7hciuLuSNglCR8WC
9: Content-Disposition: form-data; name="UserName"
10:
11: Admin
12: ------WebKitFormBoundary7hciuLuSNglCR8WC
13: Content-Disposition: form-data; name="Password"
14:
15: Admin
16: ------WebKitFormBoundary7hciuLuSNglCR8WC
17: Content-Disposition: form-data; name="BtnSubmi"
18:
19: Login
20: ------WebKitFormBoundary7hciuLuSNglCR8WC--
如上所示,Form會在多部分post傳送,每部分都是被分界線分割的,每部分包含單值。
如果form標籤包含檔案輸入控制元件的話,enctype必須被設定為”multipart/form-data“。
為什麼有時候需要設定 encType 為 “multipart/form-data”,而有時候不需要設定?
當encType 設定為”multipart/form-data“,將會實現Post資料和上傳檔案的功能,當然也會增加請求的size 增加,請求size 越大意味著效能越低。因此得出的最佳實踐經驗需要設定為預設的”application/x-www-form-urlencoded“。
為什麼在實驗27中建立ViewModel?
在View中已經有一個控制元件了,我們需要通過直接新增 HttpPostedFileBase型別的引數,並命名為”fileUpload“實現相同的結果,從而替代建立獨立的ViewModel。
1: public ActionResult Upload(HttpPostedFileBase fileUpload)
2: {
3: }
建立 ViewModel是最好的方法,Controller應該以 ViewModel的形式給View傳送資料,且資料必須來自Controller。
以上問題的解決方法
是否存在疑慮,當傳送請求時,如何獲取響應?
眾人皆知的程式設計規則,程式中任何事件都是由執行緒執行的,請求事件也是。
Asp.net framework 維護執行緒池,每次當請求傳送到webserver時,會從執行緒池中分配空閒的執行緒處理此請求。這種執行緒被稱為worker執行緒。
當請求處理完成,該執行緒無法服務其他請求時,worker 執行緒會被阻塞。現在我們來了解什麼是執行緒飢餓,如果一個應用程式接收到很多請求,且處理每個請求都非常耗時。在這種情況下,我們就必須指定一個點來結束請求,當有新的請求進入狀態時,沒有worker 執行緒可使用,這種現象稱為執行緒飢餓。
在我們的示例程式中只包含2個員工記錄,而在實際使用情況下,會包含成千上萬的記錄,這就意味著將耗費大量的時間來處理請求。這種情況就可能導致執行緒飢餓.
執行緒飢餓的解決方法:
截至現在我們討論的請求型別都是同步請求。如果使用非同步請求來代替同步請求,那麼執行緒飢餓的問題就得到解決了。
- 非同步請求的情況下,會分配worker執行緒來服務請求。
- worker 執行緒初始化非同步操作,並返回到執行緒池服務其他請求。非同步操作可使用CLR 執行緒來繼續執行。
- 存在的問題就是,CLR 執行緒無法返回響應,一旦它完成了非同步操作,它會通知Asp.net。
- Webserver 再次獲取一個worker執行緒來處理剩餘的請求,並返回響應。
上述使用場景中,會獲取兩次worker 執行緒,這兩次獲取的執行緒可能相同,也可能會不同。
檔案讀取是I/O操作,不需要使用worker 執行緒處理。因此最好將同步請求轉換為非同步。
同步請求的響應時間能提升嗎?
不可以,響應時間是相同的,執行緒會被釋放來服務其他請求。
實驗28——解決執行緒飢餓問題
在Asp.net MVC中會通過將同步Action方法轉換為非同步Action方法,將同步請求轉換為非同步請求。
1. 建立非同步控制器
在控制器中將基類 UploadController修改為 AsynController。
1: {
2: public class BulkUploadController : AsyncController
3: {
2. 轉換同步Action方法
該功能通過兩個關鍵字就可實現:“async “和” await”
1: [AdminFilter]
2: public async Task<ActionResult> Upload(FileUploadViewModel model)
3: {
4: int t1 = Thread.CurrentThread.ManagedThreadId;
5: List<Employee> employees = await Task.Factory.StartNew<List<Employee>>
6: (() => GetEmployees(model));
7: int t2 = Thread.CurrentThread.ManagedThreadId;
8: EmployeeBusinessLayer bal = new EmployeeBusinessLayer();
9: bal.UploadEmployees(employees);
10: return RedirectToAction("Index", "Employee");
11: }<actionresult><employee><list<employee>
12: </list<employee></employee></actionresult>
在action方法的開始或結束處,使用變數儲存執行緒ID。
理一下思路:
- 當上傳按鈕被點選時,新請求會被髮送到伺服器。
- Webserver從執行緒池中產生Worker執行緒 ,並分配給伺服器請求。
- worker執行緒會使Action 方法執行
- Worker方法在 Task.Factory.StartNew方法的輔助下,開啟非同步操作
- 使用async關鍵字將Action 方法標記為非同步方法,由此會保證非同步操作一旦開啟,Worker 執行緒就會釋放。
- 使用await關鍵字也可標記非同步操作,能夠保證非同步操作完成時才能夠繼續執行下面的程式碼。
- 一旦非同步操作在Action 方法中完成執行,必須執行worker執行緒。因此webserver將會新建一個空閒worker 執行緒,並用來服務剩下的請求,提供響應。
3. 測試執行
執行應用程式,並跳轉到BulkUpload頁面。會在程式碼中顯示斷點,輸入樣本檔案,點選上傳。
如圖所示,在專案啟動或關閉時有的執行緒ID是不同的。
實驗29——異常處理—顯示自定義錯誤頁面
如果一個專案不考慮異常處理,那麼可以說這個專案是不完整的。到目前為止,我們已經瞭解了MVC中的兩個過濾器:Action filter和 Authorization filter。現在我們來學習第三個過濾器,異常過濾器(Exception Filters)。
什麼是異常過濾器(Exception Filters)?
異常過濾器與其他過濾器的用法相同,可當作屬性使用。使用異常過濾器的基本步驟:
1. 使它們可用
2. 將過濾器作為屬性,應用到action 方法或控制器中。我們也可以在全域性層次使用異常過濾器。
異常過濾器的作用是什麼?,是否有自動執行的異常過濾器?
一旦action 方法中出現異常,異常過濾器就會控制程式的執行過程,開始內部自動寫入執行的程式碼。MVC為我們提供了編寫好的異常過濾器:HandeError。
當action方法中發生異常時,過濾器就會在“~/Views/[current controller]”或“~/Views/Shared”目錄下查詢到名稱為”Error”的View,然後建立該View的ViewResult,並作為響應返回。
接下來我們會講解一個Demo,幫助我們更好的理解異常過濾器的使用。
已經實現的上傳檔案功能,很有可能會發生輸入檔案格式錯誤。因此我們需要處理異常。
1. 建立含錯誤資訊的樣本檔案,包含一些非法值,如圖,Salary就是非法值。
2. 執行,查詢異常,點選上傳按鈕,選擇已建立的樣本資料,選擇上傳。
3. 啟用異常過濾器
當自定義異常被捕獲時,異常過濾器變為可用。為了能夠獲得自定義異常,開啟Web.config檔案,在System.Web.Section下方新增自定義錯誤資訊。
1: <system.web>
2: <customErrors mode="On"></customErrors>
4. 建立Error View
在“~/Views/Shared”資料夾下,會發現存在“Error.cshtml”檔案,該檔案是由MVC 模板提供的,如果沒有自動建立,該檔案也可以手動完成。
1: @{
2: Layout = null;
3: }
4:
5: <!DOCTYPE html>
6: <html>
7: <head>
8: <meta name="viewport" content="width=device-width" />
9: <title>Error</title>
10: </head>
11: <body>
12: <hgroup>
13: <h1>Error.</h1>
14: <h2>An error occurred while processing your request.</h2>
15: </hgroup>
16: </body>
17: </html>
5. 繫結異常過濾器
將過濾器繫結到action方法或controller上,不需要手動執行,開啟 App_Start folder資料夾中的 FilterConfig.cs檔案。在 RegisterGlobalFilters 方法中會看到 HandleError 過濾器已經以全域性過濾器繫結成功。
1: public static void RegisterGlobalFilters(GlobalFilterCollection filters)
2: {
3: filters.Add(new HandleErrorAttribute());//ExceptionFilter
4: filters.Add(new AuthorizeAttribute());
5: }
如果需要刪除全域性過濾器,那麼會將過濾器繫結到action 或controller層,但是不建議這麼做,最好是在全域性中應用如下:
1: [AdminFilter]
2: [HandleError]
3: public async Task<ActionResult> Upload(FileUploadViewModel model)
4: {<actionresult>
5: </actionresult>
6. 執行
7. 在View中顯示錯誤資訊
將Error View轉換為HandleErrorInfo類的強型別View,並在View中顯示錯誤資訊。
1: @model HandleErrorInfo
2: @{
3: Layout = null;
4: }
5:
6: <!DOCTYPE html>
7: <html>
8: <head>
9: <meta name="viewport" content="width=device-width" />
10: <title>Error</title>
11: </head>
12: <body>
13: <hgroup>
14: <h1>Error.</h1>
15: <h2>An error occurred while processing your request.</h2>
16: </hgroup>
17: Error Message :@Model.Exception.Message<br />
18: Controller: @Model.ControllerName<br />
19: Action: @Model.ActionName
20: </body>
21: </html>
8. 執行測試
Handle error屬效能夠確保無論是否出現異常,自定義View都能夠顯示,但是它的能力在controller和action 方法中是受限的。不會處理“Resource not found”這型別的錯誤。
執行應用程式,輸一些奇怪的URL
9. 建立 ErrorController控制器,並建立Index方法,程式碼如下:
1: public class ErrorController : Controller
2: {
3: // GET: Error
4: public ActionResult Index()
5: {
6: Exception e=new Exception("Invalid Controller or/and Action Name");
7: HandleErrorInfo eInfo = new HandleErrorInfo(e, "Unknown", "Unknown");
8: return View("Error", eInfo);
9: }
10: }
10. 在非法URL中顯示自定義Error檢視
可在 web.config中定義“Resource not found error”的設定,如下:
1: <system.web>
2: <customErrors mode="On">
3: <error statusCode="404" redirect="~/Error/Index"/>
4: </customErrors>
11. 使 ErrorController 全域性可訪問。
將AllowAnonymous屬性應用到 ErrorController中,因為錯誤控制器和index方法不應該只繫結到認證使用者,也很有可能使用者在登入之前已經輸入錯誤的URL。
1: [AllowAnonymous]
2: public class ErrorController : Controller
3: {
12. 執行
關於實驗29
View的名稱是否可以修改?
可以修改,不一定叫Error,也可以指定其他名字。如果Error View的名稱改變了,當繫結HandleError過濾器時,必須制定View的名稱。
1: [HandleError(View="MyError")]
2: Or
3: filters.Add(new HandleErrorAttribute()
4: {
5: View="MyError"
6: });
是否可以為不同的異常獲取不同的Error View?
可以,在這種情況下,必須多次應用Handle error filter。
1: [HandleError(View="DivideError",ExceptionType=typeof(DivideByZeroException))]
2: [HandleError(View = "NotFiniteError", ExceptionType = typeof(NotFiniteNumberException))]
3: [HandleError]
4:
5: OR
6:
7: filters.Add(new HandleErrorAttribute()
8: {
9: ExceptionType = typeof(DivideByZeroException),
10: View = "DivideError"
11: });
12: filters.Add(new HandleErrorAttribute()
13: {
14: ExceptionType = typeof(NotFiniteNumberException),
15: View = "NotFiniteError"
16: });
17: filters.Add(new HandleErrorAttribute());
前兩個Handle error filter都指定了異常,而最後一個更為常見更通用,會顯示所有其他異常的Error View。
上述實驗中並沒有處理登入異常,我們會在實驗30中講解登入異常。
實驗30——異常處理—登入異常
1. 建立 Logger 類
在根目錄下,新建資料夾,命名為Logger。在Logger 資料夾下新建類 FileLogger
1: namespace WebApplication1.Logger
2: {
3: public class FileLogger
4: {
5: public void LogException(Exception e)
6: {
7: File.WriteAllLines("C://Error//" + DateTime.Now.ToString("dd-MM-yyyy mm hh ss")+".txt",
8: new string[]
9: {
10: "Message:"+e.Message,
11: "Stacktrace:"+e.StackTrace
12: });
13: }
14: }
15: }
2. 建立 EmployeeExceptionFilter 類
在 Filters資料夾下,新建 EmployeeExceptionFilter類
1: namespace WebApplication1.Filters
2: {
3: public class EmployeeExceptionFilter
4: {
5: }
6: }
3. 擴充套件 Handle Error實現登入異常處理
讓 EmployeeExceptionFilter 繼承 HandleErrorAttribute類,重寫 OnException方法:
1: public class EmployeeExceptionFilter:HandleErrorAttribute
2: {
3: public override void OnException(ExceptionContext filterContext)
4: {
5: base.OnException(filterContext);
6: }
7: }
4. 定義 OnException 方法
在 OnException 方法中包含異常登入程式碼。
1: public override void OnException(ExceptionContext filterContext)
2: {
3: FileLogger logger = new FileLogger();
4: logger.LogException(filterContext.Exception);
5: base.OnException(filterContext);
6: }
5. 修改預設的異常過濾器
開啟 FilterConfig.cs檔案,刪除 HandErrorAtrribute,新增上步中建立的。
1: public static void RegisterGlobalFilters(GlobalFilterCollection filters)
2: {
3: //filters.Add(new HandleErrorAttribute());//ExceptionFilter
4: filters.Add(new EmployeeExceptionFilter());
5: filters.Add(new AuthorizeAttribute());
6: }
6. 執行
會在C盤中建立“Error”資料夾,存放一些error檔案。
關於實驗30
當異常出現後,Error View 是如何返回響應的?
檢視 OnException 方法的最後一行程式碼:
1: base.OnException(filterContext);
即基類的 OnException 方法執行並返回Error View 的ViewResult。
在 OnException 中,是否可以返回其他結果?
可以,程式碼如下:
1: public override void OnException(ExceptionContext filterContext)
2: {
3: FileLogger logger = new FileLogger();
4: logger.LogException(filterContext.Exception);
5: //base.OnException(filterContext);
6: filterContext.ExceptionHandled = true;
7: filterContext.Result = new ContentResult()
8: {
9: Content="Sorry for the Error"
10: };
11: }
當返回自定義響應時,做的第一件事情就是通知MVC 引擎,手動處理異常,因此不需要執行預設的操作,不會顯示預設的錯誤頁面。使用以下語句可完成:
1: filterContext.ExceptionHandled = true
Routing
到目前為止,我們已經解決了MVC的很多問題,但忽略了最基本的最重要的一個問題:當用戶傳送請求時,會發生什麼?
最好的答案是“執行Action 方法”,但仍存在疑問:對於一個特定的URL請求,如何確定控制器和action 方法。在開始實驗31之前,我們首先來解答上述問題,你可能會困惑為什麼這個問題會放在最後來講,因為了解內部結構之前,需要更好的瞭解MVC。
理解RouteTable
在Asp.net mvc中有RouteTable這個概念,是用來儲存URL 路徑的,簡而言之,是儲存已定義的應用程式的可能的URL pattern的集合。
預設情況下,路徑是專案模板組成的一部分。可在 Global.asax 檔案中檢查到,在 Application_Start中會發現以下語句:
1: RouteConfig.RegisterRoutes(RouteTable.Routes);
App_Start資料夾下的 RouteConfig.cs檔案,包含以下程式碼塊:
1: namespace WebApplication1
2: {
3: public class RouteConfig
4: {
5: public static void RegisterRoutes(RouteCollection routes)
6: {
7: routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
8:
9: routes.MapRoute(
10: name: "Default",
11: url: "{controller}/{action}/{id}",
12: defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
13: );
14: }
15: }
16: }
RegisterRoutes方法已經包含了由routes.MapRoute 方法定義的預設的路徑。已定義的路徑會在請求週期中確定執行的是正確的控制器和action 方法。如果使用 route.MapRoute建立了多個路徑,那麼內部路徑的定義就意味著建立Route物件。
MapRoute 方法也可與 RouteHandler 關聯。
理解ASP.NET MVC 請求週期
在本節中我們只講解請求週期中重要的知識點
1. UrlRoutingModule
當終端使用者傳送請求時,會通過UrlRoutingModule 物件傳遞,UrlRoutingModule 是HTTP 模組。
2. Routing
UrlRoutingModule 會從route table集合中獲取首次匹配的Route 物件,為了能夠匹配成功,請求URL會與route中定義的URL pattern 匹配。
當匹配的時候必須考慮以下規則:
- 數字引數的匹配(請求URL和URL pattern中的數字)
- URL pattern中的可選引數:
- 引數中定義的靜態引數
3. 建立MVC Route Handler
一旦Route 物件被選中,UrlRoutingModule會獲得 Route物件的 MvcRouteHandler物件。
4. 建立 RouteData 和 RequestContext
UrlRoutingModule使用Route物件建立RouteData,可用於建立RequestContext。RouteData封裝了路徑的資訊如Controller名稱,action名稱以及route引數值。
Controller 名稱
為了從URL 中獲取Controller名稱,需要按規則執行如在URL pattern中{Controller}是標識Controller名稱的關鍵字。
Action Method 名稱
為了獲取action 方法名稱,{action}是標識action 方法的關鍵字。
Route 引數
URL pattern能夠獲得以下值:
1.{controller}
2.{action}
3. 字串,如 “MyCompany/{controller}/{action}”,“MyCompany”是字串。
4. 其他,如“{controller}/{action}/{id}”,”id“是路徑的引數。
例如:
Route pattern - > “{controller}/{action}/{id}”
請求 URL ->http://localhost:8870/BulkUpload/Upload/5
測試1
1: public class BulkUploadController : Controller
2: {
3: public ActionResult Upload (string id)
4: {
5: //value of id will be 5 -> string 5
6: ...
7: }
8: }
測試2
1: public class BulkUploadController : Controller
2: {
3: public ActionResult Upload (int id)
4: {
5: //value of id will be 5 -> int 5
6: ...
7: }
8: }
測試3
1: public class BulkUploadController : Controller
2: {
3: public ActionResult Upload (string MyId)
4: {
5: //value of MyId will be null
6: ...
7: }
8: }
5. 建立MVC Handler
MvcRouteHandler 會建立 MVCHandler的例項傳遞 RequestContext物件
6. 建立Controller例項
MVCHandler會根據 ControllerFactory的幫助建立Controller例項
7. 執行方法
MVCHandler呼叫Controller的執行方法,執行方法是由Controller的基類定義的。
8. 呼叫Action 方法
每個控制器都有與之關聯的 ControllerActionInvoker物件。在執行方法中ControllerActionInvoker物件呼叫正確的action 方法。
9. 執行結果
Action方法會接收到使用者輸入,並準備好響應資料,然後通過返回語句返回執行結果,返回型別可能是ViewResult或其他。
實驗31——實現對使用者有好的URL
1. 重新定義 RegisterRoutes 方法
在RegisterRoutes 方法中包含 additional route
1: public static void RegisterRoutes(RouteCollection routes)
2: {
3: routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
4:
5: routes.MapRoute(
6: name: "Upload",
7: url: "Employee/BulkUpload",
8: defaults: new { controller = "BulkUpload", action = "Index" }
9: );
10:
11: routes.MapRoute(
12: name: "Default",
13: url: "{controller}/{action}/{id}",
14: defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
15: );
16: }
2. 修改URL 引用
開啟“~/Views/Employee”檔案下的 AddNewLink.cshtml ,修改BulkUpload 連結,如下:
1:
2: <a href="/Employee/BulkUpload">BulkUpload</a>
3. 執行測試
關於實驗31
之前的URL 現在是否起作用?
是,仍然有用。BulkUploadController中的Index 方法可通過兩個URL 訪問。
1. ”http://localhost:8870/Employee/BulkUpload“
2. “http://localhost:8870/BulkUpload/Index”
Route 引數和Query 字串有什麼區別?
- Query 字串本身是有大小限制的,而無法定義Route 引數的個數。
- 無法在Query 字串值中新增限制,但是可以在Route 引數中新增限制。
- 可能會設定Route引數的預設值,而Query String不可能有預設值。
- Query 字串可使URL 混亂,而Route引數可保持它有條理。
如何在Route 引數中使用限制?
可使用正則表示式。
如:
1: routes.MapRoute(
2: "MyRoute",
3: "Employee/{EmpId}",
4: new {controller=" Employee ", action="GetEmployeeById"},
5: new { EmpId = @"\d+" }
6: );
Action 方法:
1: public ActionResult GetEmployeeById(int EmpId)
2: {
3: ...
4: }
為了保證每個路徑引數都能獨立,因此引數名稱必須與Route Parameter一致。
是否需要將action 方法中的引數名稱與Route 引數名稱保持一致?
Route Pattern 也許會包含一個或多個RouteParameter,為了區分每個引數,必須保證action 方法的引數名稱與Route 引數名稱相同。
定義路徑的順序重要嗎?
有影響,在上面的實驗中,我們定義了兩個路徑,一個是自定義的,一個是預設的。預設的是最先定義的,自定義路徑是在之後定義的。
當用戶輸入“http://.../Employee/BulkUpload”地址後傳送請求,UrlRoutingModule會搜尋與請求URL 匹配的預設的route pattern ,它會將 Employee作為控制器的名稱,“BulkUpload”作為action 方法名稱。因此定義的順序是非常重要的,更常用的路徑應放在最後。
是否有什麼簡便的方法來定義Action 方法的URL pattern?
我們可使用基於 routing 的屬性。
1. 基本的routing 屬性可用
在 RegisterRoutes 方法中在 IgnoreRoute語句後輸入程式碼如下:
1: routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
2:
3: routes.MapMvcAttributeRoutes();
4:
5: routes.MapRoute(
6: ...
2. 定義action 方法的 route pattern
1: [Route("Employee/List")]
2: public ActionResult Index()
3: {
3. 執行測試
routing 屬性可定義route 引數,如下:
1: [Route("Employee/List/{id}")]
2: publicActionResult Index (string id) { ... }
IgnoreRoutes 的作用是什麼?
當我們不想使用routing作為特別的擴充套件時,會使用IgnoreRoutes。作為MVC模板的一部分,在RegisterRoute 方法中下列語句是預設的:
1: routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
這就是說如果使用者傳送以“.axd”為結束的請求,將不會有任何路徑載入的操作,請求將直接定位到物理資源。
總結
本節內容中講述的執行緒問題是我們在MVC開發過程中經常遇到的,所以希望大家深入學習。同時在進行MVC開發時,還可以藉助一些開發工具來幫助開發過程。是一款針對 MVC 平臺的控制元件包,它與 Visual Studio 無縫整合,完全與 MVC6 和 ASP.NET 5.0 相容,將大幅提高工作效率。
6天的MVC 學習已經完成了,希望大家能夠將所講的知識充分理解,充分吸收。第7章我們會使用MVC,JQUery 和Ajax建立簡單的頁面應用。歡迎大家持續關注!
相關閱讀: