Làm việc với application model trong ASP.NET Core
Bởi Steve Smith
ASP.NET Core MVC định nghĩa một application model (mô hình ứng dụng) đại diện cho các thành phần của một ứng dụng MVC. Đọc và thao tác với model này để sửa đổi cách các phần tử MVC hoạt động. Theo mặc định, MVC tuân theo các quy ước nhất định để xác định các lớp nào được coi là controllers, các phương thức nào trên các lớp đó là actions, và cách parameters (tham số) và routing (định tuyến) hoạt động. Tùy chỉnh hành vi này để phù hợp với nhu cầu của ứng dụng bằng cách tạo các quy ước tùy chỉnh và áp dụng chúng toàn cục hoặc dưới dạng attributes.
Models và Providers (IApplicationModelProvider)
Application model của ASP.NET Core MVC bao gồm cả các interfaces trừu tượng và các lớp triển khai cụ thể mô tả một ứng dụng MVC. Model này là kết quả của việc MVC khám phá các controllers, actions, action parameters, routes và filters của ứng dụng theo các quy ước mặc định. Bằng cách làm việc với application model, bạn có thể sửa đổi ứng dụng để tuân theo các quy ước khác với hành vi MVC mặc định. Các parameters, names (tên), routes và filters đều được sử dụng như dữ liệu cấu hình cho actions và controllers.
Application Model của ASP.NET Core MVC có cấu trúc sau:
- ApplicationModel
- Controllers (ControllerModel)
- Actions (ActionModel)
- Parameters (ParameterModel)
Mỗi cấp độ của model có quyền truy cập vào một bộ sưu tập Properties chung, và các cấp độ thấp hơn có thể truy cập và ghi đè các giá trị thuộc tính được đặt bởi các cấp độ cao hơn trong hệ thống phân cấp. Các thuộc tính được duy trì trong ActionDescriptor.Properties khi các actions được tạo. Sau đó khi một request đang được xử lý, bất kỳ thuộc tính nào mà một quy ước đã thêm hoặc sửa đổi có thể được truy cập thông qua ActionContext.ActionDescriptor. Sử dụng thuộc tính là một cách tuyệt vời để cấu hình filters, model binders, và các khía cạnh app model khác trên cơ sở mỗi action.
Bộ sưu tập ActionDescriptor.Properties không thread-safe (an toàn luồng) (cho các ghi) sau khi ứng dụng khởi động. Quy ước là cách tốt nhất để thêm dữ liệu một cách an toàn vào bộ sưu tập này.
ASP.NET Core MVC tải application model sử dụng mẫu provider (nhà cung cấp), được định nghĩa bởi interface IApplicationModelProvider. Phần này bao gồm một số chi tiết triển khai nội bộ về cách provider này hoạt động. Sử dụng mẫu provider là một chủ đề nâng cao, chủ yếu dành cho sử dụng framework. Hầu hết các ứng dụng nên sử dụng conventions (quy ước), không phải mẫu provider.
Các triển khai của interface IApplicationModelProvider "bọc" lẫn nhau, trong đó mỗi triển khai gọi OnProvidersExecuting theo thứ tự tăng dần dựa trên thuộc tính Order của nó. Phương thức OnProvidersExecuted sau đó được gọi theo thứ tự ngược lại. Framework định nghĩa nhiều providers:
Đầu tiên (Order=-1000):
DefaultApplicationModelProvider
Tiếp theo (Order=-990):
AuthorizationApplicationModelProviderCorsApplicationModelProvider
Thứ tự trong đó hai providers có cùng giá trị cho Order được gọi là không xác định và không nên được dựa vào.
IApplicationModelProvider là một khái niệm nâng cao dành cho các tác giả framework để mở rộng. Nói chung, ứng dụng nên sử dụng conventions và frameworks nên sử dụng providers. Sự khác biệt chính là providers luôn chạy trước conventions.
DefaultApplicationModelProvider thiết lập nhiều hành vi mặc định được sử dụng bởi ASP.NET Core MVC. Trách nhiệm của nó bao gồm:
- Thêm global filters (bộ lọc toàn cục) vào context
- Thêm controllers vào context
- Thêm các phương thức controller công khai dưới dạng actions
- Thêm các tham số phương thức action vào context
- Áp dụng route và các attributes khác
AuthorizationApplicationModelProvider chịu trách nhiệm áp dụng hành vi liên quan đến các attributes AuthorizeFilter và AllowAnonymousFilter.
CorsApplicationModelProvider triển khai hành vi liên quan đến IEnableCorsAttribute và IDisableCorsAttribute.
Conventions (Quy ước)
Application model định nghĩa các convention abstractions cung cấp một cách đơn giản hơn để tùy chỉnh hành vi của các models so với việc ghi đè toàn bộ model hoặc provider. Những abstractions này là cách được khuyến nghị để sửa đổi hành vi của ứng dụng. Conventions cung cấp một cách để viết code áp dụng các tùy chỉnh động. Trong khi filters cung cấp một phương tiện để sửa đổi hành vi của framework, các tùy chỉnh cho phép kiểm soát cách toàn bộ ứng dụng hoạt động cùng nhau.
Các conventions sau đây có sẵn:
- IApplicationModelConvention
- IControllerModelConvention
- IActionModelConvention
- IParameterModelConvention
Conventions được áp dụng bằng cách thêm chúng vào MVC options hoặc bằng cách triển khai attributes và áp dụng chúng cho controllers, actions, hoặc action parameters (tương tự như filters). Không giống filters, conventions chỉ được thực thi khi ứng dụng khởi động, không phải như một phần của mỗi request.
Để biết thông tin về Razor Pages route và application model provider conventions, xem Razor Pages route and app conventions in ASP.NET Core.
Sửa đổi ApplicationModel
Convention sau được sử dụng để thêm một thuộc tính vào application model:
using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace AppModelSample.Conventions
{
public class ApplicationDescription : IApplicationModelConvention
{
private readonly string _description;
public ApplicationDescription(string description)
{
_description = description;
}
public void Apply(ApplicationModel application)
{
application.Properties["description"] = _description;
}
}
}Các application model conventions được áp dụng như options khi MVC được thêm vào trong Startup.ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Conventions.Add(new ApplicationDescription("My Application Description"));
options.Conventions.Add(new NamespaceRoutingConvention());
});
}Các thuộc tính có thể truy cập được từ bộ sưu tập ActionDescriptor.Properties trong các controller actions:
public class AppModelController : Controller
{
public string Description()
{
return "Description: " + ControllerContext.ActionDescriptor.Properties["description"];
}
}Sửa đổi mô tả ControllerModel
Controller model cũng có thể bao gồm các thuộc tính tùy chỉnh. Các thuộc tính tùy chỉnh ghi đè các thuộc tính hiện có có cùng tên được chỉ định trong application model. Convention attribute sau thêm một mô tả ở cấp độ controller:
using System;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace AppModelSample.Conventions
{
public class ControllerDescriptionAttribute : Attribute, IControllerModelConvention
{
private readonly string _description;
public ControllerDescriptionAttribute(string description)
{
_description = description;
}
public void Apply(ControllerModel controllerModel)
{
controllerModel.Properties["description"] = _description;
}
}
}Convention này được áp dụng như một attribute trên controller:
[ControllerDescription("Controller Description")]
public class DescriptionAttributesController : Controller
{
public string Index()
{
return "Description: " + ControllerContext.ActionDescriptor.Properties["description"];
}Sửa đổi mô tả ActionModel
Một convention attribute riêng biệt có thể được áp dụng cho các actions riêng lẻ, ghi đè hành vi đã được áp dụng ở cấp độ ứng dụng hoặc controller:
using System;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace AppModelSample.Conventions
{
public class ActionDescriptionAttribute : Attribute, IActionModelConvention
{
private readonly string _description;
public ActionDescriptionAttribute(string description)
{
_description = description;
}
public void Apply(ActionModel actionModel)
{
actionModel.Properties["description"] = _description;
}
}
}Áp dụng điều này cho một action trong controller cho thấy cách nó ghi đè convention cấp controller:
[ControllerDescription("Controller Description")]
public class DescriptionAttributesController : Controller
{
public string Index()
{
return "Description: " + ControllerContext.ActionDescriptor.Properties["description"];
}
[ActionDescription("Action Description")]
public string UseActionDescriptionAttribute()
{
return "Description: " + ControllerContext.ActionDescriptor.Properties["description"];
}
}Sửa đổi ParameterModel
Convention sau có thể được áp dụng cho các action parameters để sửa đổi BindingInfo của chúng. Convention sau yêu cầu tham số phải là một route parameter (tham số route). Các nguồn binding tiềm năng khác, chẳng hạn như giá trị query string (chuỗi truy vấn), bị bỏ qua:
using System;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace AppModelSample.Conventions
{
public class MustBeInRouteParameterModelConvention : Attribute, IParameterModelConvention
{
public void Apply(ParameterModel model)
{
if (model.BindingInfo == null)
{
model.BindingInfo = new BindingInfo();
}
model.BindingInfo.BindingSource = BindingSource.Path;
}
}
}Attribute có thể được áp dụng cho bất kỳ action parameter nào:
public class ParameterModelController : Controller
{
// Sẽ bind: /ParameterModel/GetById/123
// KHÔNG bind: /ParameterModel/GetById?id=123
public string GetById([MustBeInRouteParameterModelConvention]int id)
{
return $"Bound to id: {id}";
}
}Để áp dụng convention cho tất cả các action parameters, hãy thêm MustBeInRouteParameterModelConvention vào MvcOptions trong Startup.ConfigureServices:
options.Conventions.Add(new MustBeInRouteParameterModelConvention());
Sửa đổi tên ActionModel
Convention sau sửa đổi ActionModel để cập nhật tên của action mà nó được áp dụng. Tên mới được cung cấp như một tham số cho attribute. Tên mới này được sử dụng bởi routing, vì vậy nó ảnh hưởng đến route được sử dụng để tiếp cận phương thức action này:
using System;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace AppModelSample.Conventions
{
public class CustomActionNameAttribute : Attribute, IActionModelConvention
{
private readonly string _actionName;
public CustomActionNameAttribute(string actionName)
{
_actionName = actionName;
}
public void Apply(ActionModel actionModel)
{
// tên này sẽ được sử dụng bởi routing
actionModel.ActionName = _actionName;
}
}
}Attribute này được áp dụng cho một phương thức action trong HomeController:
// Route: /Home/MyCoolAction
[CustomActionName("MyCoolAction")]
public string SomeName()
{
return ControllerContext.ActionDescriptor.ActionName;
}Mặc dù tên phương thức là SomeName, attribute ghi đè quy ước MVC của việc sử dụng tên phương thức và thay thế tên action bằng MyCoolAction. Do đó, route được sử dụng để tiếp cận action này là /Home/MyCoolAction.
Ví dụ trong phần này về cơ bản giống như sử dụng ActionNameAttribute tích hợp sẵn.
Convention routing tùy chỉnh
Sử dụng IApplicationModelConvention để tùy chỉnh cách routing hoạt động. Ví dụ, convention sau kết hợp namespaces của controllers vào các routes của chúng, thay thế . trong namespace bằng / trong route:
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using System.Linq;
namespace AppModelSample.Conventions
{
public class NamespaceRoutingConvention : IApplicationModelConvention
{
public void Apply(ApplicationModel application)
{
foreach (var controller in application.Controllers)
{
var hasAttributeRouteModels = controller.Selectors
.Any(selector => selector.AttributeRouteModel != null);
if (!hasAttributeRouteModels
&& controller.ControllerName.Contains("Namespace"))
{
// Thay thế . trong namespace bằng / để tạo attribute route
// Ví dụ: namespace MySite.Admin sẽ tương ứng với attribute route MySite/Admin
// Sau đó gắn token [controller], [action] và tùy chọn {id?}.
controller.Selectors[0].AttributeRouteModel = new AttributeRouteModel()
{
Template = controller.ControllerType.Namespace.Replace('.', '/') + "/[controller]/[action]/{id?}"
};
}
}
}
}
}Convention được thêm vào như một option trong Startup.ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.Conventions.Add(new ApplicationDescription("My Application Description"));
options.Conventions.Add(new NamespaceRoutingConvention());
});
}Thêm conventions vào middleware (phần mềm trung gian) thông qua MvcOptions sử dụng cách tiếp cận sau. Trình giữ chỗ {CONVENTION} là convention cần thêm:
``csharp
services.Configure<MvcOptions>(c => c.Conventions.Add({CONVENTION}));
``
Ví dụ sau áp dụng một convention cho các routes không sử dụng attribute routing trong đó controller có Namespace trong tên của nó:
using Microsoft.AspNetCore.Mvc;
namespace AppModelSample.Controllers
{
public class NamespaceRoutingController : Controller
{
// sử dụng NamespaceRoutingConvention
// route: /AppModelSample/Controllers/NamespaceRouting/Index
public string Index()
{
return "This demonstrates namespace routing.";
}
}
}Sử dụng ApiExplorer để tài liệu hóa một ứng dụng
Application model hiển thị một thuộc tính ApiExplorerModel ở mỗi cấp độ có thể được sử dụng để duyệt qua cấu trúc của ứng dụng. Điều này có thể được sử dụng để tạo các trang trợ giúp cho web APIs sử dụng các công cụ như Swagger. Thuộc tính ApiExplorer hiển thị một thuộc tính IsVisible có thể được đặt để chỉ định phần nào của model ứng dụng nên được hiển thị. Cấu hình thiết lập này sử dụng một convention:
using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace AppModelSample.Conventions
{
public class EnableApiExplorerApplicationConvention : IApplicationModelConvention
{
public void Apply(ApplicationModel application)
{
application.ApiExplorer.IsVisible = true;
}
}
}Sử dụng cách tiếp cận này (và các conventions bổ sung nếu cần), khả năng hiển thị API được bật hoặc tắt ở bất kỳ cấp độ nào trong ứng dụng.