65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.46/5 (39投票s)

2019 年 6 月 1 日

CPOL

12分钟阅读

viewsIcon

80990

downloadIcon

1775

本文将引导您从零开始使用 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 类开始。此类包含两个非常重要的方法,名为 ConfigureServicesConfigure。这两个方法都会被 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 页面,如下所示

现在运行应用程序,您将在导航栏中看到两个附加链接。当然,您现在可以自己尝试 RegisterLogin 功能了。是不是很酷?

添加授权支持

到目前为止,我们已经提供了登录功能,但是如何限制用户使用网站的某些功能呢?例如,只有登录用户才能提供反馈。这时就涉及到授权的概念了。让我们通过在 Feedback 控制器上添加 Authorize 属性来快速实现,如下所示

[Authorize]
public class FeedbackController : Controller
{
   ……         
}

我们完成了,现在是时候运行应用程序并验证所有内容了。

希望您喜欢创建自己的 ASP.NET Core 应用程序,其中涵盖了所有基本概念。

参考文献

© . All rights reserved.