Bootstrap v4.5.2
path:/src/Presentation/Nop.Web/wwwroot/lib_npm/bootstrap/css/bootstrap.min.css
Quick start
Step 1. Create direcotry name with Areas (新建areas 目录)
Step 2.新建Controller目录
添加控制器HomeController
Step 3.添加路由
Add the following code to the Configure method in your Application's Startup class if not already done:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name : "areas",
pattern : "{area:exists}/{controller=Home}/{action=Index}/{id?}"
);
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute(
name: "areas",
pattern: "{area}/{controller}/{did?}/{action=Index}/{id?}");
});
访问
http://localhost:5000/stockso/home
出现下面的错误:
An unhandled exception occurred while processing the request.
InvalidOperationException: The view 'Index' was not found. The following locations were searched:
/Areas/Stockso/Views/Home/Index.cshtml
/Areas/Stockso/Views/Shared/Index.cshtml
/Views/Shared/Index.cshtml
/Pages/Shared/Index.cshtml
添加View目录
step 1: create areas name with Admin
src\ElasticsearchWebSearch\Elasticsearch.Web\Areas\StocksoData\
<ItemGroup>
<Folder Include="Areas\Bootstrap5\Data\" />
<Folder Include="Areas\Bootstrap5\Models\" />
+ <Folder Include="Areas\StocksoData\Controllers\" />
+ <Folder Include="Areas\StocksoData\Data\" />
+ <Folder Include="Areas\StocksoData\Models\" />
+ <Folder Include="Areas\StocksoData\Views\" />
<Folder Include="Views\Indexer\" />
</ItemGroup>
step 2:Add Controller
1. Add BaseAdminController
Nop.Web.Areas.Admin.BaseAdminController<-Nop.Web.Framework.Controllers.BaseController<-Controller
add [Area("Admin")] or [Area(AreaNames.Admin)] on Nop.Web.Areas.Admin.BaseAdminController
2.Add BlogController
\src\Presentation\Nop.Admin\Controllers\BlogController.cs
namespace Nop.Admin.Controllers
{
public class BlogController : BaseAdminController
{
3. Add below code in Nop.Web.Start.cs
In order to not specify any area name, you can use this.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute(
name: "areas",
pattern: "{area}/{controller}/{did?}/{action=Index}/{id?}");
});
step 3. Add Models
4.Models Validators
Install FluentValidation
<PackageReference Include="FluentValidation" Version="8.5.1" />
<PackageReference Include="FluentValidation.AspNetCore" Version="8.5.1" />
<PackageReference Include="FluentValidation.ValidatorAttribute" Version="8.5.1" />
add rule
src\Presentation\Nop.Admin\Validators\Blogs\BlogPostValidator.cs
using FluentValidation;
using Nop.Admin.Models.Blogs;
using Nop.Services.Localization;
using Nop.Web.Framework.Validators;
namespace Nop.Admin.Validators.Blogs
{
public class BlogPostValidator : BaseNopValidator<BlogPostModel>
{
public BlogPostValidator(ILocalizationService localizationService)
{
RuleFor(x => x.Title)
.NotEmpty()
.WithMessage(localizationService.GetResource("Admin.ContentManagement.Blog.BlogPosts.Fields.Title.Required"));
RuleFor(x => x.Body)
.NotEmpty()
.WithMessage(localizationService.GetResource("Admin.ContentManagement.Blog.BlogPosts.Fields.Body.Required"));
//blog tags should not contain dots
//current implementation does not support it because it can be handled as file extension
RuleFor(x => x.Tags)
.Must(x => x == null || !x.Contains("."))
.WithMessage(localizationService.GetResource("Admin.ContentManagement.Blog.BlogPosts.Fields.Tags.NoDots"));
}
}
}
use
\src\Presentation\Nop.Admin\Models\Blogs\BlogPostModel.cs
using FluentValidation.Attributes;
...
namespace Nop.Admin.Models.Blogs
{
[Validator(typeof(BlogPostValidator))]
public partial class BlogPostModel : BaseNopEntityModel
{
[NopResourceDisplayName("Admin.ContentManagement.Blog.BlogPosts.Fields.Language")]
public ObjectId LanguageId { get; set; }
[NopResourceDisplayName("Admin.ContentManagement.Blog.BlogPosts.Fields.Language")]
public string LanguageName { get; set}
...
}
}
5.AdminAntiForgery
[AdminAntiForgery(true)]
AdminAntiForgeryAttribute
src\Presentation\Nop.Web.Framework\Security\AdminAntiForgeryAttribute.cs
namespace Nop.Web.Framework.Security
{
public class AdminAntiForgeryAttribute : TypeFilterAttribute
{
/// <summary>
..
}
}
use
src\Presentation\Nop.Admin\Controllers\DownloadController.cs
[HttpPost]
//do not validate request token (XSRF)
[AdminAntiForgery(true)]
public virtual IActionResult SaveDownloadUrl(string downloadUrl)
{
//insert
var download = new Download
{
DownloadGuid = Guid.NewGuid(),
UseDownloadUrl = true,
DownloadUrl = downloadUrl,
IsNew = true
};
_downloadService.InsertDownload(download);
return Json(new { downloadId = download.Id });
}
6.add Models Automapper
src\Presentation\Nop.Admin\Controllers\SettingController.cs
public ActionResult EmailAccount()
{
//if (!_permissionService.Authorize(StandardPermissionProvider.ManageSettings))
// return AccessDeniedView();
var emailAccountSettings = _settingService.GetSetting<EmailAccountSettings>();
var configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<EmailAccountSettings, EmailAccountSettingsModel>();
});
var mapper = configuration.CreateMapper();
var model = mapper.Map<EmailAccountSettingsModel>(emailAccountSettings);
return View(model);
}
{
//if (!_permissionService.Authorize(StandardPermissionProvider.ManageSettings))
// return AccessDeniedView();
//load settings for a chosen store scope
//var storeScope = this.GetActiveStoreScopeConfiguration(_storeService, _workContext);
if (ModelState.IsValid)
{
var emailAccountSettings = _settingService.GetSetting<EmailAccountSettings>();
var configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<EmailAccountSettingsModel,EmailAccountSettings>();
});
var mapper = configuration.CreateMapper();
emailAccountSettings = mapper.Map<EmailAccountSettings>(model);
_settingService.SaveSetting<EmailAccountSettings>(emailAccountSettings);
..
}
add 菜单
sitemap.config:菜单项目定义文件,包含菜单名称、访问路径和访问权限
src\Presentation\Nop.Admin\sitemap.config
<?xml version="1.0" encoding="utf-8" ?>
<siteMap>
<siteMapNode SystemName="Home" nopResource="Admin.Home" controller="Home" action="Overview">
<siteMapNode SystemName="Dashboard" nopResource="Admin.Dashboard" controller="Home" action="Index" IconClass="fa-desktop" />
<siteMapNode SystemName="Catalog" nopResource="Admin.Catalog" PermissionNames="ManageProducts,ManageCategories,ManageManufacturers,ManageProductReviews,ManageProductTags,ManageAttributes" IconClass="fa-book" >
</siteMapNode>
添加控制器权限管理
定义权限的静态类
G:\Nopcommerce\wwwstocksocom\src\Libraries\Nop.Services\Security\StandardPermissionProvider.cs
namespace Nop.Services.Security
{
/// <summary>
/// Standard permission provider
/// </summary>
public partial class StandardPermissionProvider : IPermissionProvider
{
//admin area permissions
public static readonly PermissionRecord AccessAdminPanel = new PermissionRecord { Name = "Access admin area", SystemName = "AccessAdminPanel", Category = "Standard" };
public static readonly PermissionRecord AllowCustomerImpersonation = new PermissionRecord { Name = "Admin area. Allow Customer Impersonation", SystemName = "AllowCustomerImpersonation", Category = "Customers" };
...
}
}
将用户角色和定义的权限进行比较,若有权限则返回true
public ActionResult List()
{
if (!_permissionService.Authorize(StandardPermissionProvider.ManageBlog))
return AccessDeniedView();
return View();
}
startup.cs
step 1: add IWebHostEnvironment
namespace Nop.Admin
{
public class Startup
{
public Startup(IConfiguration configuration)
public Startup(IConfiguration configuration, IWebHostEnvironment webHostEnvironment)
{
Configuration = configuration;
_webHostEnvironment = webHostEnvironment;
}
public IConfiguration Configuration { get; }
private readonly IWebHostEnvironment _webHostEnvironment;
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
客户端和服务端api交互
kendui 要用到json 返回的变量名第一个字母大写,才显示正常。缺省的模板api是小写。
install Microsoft.AspNetCore.Mvc.NewtonsoftJson Nuget Package
in the Startup’s ConfigureServices, you will need to configure MVC like this:
services.AddControllers()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
});
运行后台安装中、english语言包
Language
LocaleStringResource 语言ID要保持一致