1. Razor视图概述
Razor是ASP.NET Core的视图引擎,用于生成动态HTML内容。NopCommerce广泛使用Razor视图来构建前端页面,包括商品列表、购物车、结账流程等)
1.1 核心特性
- 简洁的语法,结合HTML和C#代码
- 强大的模板继承机制- 支持布局、部分视图和视图组件
- 内置模型绑定支持
- 支持依赖注入
2. 视图结构与布局
2.1 基本视图结构
一个典型的Razor视图文件)cshtml)包含以下结构:
@model Nop.Web.Models.Product.ProductDetailsModel @{ ViewBag.Title = Model.Name; Layout = "_ColumnOne.cshtml"; }<h1>@Model.Name</h1><divclass="product-description">@Html.Raw(Model.FullDescription)</div><!-- 其他视图内容 -->2.2 布局视图
布局视图定义了页面的基本结构,通常包含HTML、CSS和JavaScript引用途
<!DOCTYPEhtml><htmllang="@_workContext.WorkingLanguage.LanguageCulture"><head><metacharset="utf-8"/><metaname="viewport"content="width=device-width, initial-scale=1.0"/><title>@ViewBag.Title - @_storeInformationSettings.StoreName</title><!-- CSS引用 -->@await RenderSectionAsync("Styles", required: false)</head><body><!-- 页头 --><headerid="header">@await Html.PartialAsync("_Header.cshtml")</header><!-- 主内)--><mainid="main">@RenderBody()</main><!-- 页脚 --><footerid="footer">@await Html.PartialAsync("_Footer.cshtml")</footer><!-- JavaScript引用 -->@await RenderSectionAsync("Scripts", required: false)</body></html>2.3 部分视图
部分视图用于封装可重用的视图组件)
@model Nop.Web.Models.Catalog.CategoryNavigationModel<divclass="category-navigation"><ul>@foreach (var category in Model.Categories) {<li><ahref="@Url.RouteUrl("Category",new{SeName=category.SeName})">@category.Name @if (category.NumberOfProducts > 0) {<spanclass="badge">@category.NumberOfProducts</span>}</a>@if (category.SubCategories.Any()) {<ul>@foreach (var subCategory in category.SubCategories) {<li><ahref="@Url.RouteUrl("Category",new{SeName=subCategory.SeName})">@subCategory.Name @if (subCategory.NumberOfProducts > 0) {<spanclass="badge">@subCategory.NumberOfProducts</span>}</a></li>}</ul>}</li>}</ul></div>3. 模型绑定
3.1 基本概念
模型绑定是ASP.NET Core将HTTP请求数据映射到控制器动作方法参数的过程。在NopCommerce中,模型绑定被广泛用于处理表单提交、查询字符串参数等)
3.2 绑定)
模型绑定支持多种数据源:
- 表单数据(Form- 查询字符串(Query- 路由数据(Route- HTTP头(Header- Cookie
3.3 绑定属性
NopCommerce使用各种模型绑定属性来控制绑定行为)
publicIActionResultProductDetails([Route("productId")]intproductId,// 从路由绑) [FromQuery] string viewMode, // 从查询字符串绑定[FromForm]AddToCartModel model// 从表单绑)){// 动作方法实现}4. 强类型视)
4.1 定义视图模型
视图模型用于在控制器和视图之间传递数据:
publicpartialclassProductDetailsModel:BaseNopModel{publicProductDetailsModel(){ProductAttributes=newList<ProductAttributeModel>();ProductSpecifications=newList<ProductSpecificationModel>();RelatedProducts=newList<ProductOverviewModel>();}publicintId{get;set;}publicstringName{get;set;}publicstringShortDescription{get;set;}publicstringFullDescription{get;set;}publicdecimalPrice{get;set;}publicboolInStock{get;set;}// 其他属性..publicIList<ProductAttributeModel>ProductAttributes{get;set;}publicIList<ProductSpecificationModel>ProductSpecifications{get;set;}publicIList<ProductOverviewModel>RelatedProducts{get;set;}}4.2 在视图中使用模型
@model Nop.Web.Models.Product.ProductDetailsModel<divclass="product-info"><h1>@Model.Name</h1><divclass="price">@if (Model.OldPrice > Model.Price) {<spanclass="old-price">@Model.OldPrice</span>}<spanclass="current-price">@Model.Price</span></div><divclass="stock-status">@if (Model.InStock) {<spanclass="in-stock">@T("Products.InStock")</span>} else {<spanclass="out-of-stock">@T("Products.OutOfStock")</span>}</div><!-- 其他产品信息 --></div>5. 表单处理与验)
5.1 HTML表单
@model Nop.Web.Models.Customer.RegisterModel<formasp-action="Register"method="post"class="form-horizontal"><divasp-validation-summary="All"class="text-danger"></div><divclass="form-group"><labelasp-for="Email"class="control-label"></label><inputasp-for="Email"class="form-control"/><spanasp-validation-for="Email"class="text-danger"></span></div><divclass="form-group"><labelasp-for="Password"class="control-label"></label><inputasp-for="Password"type="password"class="form-control"/><spanasp-validation-for="Password"class="text-danger"></span></div><divclass="form-group"><labelasp-for="ConfirmPassword"class="control-label"></label><inputasp-for="ConfirmPassword"type="password"class="form-control"/><spanasp-validation-for="ConfirmPassword"class="text-danger"></span></div><divclass="form-group"><buttontype="submit"class="btn btn-primary">@T("Account.Register")</button></div></form>5.2 模型验证
NopCommerce使用数据注解进行模型验证)
publicpartialclassRegisterModel:BaseNopModel{[Required(ErrorMessage="Email is required")][EmailAddress(ErrorMessage="Invalid email address")][Display(Name="Email")]publicstringEmail{get;set;}[Required(ErrorMessage="Password is required")][StringLength(100,ErrorMessage="The {0} must be at least {2} characters long.",MinimumLength=6)][DataType(DataType.Password)][Display(Name="Password")]publicstringPassword{get;set;}[DataType(DataType.Password)][Display(Name="Confirm password")][Compare("Password",ErrorMessage="The password and confirmation password do not match.")]publicstringConfirmPassword{get;set;}// 其他属性..}6. 视图组件
6.1 定义视图组件
视图组件是一种可重用的UI组件,类似于部分视图,但具有更强的功能:
[ViewComponent(Name="TopMenu")]publicclassTopMenuViewComponent:NopViewComponent{privatereadonlyICatalogModelFactory_catalogModelFactory;publicTopMenuViewComponent(ICatalogModelFactorycatalogModelFactory){_catalogModelFactory=catalogModelFactory;}publicasyncTask<IViewComponentResult>InvokeAsync(int) customerId=null){varmodel=await_catalogModelFactory.PrepareTopMenuModelAsync();returnView(model);}}6.2 使用视图组件
在视图中使用视图组件)
@await Component.InvokeAsync("TopMenu")<!-- 或使用标签助手语)--><vc:top-menu></vc:top-menu>7. 本地化支)
7.1 使用T()方法
NopCommerce提供了T()方法用于本地化字符串)
<h2>@T("ShoppingCart.Cart")</h2><p>@T("ShoppingCart.TotalItems", Model.TotalItems)</p><buttontype="submit">@T("ShoppingCart.Checkout")</button>7.2 本地化资源文)
本地化字符串存储在XML资源文件中,位于Localization目录下:
<LanguageName="English"LanguageCulture="en-US"UniqueSeoCode="en"Published="True"DisplayOrder="1"><ResourceName="ShoppingCart.Cart"Value="Shopping Cart"/><ResourceName="ShoppingCart.TotalItems"Value="Total items: {0}"/><ResourceName="ShoppingCart.Checkout"Value="Checkout"/></Language>8. URL生成
8.1 使用Url助手
NopCommerce提供了Url助手用于生成URL)
<ahref="@Url.RouteUrl("HomePage")">@T("Home.PageTitle")</a><ahref="@Url.RouteUrl("Category",new{SeName=category.SeName})">@category.Name</a><ahref="@Url.Action("ContactUs","Common")">@T("ContactUs")</a>8.2 路由定义
路由定义在Startup.cs或插件的路由配置中:
publicpartialclassStartup{publicvoidConfigure(IApplicationBuilderapplication){// 其他配置...application.UseMvc(routes=>{// 主页路由routes.MapRoute("HomePage","",new{controller="Home",action="Index"});// 分类路由routes.MapRoute("Category","category/{SeName}",new{controller="Catalog",action="Category"});// 产品路由routes.MapRoute("Product","product/{SeName}",new{controller="Catalog",action="Product"});// 其他路由...});}}9. 依赖注入
9.1 在视图中注入服务
NopCommerce允许在视图中直接注入服务)
@inject IWorkContext _workContext @inject IStoreContext _storeContext @inject ISettingService _settingService<divclass="customer-info"><p>@T("Welcome.Message", (await _workContext.GetCurrentCustomerAsync()).Email)</p><p>@T("Current.Store", (await _storeContext.GetCurrentStoreAsync()).Name)</p></div>10. 最佳实现
10.1 视图设计原则
- **保持视图简)*:视图应主要负责展示数据,避免包含复杂的业务逻辑
- **使用强类型视)*:优先使用强类型视图,避免使用
ViewBag和ViewData - **分离关注)*:将业务逻辑放在服务层,数据访问放在仓储层,视图只负责渲)4.使用视图模型:创建专门的视图模型,不要直接将实体传递给视图
- 重用组件:将可重用的UI元素封装为部分视图或视图组件
10.2 性能优化
- 避免在视图中执行复杂查询:所有数据应在控制器或服务层准备份2.使用缓存:对频繁访问的视图或数据使用缓存
- 优化HTML输出:减少不必要的HTML元素和属性4.延迟加载:对于大型页面,考虑使用延迟加载技)
10.3 安全)
- 防止XSS攻击:使用
Html.Raw()时要确保内容已经过安全处)2.防止CSRF攻击:使用@Html.AntiForgeryToken()保护表单 - 验证输入:始终在服务器端验证用户输入
- 使用HTTPS:确保所有敏感数据通过HTTPS传输
11. 调试技)
- 使用Razor视图调试:在Visual Studio中启用Razor视图调试
- 添加诊断信息:在开发环境中添加调试信息
- 使用日志:在视图组件和服务中添加适当的日)4. **检查模型状)*:在控制器中检查
ModelState.IsValid
12. 总结
Razor视图与模型绑定是NopCommerce前端开发的核心部分。通过掌握Razor语法、模型绑定、视图组件等技术,你可以构建出功能强大、性能优良的前端页面)
在实际开发中,建议遵循NopCommerce的最佳实践,保持视图的简洁性和可维护性,同时注重性能和安全性。通过合理使用视图模型、部分视图和视图组件,可以提高代码的重用性和可测试性,从而加快开发速度并提高代码质量