逐步创建 ASP.NET Core 2.2 应用程序






4.46/5 (39投票s)
本文将引导您从零开始使用 ASP.NET Core 2.2 创建 ASP.NET Core 应用程序,充分利用程序包管理器、EF Core、Identity API、Razor 类库等功能。
引言
本文将引导您从零开始使用 ASP.NET Core 2.2 创建 ASP.NET Core 应用程序,充分利用程序包管理器、EF Core、Identity API、Razor 类库等功能。因此,本文将更多地侧重于实现部分,而非理论。在需要时,我也会对概念部分进行一些阐述。
先决条件
具备 C# 知识,并了解 Web 设计及其概念。
机器配置
本文教程使用的机器配置为 Visual Studio 2017,包含 .NET Core 开发工具/SDK。请确保您已安装 .NET Core 2.2 或更高版本。
预期
阅读完本文后,读者将能够拥有一个用于“Cookie 商店”的 ASP.NET Core 网站的可用模型。
准备开始
背景已就绪,我们可以开始了。让我们一步一步地创建我们的应用程序。
创建新的 ASP.NET Core 2.2 应用程序
让我们打开 Visual Studio。转到“文件”菜单,选择“新建”,然后选择“项目”。在选择“Web”作为类别后,您将看到以下对话框
单击“确定”后,您将看到以下对话框,可以在其中重新验证所选的 ASP.NET Core 版本。作为学习者,最好选择“Empty”模板,这样我们就可以自己处理所有事情,而不必依赖自动生成的代码。选择“Empty”并继续。
项目成功创建后,您将看到“Dependency”节点已添加了所需的引用,如下所示
在这里,让我们删除 appsettings.json 文件,因为稍后我们会创建自己的文件,并在项目下添加一个名为 wwwroot 的新文件夹。创建 wwwroot 文件夹后,您会注意到文件夹图标发生了变化。这是一个特殊文件夹,用于存储所有静态文件(css、图像文件、JavaScript 文件等),并直接映射到网站 URL。
集成中间件和服务
让我们从 Startup
类开始。此类包含两个非常重要的方法,名为 ConfigureServices
和 Configure
。这两个方法都会被 ASP.NET Core 自动调用。使用 ConfigureServices
,我们可以使用 IServiceCollection
将服务添加到依赖注入容器中。所以,让我们添加 MVC 服务,如下所示
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
接下来是 ConfigureServices
方法,它用于使用将为我们的请求提供服务的组件配置中间件管道。这是集成所需中间件的初始代码
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseStatusCodePages();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
请注意,添加中间件的顺序非常重要。现在我们已经为应用程序打下了基础,让我们继续设置其他有用的组件。
创建模型和存储库类
让我们首先在解决方案下创建一个名为 Models 的文件夹,并在其中创建一个名为 Cookie
的类。
public class Cookie
{
public int Id { get; set; }
public string Name { get; set; }
public string TinyDescription { get; set; }
public string FullDescription { get; set; }
public decimal Price { get; set; }
public string ImageUrl { get; set; }
public string ImageThumbnailUrl { get; set; }
public bool IsCookieOfTheDay { get; set; }
}
现在,在此文件夹下快速创建一个名为 IRepository
的接口,并包含以下两个初始方法
public interface IRepository
{
IEnumerable<cookie> GetAllCookies();
Cookie GetCookieById(int id);
}
下一个任务是创建一些虚拟数据,我们可以使用这些数据来验证我们的应用程序是否按预期工作。以下是 IRepository
接口的实现
public class MockRepository : IRepository
{
private List<cookie> _cookies;
public MockRepository()
{
if (_cookies == null)
{
InitializeRepository();
}
}
private void InitializeRepository()
{
_cookies = new List<cookie>
{
new Cookie
{ Id=1,Name="Raspberry", Price=110,
TinyDescription="Dark chocolate overloaded",
FullDescription ="This is one of the best ever
soft and chewy cookie and also been awarded as the
best cookie several times.", IsCookieOfTheDay=false,
ImageUrl ="\\Images\\1.png"},
new Cookie{ Id=2, Name="Nuts Overloaded",
Price=100, TinyDescription="Truely healthy",
FullDescription ="This cookie is fully loaded
with nuts of various types and contains
nice amount of peanut butter.", IsCookieOfTheDay=true,
ImageUrl ="\\Images\\2.png"},
new Cookie{Id=3, Name="Chocolate",
Price=70,TinyDescription="Amazingly fruity",
FullDescription ="This cookie is best suited
for the chocolate lovers. It's less sweet and gives
very elegant taste.", IsCookieOfTheDay=false,
ImageUrl ="\\Images\\3.png"},
new Cookie{Id=4, Name="Delicious Oatmeal",Price=50,
TinyDescription="Truely healthy",
FullDescription ="This is one of the most moist and
flavourful cookie, which can make anyone's mood happy.",
IsCookieOfTheDay=false,
ImageUrl ="\\Images\\4.png"},
};
}
public Cookie GetCookie(int id)
{
return _cookies.FirstOrDefault(x => x.Id == id);
}
public IEnumerable<cookie> GetCookies()
{
return _cookies;
}
}
让我们在 ConfigureServices
方法中注册此 IRepository
,如下所示
services.AddTransient<IRepository, MockRepository>(); // get me new instance every time
添加控制器
让我们在项目下创建一个名为 Controllers 的新文件夹,并在其中添加一个名为 Home
的空控制器。简单来说,控制器负责利用模型中的方法,根据用户的请求创建响应。在 MVC 中,这些方法通常被称为操作方法。以下是使用 IRepository
在控制器中获取模型数据的代码。
public class HomeController : Controller
{
private readonly IRepository _repository;
public HomeController(IRepository repository)
{
_repository = repository;
}
public IActionResult Index()
{
return View();
}
}
添加视图
到目前为止,我们已经完成了基本的模型和控制器。所以,唯一剩下的就是视图。在 ASP.NET Core 2.2 中,视图可以有两种类型:常规/普通视图和强类型视图。但在大多数情况下,都需要强类型视图。在这里,我们将使用 Razor。
因此,让我们在项目下创建一个名为 Views 的新文件夹,并在其中创建一个名为 Home 的子文件夹。右键单击 Home 文件夹并添加新项“Razor View”。成功添加视图后,您会注意到 Index.cshtml 文件已添加到 Home 文件夹下。
现在是时候验证视图和控制器是否正确连接并且能够进行通信了。为了进行验证,让我们为页面添加一个标题,并将此标题的值从控制器类使用 ViewBag
传递。同样,我们还将显示一些关于 Cookie 的信息。以下是 HomeController
的更新方法
public IActionResult Index()
{
ViewBag.Title = "Cookies and only Cookies";
var cookies = _repository.GetAllCookies().OrderByDescending(x=>x.Price);
return View(cookies);
}
接下来是更新视图以读取此 title
值。以下是代码
现在,如果您运行应用程序,您将能够看到以下输出
改进 HTML 标记的包含
在上面的代码片段中,我们已经看到了编写完整的 HTML 标记来显示页面。那么,如果我们有很多视图呢?我们是否会为每个页面重复编写此 HTML 代码?
当然不是。这里就需要用到模板。我们可以创建一个模板并在所有视图中引用该模板。这样做还可以减少单个视图中的代码量。很明显,如果某个东西要在多个组件之间共享,那么它就必须保存在一个共享位置,而这正是 MVC 设计中引入共享文件夹的地方。
引入布局模板
在 Views 文件夹下添加一个名为 Shared 的新文件夹。右键单击 Shared 文件夹并添加一个名为 Razor Layout 的新项。此新添加项的默认名称是 _Layout.cshtml,它看起来如下
如果您仔细查看,可能会注意到 _Layout.cshtml 和我们的视图包含大部分公共代码。所以,是时候将 _Layout.cshtml 集成到我们的视图中了,集成后,我们的视图将只包含以下代码
使用 _ViewStart 进一步优化视图
我们通过减少视图文件中的代码行数做得很好,但仍有改进的空间。同样,我们集成到视图中的布局将在每个视图中重复。我们能摆脱这种重复吗?当然可以。
让我们在 Views 文件夹下添加另一个名为 Razor View Start 的新项,默认名称为 _ViewStart.cshtml。此文件包含默认代码,如下所示,并会自动调用
@{
Layout = "_Layout";
}
现在您可以看到,此文件已经为我们配置了 Layout
,这意味着我们视图中的代码行数进一步减少到只有几行,如下所示
您可以重新运行应用程序并验证它是否按预期工作。
引入 ViewModel
您可能已经注意到,我们的视图从多个路径获取数据。那么,为什么我们不能摆脱这种情况,创建一个单一的实体作为我们视图的数据源呢?让我们试试。
我们在 project 下添加一个名为 ViewModels 的新文件夹,并在其中添加一个名为 HomeViewModel
的新类。这个 ViewModel
类将是我们 Home
视图的数据源。目前,我们将只包含最基本字段,如下所示
public class HomeViewModel
{
public string Title { get; set; }
public List<cookie> Cookies { get; set; }
}
相应地,我们必须更新 HomeController
中的 Index
方法,如下所示
public IActionResult Index()
{
HomeViewModel viewModel = new HomeViewModel
{
Title = "Cookies and only Cookies",
Cookies = _repository.GetAllCookies().OrderByDescending(x => x.Price).ToList()
};
return View(viewModel);
}
最后,让我们更新 View
,并引用 HomeViewModel
,如下所示
重新验证输出,结果应该仍然相同。
使用 Bootstrap 改善视图的外观
这可以使用一个名为 Bootstrap 的客户端包来完成。根据 Visual Studio 的版本和更新,可以选择任何包管理器,如 Bower、Library Manager (LibMan
) 等。在这里,我将使用 Library Manager,因为我的 Visual Studio 版本是 15.8.5。让我们去项目级别添加一个名为“Add Client-Side Library”的新项,并提供如下详细信息
接下来是向我们的应用程序添加图像。为此,我们必须在 wwwroot 下创建另一个名为 Images 的文件夹。
然后,我们需要为我们的应用程序添加一个样式表。为此,必须在 wwwroot 下创建一个名为 content 的新文件夹,并在其中创建一个名为 site.css 的 CSS 文件。完成此操作后,我们可以添加以下基本样式
body {
padding-top:50px;
padding-bottom:20px;
background-image: url();
background-repeat: repeat;
}
.body-content{
padding-left: 15px;
padding-right: 15px;
}
最后,我们必须使用 <Link>
将 CSS 和 Bootstrap 关联到我们的 _Layout 页面,如下所示
接下来是更新视图文件以适应 Bootstrap。以下是完整代码
如果一切顺利,运行应用程序时将看到以下网页
接下来是为我们的应用程序关联实际数据库。
使用 EF Core 获取实际数据
EF Core 是一个 ORM,支持跨平台功能,如 ASP.NET Core。值得一提的是,截至目前,EF Core 只支持 Code-First 方法。以下是完全集成 EF 到我们的应用程序中需要注意的步骤
- 创建实体类
- 创建数据库上下文
- 设置连接字符串
- 应用程序配置更改
让我们在 Models 文件夹下创建一个名为 DatabaseContext
的新类,并将以下代码放入其中
public class DatabaseContext:DbContext
{
public DatabaseContext(DbContextOptions<databasecontext> options):base(options)
{
}
public DbSet<cookie> Cookies { get; set; }
}
DataContext
在连接我们的应用程序到实际数据库方面起着至关重要的作用。到目前为止,我们一直在使用 MockRepository
类进行模拟值。所以,是时候创建一个名为 Repository
的实际类,放在 Models 文件夹下,并包含以下代码
public class Repository:IRepository
{
private readonly DatabaseContext _dbContext;
public Repository(DatabaseContext databaseContext)
{
_dbContext = databaseContext;
}
public Cookie GetCookie(int id)
{
return _dbContext.Cookies.FirstOrDefault(x => x.Id == id);
}
public IEnumerable<cookie> GetCookies()
{
return _dbContext.Cookies;
}
}
接下来是设置连接字符串。希望大多数人都知道 ASP.NET Core 不再使用 Web.Config 文件,而是使用 appsettings.json。因此,添加一个名为 appsettings.json 的新项“App Settings File”,并在其中更新连接字符串。我的代码看起来像这样
{
"ConnectionStrings": {
"DefaultConnection":
"Server=(localdb)\\MSSQLLocalDB;Database=CookiesDataStore;
Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
作为最后一步,我们必须将 EF Core 注册到我们的应用程序中,这可以通过更新 Startup
类中的 ConfigureServices()
来完成,如下所示
public class Startup
{
public IConfiguration Configuration { get; set; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DatabaseContext>(options => options.UseSqlServer
(Configuration.GetConnectionString("DefaultConnection")));
services.AddTransient<IRepository, Repository>();
services.AddMvc();
}
// This method gets called by the runtime.
// Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseStatusCodePages();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
}
现在是时候构建代码并验证不再有编译错误了。
创建数据库
为了在网页上显示内容,我们需要数据库中有数据。在这里,我们将使用程序包管理器控制台执行以下命令
PM> add-migration CookiesDatabaseMigration
PM> update-database
为了向数据库添加一些初始数据,我们将创建一个名为 DbInitializer
的新类,放在 Models 文件夹下,并包含以下代码
public static class DbInitializer
{
public static void Seed(DatabaseContext dbContext)
{
if (!dbContext.Cookies.Any())
{
dbContext.AddRange(
new Cookie
{
Name = "Choco Chips",
Price = 80,
TinyDescription = "Dark chocolate overloaded",
FullDescription = "This is one of the most moist
and flavourful cookie,
which can make anyone's mood happy.",
IsCookieOfTheDay = false,
ImageUrl = "\\Images\\Chocochip.png"
},
new Cookie
{
Name = "Nuts & Peanuts",
Price = 75,
TinyDescription = "Truely healthy",
FullDescription = "This cookie is fully loaded
with nuts of various types and
contain nice amount of peanut butter.",
IsCookieOfTheDay = true,
ImageUrl = "\\Images\\ChocolateChipWalnut.png"
},
new Cookie
{
Name = "Berries & Rasins",
Price = 50,
TinyDescription = "Amazingly fruity",
FullDescription = "This is one of the best ever soft
and chewy cookie and also been awarded
as the best cookie several times.",
IsCookieOfTheDay = false,
ImageUrl = "\\Images\\Nuts.png"
},
new Cookie
{
Name = "Coconut",
Price = 100,
TinyDescription = "Truely healthy",
FullDescription = "This cookie is best suited
for the nut lovers. It's less sweet and
gives very elegant taste.",
IsCookieOfTheDay = false,
ImageUrl = "\\Images\\Coconut.png"
}
);
}
dbContext.SaveChanges();
}
}
接下来是从 Program
类调用此 DbInitializer
,这是更新后的代码
public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<databasecontext>();
DbInitializer.Seed(context);
}
catch (Exception ex)
{
// TODO
}
}
host.Run();
}
现在快速运行应用程序,您将看到与之前相同的输出。
添加导航
作为导航的一部分,我们将创建一个详细信息页面,其中将包含所选 Cookie 的详细信息。为了获取所选 Cookie 的详细信息,必须在 HomeController
类中添加一个新方法,如下所示
public IActionResult Details(int id)
{
var cookie = _repository.GetCookie(id);
return View(cookie);
}
当然,我们必须为该方法添加视图,名称为 Details.cshtml,位于 HomeController
下
现在,我们希望从 Index
视图导航到 Details
视图。所以,我们将使用标签助手,如下所示
最后一件事情是在我们的主页上添加导航,这可以通过使用
nav
元素来完成。以下是 _Layout.cshtml 的更新代码
现在快速运行应用程序,您将在页面顶部看到以下链接
添加表单以请求评论
现在,让我们添加一个表单,用户可以通过该表单为我们可爱的 Cookie 提供反馈或评论。为了做到这一点,我们必须添加一个新的模型实体,名为 Feedback
,如下所示
public class Feedback
{
public int Id { get; set; }
public string Name { get; set; }
public string Content { get; set; }
public string Email { get; set; }
}
接下来是更新数据库中的此新实体,可以通过运行 add-migration
命令来完成
PM> Add-Migration Feedback
让我们快速添加接口和其中的一个方法来保存反馈,代码如下
public class FeedbackRepository: IFeedbackRepository
{
private readonly DatabaseContext _dbContext;
public FeedbackRepository(DatabaseContext context)
{
_dbContext = context;
}
public void AddFeedback(Feedback feedback)
{
_dbContext.Feedbacks.Add(feedback);
_dbContext.SaveChanges();
}
}
接下来是在 ConfigureServices
方法下将此新接口注册到依赖注入容器
public void ConfigureServices(IServiceCollection services)
{
…
services.AddTransient<IFeedbackRepository, FeedbackRepository>();
services.AddMvc();
}
现在是时候进行 UI 更改以实现反馈功能了。以下是 FeedbackController
及其视图的代码
public class FeedbackController : Controller
{
private readonly IFeedbackRepository _feedbackRepository;
public FeedbackController(IFeedbackRepository feedbackRepository)
{
_feedbackRepository = feedbackRepository;
}
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult Index(Feedback feedback)
{
_feedbackRepository.AddFeedback(feedback);
return View();
}
}
接下来是在应用程序主页上添加“Feedback”链接,这可以通过更新
Layout
页面来完成,如下所示
点击
Feedback
后,将导航到以下页面
保护应用程序配置身份
在本节中,我们将探索 ASP.NET Core Identity API,并为此,我们需要稍微更新我们现有的代码。让我们从 DatabaseContext
类开始。现在,我们将 DatabaseContext
类继承自 IdentityDbContext<IdentityUser>
,而不是继承自 DbContext
。同时,我们必须通过添加 app.UseAuthentication()
来更新我们的中间件管道。
接下来,我们需要更新数据库中的用户信息。所以,我们必须像下面这样运行 add 和 update 迁移
PM> add-migration AuthenticationAdded
PM> update-database
成功执行后,您将在数据库中看到创建了以下表
添加身份验证支持
为了向我们的应用程序添加身份验证功能,我们将使用 ASP.NET Core 附带的 Razor 类库提供的开箱即用功能。要做到这一点,右键单击项目,选择 **Add**,然后选择 **New Scaffolded Item**…,选择 **Identity**。
执行上述操作后,我们将看到以下对话框,其中列出了所有现成的视图。在这里,我将选择三个视图及其所需的数据上下文,如下所示
public class IdentityHostingStartup : IHostingStartup
{
public void Configure(IWebHostBuilder builder)
{
builder.ConfigureServices((context, services) => {
services.AddDefaultIdentity<identityuser>(IdentityUser)
.AddEntityFrameworkStores<databasecontext>(DatabaseContext);
});
}
}
通过上面的代码,我们使用数据库上下文来保存身份信息。最后,我们必须在导航栏中提供链接,所以只需通过 <Partial>
将登录相关内容插入到 _Layout
页面,如下所示
现在运行应用程序,您将在导航栏中看到两个附加链接。当然,您现在可以自己尝试 Register
和 Login
功能了。是不是很酷?
添加授权支持
到目前为止,我们已经提供了登录功能,但是如何限制用户使用网站的某些功能呢?例如,只有登录用户才能提供反馈。这时就涉及到授权的概念了。让我们通过在 Feedback 控制器上添加 Authorize
属性来快速实现,如下所示
[Authorize]
public class FeedbackController : Controller
{
……
}
我们完成了,现在是时候运行应用程序并验证所有内容了。
希望您喜欢创建自己的 ASP.NET Core 应用程序,其中涵盖了所有基本概念。
参考文献
- 课程由 Gill Cleeren 和 MSDN 文档 提供