2021暑假成都
2021年小升初后去成都
如果在编写代码时,养成良好的习惯,严格遵守规范,代码的可复用性和可维护性就高很多。评价代码的质量,不是在需求明确且不发生变化的情况下能够运行就可以了,而是在发生变化的情况下是否仍然保持稳定。
在我的文章《从零开始进行ABP项目开发——为什么从零开始搭建项》中,说明了为什么要从零开始搭建ABP项目,而不是从现有的模板入手。同样的原因也适用于其它技术的学习:从零开始,可以使我们了解各种内在的关联,知道很多设计结果的来龙去脉。我一直认为模板不是为入门选手学习而准备的,而是为熟练的工程师节省创建时间而准备的。现在我们开始从一个空项目一步一步创建Asp.Net Core应用。这里要解决的问题是创建一个最简单的项目,可以访问静态页面。
首先,使用Visual Studio 2019 创建一个空的Web项目:
创建时选择空Web项目。这样所创建的项目中,只有Program.cs、StartUp.cs和appsettings.json这几个文件。运行这个项目,会启动浏览器,显示“Hello World”。
现在,我们要为项目增加静态html文件,首先创建目录wwwroot,因为所有的静态文件都需要放置在这个目录中。然后在这个目录中增加一个html文件index.html,在文件中随便写点什么:
1 | <!DOCTYPE html> |
然后修改StartUp:
1 |
|
我们只增加了app.UseStaticFiles(),说明我们需要应用可以访问静态文件。再次运行项目,可以在浏览器中访问index.html:
基本的功能已经有了,下一步我们将这个应用部署到IIS。
现在我们部署已经创建好的简单应用。在Visual Studio 2019菜单中选择生成->发布,然后选择文件作为发布目标:
按“创建配置文件”按钮,创建一个发布配置:
按发布按钮,在指定的文件夹中生成了需要发布的文件:
这时可以直接运行文件夹中的exe文件,项目作为独立的应用运行:
在浏览器中可以访问http://localhost:5000
如果需要在IIS中托管运行,需要按照如下步骤进行。
(1)如果没有安装Asp.Net Core的托管服务,需要下载并安装,下载地址如下:
https://www.microsoft.com/net/permalink/dotnetcore-current-windows-runtime-bundle-installer
(2)创建一个应用程序池,将.NET CLR版本设置为无托管代码
(3)将生成的部署文件拷贝到需要部署的目录,比如TestWebSite:
(4)在IIS中创建网站或者应用,指向部署目录,并且设置应用程序池为(2)中创建的:
(5)在浏览器中可以访问这个应用了:
在Startup的Configure中,可以使用UseEndPoints定义URL与处理程序之间的对应。MapPost可以映射POST方法的Url地址和处理函数,我们可以在这里编写简单的中间件代码,下面是简单的示例:
1 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) |
这里,我们定义了Url”/SaveGraph”响应POST方法,用于接收页面发送的数据,使用context.Reqeust.Form[key]可以获取POST发送的数据。定义了“/GetXML”,用于根据xml的文件名,获取文件的内容,可以使用context.Request.Query[key]获取Url中的参数。
我们从零开始创建了一个Asp.Net Core的项目,在这个项目中增加了自定义的中间件和静态页面,现在我们要使用Asp.Net Core引入的轻量级的页面技术RazorPage创建简单的页面。在项目的Startup.cs中增加如下代码:
1、在ConfigrueServices中增加services.AddRazorPages():
1 | public void ConfigureServices(IServiceCollection services) |
2、在Configure的app.UseEndpoints中增加,endpoints.MapRazorPages(),位置在自定义的路由后面:
1 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) |
然后在项目中创建Pages文件夹,在这个文件夹中添加RazorPage:
RazorPage就已经添加好了,可以进行访问了。在创建的空的Asp.Net Core项目中,已经有了对RazorPage的支持,不需要增加新的依赖项,只需要在服务中增加RazorPage的支持,并在endpoints中定义映射就可以了。
增加MVC的支持与增加RazorPage的支持类似,只要1、在ConfigureServices中增加对MVC的支持,2、增加endpoines的映射就可以,具体的代码如下:
1 | public void ConfigureServices(IServiceCollection services) |
···
在endpoints.MapRazorPages();后面增加:
1 | endpoints.MapControllerRoute( |
然后在项目中增加Controllers文件夹和Views文件夹,然后在Controllers增加一个控制器MyPageController,在Views文件夹中创建MyPage文件夹,在这个文件夹中增加Razor视图Index.cshtml。
MVC的支持就增加完成了。
这里有一个问题,如果RazorPage中有与MVC路径相同的页面,系统如何处理?比如我们创建一个RazorPage,名称为MyPage,会怎么样呢?如果url中没有action,会访问RazorPage:
如果访问MVC页面需要增加index:
Asp.Net Core缺省情况下会在发布时将视图编译到动态库,这样在发布后是不能编辑视图文件的。可是在实际项目中,我们经常需要在发布后编辑视图文件,这种情况下,我们需要启动视图的动态编译功能。
首先,通过NuGet安装 Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation,然后,在ConfigureServices中增加.AddRazorRuntimeCompilation():
1 | public void ConfigureServices(IServiceCollection services) |
最后,需要在项目文件中将CopyRazorGenerateFilesToPublishDirectory设置为true:
1 |
|
这时,在执行发布后,在发布的目录中可以发现Views和Pages目录,分别保存RazorPage和MVC的视图。我们就可以在发布后编辑视图文件了。
Asp.Net Framework的配置文件是web.config,Asp.Net Core中配置文件是appsettings.json。在StartUp中可以通过依赖注入获取IConfiguration对象,对配置项进行读取:
1 | public class Startup |
读取配置项很简单,比如在appsettings.json中有如下配置项:
1 | { |
通过下面的代码可以读取PlugIns:
1 | var plugins=Configuration["PlugIns"]; |
读取上面的Logging中子项的信息也很简单,只要在层次之间使用冒号就可以:
1 | var defaultLogLevel=Configuration["Logging:LogLevel:Default"]; |
IConfiguration 可以注入到RazorPageModel和MVC的Controller中,这样在RazorPage和MVC中可以获取配置项。配置项的键值大小写不敏感,ConnectionString和connectionstring是一样的。
@{
Layout = “_Layout”;
}
1 | 如果没有特殊定义,所有的页面使用_Layout布局页面。 |
1 | _Header.cshtml和_Footer.cshtml也在Shared文件夹中,通过partial引用。<partial name="_Header" />等效于下面的代码: |
1 | 可以实现对局部视图的异步加载,原来的@Html.Partial("_Header")仍然可以使用,不过会有警告,提示可能出现阻塞。 |
services.AddDefaultIdentity<IdentityUser>(
options => { options.SignIn.RequireConfirmedAccount = false; }
)
.AddEntityFrameworkStores<ApplicationDbContext>();
1 | 这里需要注意的是 options.SignIn.RequireConfirmedAccount 设置项,缺省设置为true,这种情况下,新注册的用户需要进行确认才能完成注册,如果没有安装邮件系统,这个步骤无法完成,所以这里改为false。 |
services.Configure<IdentityOptions>(options =>
{
// Password settings.
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireDigit = false;
options.Password.RequiredLength = 1;
options.Password.RequiredUniqueChars = 1;
// Lockout settings.
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// User settings.
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
options.User.RequireUniqueEmail = false;
});
1 | ## 现有项目中增加身份验证 |
public class IdentityHostingStartup : IHostingStartup
{
public void Configure(IWebHostBuilder builder)
{
builder.ConfigureServices((context, services) => {
services.AddDbContext
options.UseSqlServer(
context.Configuration.GetConnectionString(“ZLWorkflowWebEditorContextConnection”)));
services.AddDefaultIdentity<ZLWorkflowWebEditorUser>(options => options.SignIn.RequireConfirmedAccount = false)
.AddEntityFrameworkStores<ZLWorkflowWebEditorContext>();
services.Configure<IdentityOptions>(options =>
{
// Password settings.
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireDigit = false;
options.Password.RequiredLength = 1;
options.Password.RequiredUniqueChars = 1;
// Lockout settings.
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// User settings.
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
options.User.RequireUniqueEmail = false;
});
});
}
}
1 | 还需要修改的是在StartUp中,增加: |
app.UseAuthentication();
app.UseAuthorization();
1 | 最后,需要修改appsettings中的数据库连接,并且在程序包控制台中执行Update-Database,生成需要的数据库结构。 |
public void ConfigureServices(IServiceCollection services)
{
var mvcBuilders = services.AddMvc();
String basePath2 = System.IO.Path.GetDirectoryName(typeof(Program).Assembly.Location);
var plugins = Configuration["PlugIns"];
if (!string.IsNullOrEmpty(plugins))
{
var arr = plugins.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
foreach (var plugin in arr)
{
var mypath = System.IO.Path.Combine(basePath2, plugin);
var myPlugin = System.Runtime.Loader.AssemblyLoadContext.Default
.LoadFromAssemblyPath(mypath);
mvcBuilders.AddApplicationPart(myPlugin);
}
}
}
1 | 这里,我们将插件名称保存在配置文件的PlugIns配置项中,多个插件用逗号隔开。如果插件中包含视图动态库,也需要包括进来。下面是配置文件的例子: |
{
“Logging”: {
“LogLevel”: {
“Default”: “Information”,
“Microsoft”: “Warning”,
“Microsoft.Hosting.Lifetime”: “Information”
}
},
“AllowedHosts”: “*”,
“PlugIns”: “RazorPlugin,RazorPlugin.Views.dll,MyRazor,MyRazor.Views.dll”
}
1 | 这里包含两个插件RazorPlugin和MyRazor,以及它们各自的视图RazorPlugin.Views.dll和MyRazor.Views.dll。 |
public class SayHelloViewComponent : ViewComponent
1 | 或者使用ViewComponent作为类名的后缀。也可以使用[ViewComponent]属性进行修饰,这三种方法都可以。 |
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace RazorPlugin.Components
{
public class SayHelloViewComponent : ViewComponent
{
public async Task
string Name)
{
var view = View("Default", Name);
return view;
}
}
}
1 | 3、定义ViewComponent的Razor页面部分,类似于MVC的视图,这个文件可以保存在如下位置: |
@model string
1 | 4、在视图、RazorPage或者其它ViewComponent中可以引用已定义的组件: |
根据流程定义文件生成测试用例,每个分支一个用例,需要进行流程图遍历