ASP.NET核心之路微服务第01部分:构建视图

目录

介绍

软件要求

创建项目

Index页面

样式

品牌

部分视图

搜索产品部分视图

Basket视图

Basket部分视图

登记视图

结帐视图

通知视图

JSON产品加载

应用导航

结论


  • 下载第1部分来源 - 2.9 MB

介绍

 

ASP.NET核心之路微服务第01部分:构建视图_第1张图片

在本文中,我将展示如何在Visual Studio的帮助下使用ASP.NET Core创建基本的电子商务应用程序流。

本文是演示可在ASP.NET核心电子商务应用程序中实现的各种实践,模式,技术和框架的系列文章中的第一篇,同时我们逐步实现构建微服务解决方案的最终目标。

ASP.NET核心之路微服务第01部分:构建视图_第2张图片

 

也就是说,如果您对本文中没有找到任何真正的微服务感到有点失望,那是因为我想要涵盖的主题太多了,仅仅在一篇文章中就不可能讨论所有这些问题(或者讨论它们,而不仅仅是表面上的问题)。此外,我不会立即提供微服务,因为我希望以渐进式逐步方法工作,重构和推进代码库,因为我们将继续前进。请耐心等待,享受骑行乐趣。

由于这只是整个系列的第一篇文章,我想列举下一部分的设想主题:

  • 1部分:构建视图
  • 第2部分:查看组件
  • 第3部分:ASP.NET Core Identity
  • 4部分:SQLite
  • 5部分:Dapper
  • 6部分:SignalR
  • 7部分:单元测试Web API
  • 8部分:单元测试Web MVC应用程序
  • 9部分:监测健康检查
  • 10部分:Redis数据库
  • 11部分:IdentityServer4
  • 12部分:订购Web API
  • 13部分:Basket Web API
  • 14部分:Catalog Web API
  • 15部分:具有Polly的弹性HTTP客户端
  • 16部分:使用Swagger记录Web API
  • 17部分:Docker容器
  • 18部分:Docker配置
  • 19部分:使用Kibana进行中心日志

我们可以看到,有很多主题要涵盖。虽然部件已编号,但这仅用于计数目的。事实上,实际的顺序可以随着我们的进步而改变。

软件要求

ASP.NET核心之路微服务第01部分:构建视图_第3张图片

 

  • 下载并安装Visual Studio社区或高级版。

创建项目

该项目将使用Visual Studio Community创建(这可以通过Visual Studio Code甚至命令行工具完成),选择MVC项目模板。

MVC代表模型——视图——控制器,它现在是一种无处不在的软件架构模式,用于在应用关注点分离原则的同时构建用户界面。

模型部分指的是承载对象的数据,负责保存在对外用户界面中显示的信息,以及验证,收集和传输用户键入的信息到应用程序后端。

视图部分负责呈现/显示用户界面组件。通常,这些在术语中称为网页,但实际上网页在技术上是一整套HTML文件(包括页眉,正文和页脚),图像,图标,CSS样式表,JavaScript代码等。单个视图可能会呈现所有网页,但通常每个视图仅负责内部页面内容。

控制器是负责处理对一组视图的传入请求,决定需要为视图提供哪些数据,以及请求和准备这些数据以及相应地调用视图的组件。控制器还将处理数据违规,并在需要时将应用程序重定向到错误页面。

因此,让我们开始使用Visual Studio创建一个新的ASP.NET Core MVC项目。

首先,我们从Visual Studio菜单中单击新项目,选择Web Application选项。

ASP.NET核心之路微服务第01部分:构建视图_第4张图片

1:新建项目对话框

这将打开向导窗口,我们必须在其中选择Web应用程序(模型——视图——控制器)选项。请务必取消选择HTTPS配置选项,因为为了简单起见,我们不使用安全的HTTPHTTPS),至少现在不使用。

ASP.NET核心之路微服务第01部分:构建视图_第5张图片

2:新的ASP.NET核心Web应用程序选择器

一旦从选定的MVC模板加载项目,我们就可以运行(通过按F5键),现在我们可以在我们喜欢的Web浏览器中看到应用程序的主页打开。

ASP.NET核心之路微服务第01部分:构建视图_第6张图片

 

3:运行应用程序主页

上图显示了一个非常简单的Web应用程序。新项目已经为我们提供了基本MVC架构所需的文件。

现在,我们在谈论哪些文件?让我们检查Visual Studio中的解决方案树:

ASP.NET核心之路微服务第01部分:构建视图_第7张图片

4ASP.NET Core MVC项目结构

请注意上图中我们如何为所有MVC部件提供项目文件夹:ModelViewController

但是我们的ASP.NET核心MVC应用程序是如何启动的?与每个.NET应用程序一样,可执行文件具有入口点,该入口点必须是包含在Program类中的Main()方法。

ASP.NET Core应用程序中,该Main()方法必须设置并启动Web主机,即Web应用程序的主机。

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup();
}

清单1Program

正如我们在这里看到的那样,调用WebHost.CreateDefaultBuilder()方法以便可以创建webhost,但由于需要配置它,我们还必须调用UseStartup()以传递负责webhost配置的Startup 类名。让我们看看这个类是如何工作的以及它将如何在我们的应用程序中使用。

Startup结构简单。它只包含两种方法:

  • ConfigureServices()
  • Configure()

在此上下文中,服务是可以添加的任何组件,以便为我们的应用程序提供特定功能,例如:MVC,日志记录,数据库,身份验证,cookie,会话等。

这些组件也称为中间件,可以是管道的一部分。每个中间件决定是否将请求传递给管道中的下一个组件,并且可以包括要在管道中的后续组件之前或之后执行的算法。

通常,名为MyService” 的服务将在我们的Startup类中引用两次:

  • 首先,在AddMyService()方法的ConfigureServices()方法中。这里,该AddMyService()方法将提供适当的配置,以便服务可以正常运行;
  • 然后,在UseMyService()Configure()方法。

我们来看看Startup类中的方法。第一个方法是ConfigureServices(),其只配置cookie策略选项并将MVC服务添加到应用程序:

public class Startup
{
    ...
    // This method gets called by the runtime. Use this method 
    // to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure(options =>
        {
            // This lambda determines whether user consent 
            // for non-essential cookies is needed for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }
    ...

清单2Startup类中的ConfigureServices()方法

然后,Configure方法定义了一组UseService方法引用的中间件:

public class Startup
{
    ...
    // This method gets called by the runtime. 
    // Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            // The default HSTS value is 30 days. 
            // You may want to change this for production scenarios, 
            // see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseCookiePolicy();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

清单3Startup类中的Configure()方法

在这里,我们对要添加到请求管道的每个服务进行简短描述:

  • app.UseDeveloperExceptionPage:操作完成后对应用程序的引用
  • app.UseExceptionHandler:向管道添加中间件,以捕获异常,记录它们,重置请求路径,然后重新执行请求
  • app.UseHsts:为当前请求路径启用静态文件服务
  • app.UseHttpsRedirection:将CookiePolicyMiddleware处理程序添加到指定的IApplicationBuilder,从而启用cookie策略功能
  • app.UseStaticFiles:为当前请求路径启用静态文件服务
  • app.UseCookiePolicy:将CookiePolicyMiddleware处理程序添加到指定的IApplicationBuilder,从而启用cookie策略功能
  • app.UseMvc:将MVC添加到IApplicationBuilder请求执行管道

Index页面

我们将在我们的商店中使用有限的30种产品。对于每一个,我们都有一个图像,要添加到wwwroot项目文件夹内的 /images/catalog文件夹中。

ASP.NET核心之路微服务第01部分:构建视图_第8张图片

5:产品catalog图像

这些产品将在主页内的catalog视图中显示。此catalog显示为按每个产品类别分组的一组产品。每个类别都有一个名为Carousel” Bootstrap 4组件,它可以按4个产品组自动旋转category产品。

@{
    ViewData["Title"] = "Home Page";
}

@for (int category = 0; category < 6; category++)
{
    

Category Name

}

清单4catalogIndex视图

样式

这是一小步,但由于Bootstrap 4不再带有icon fonts (gliphicon),所以我们自己安装它。

Visual Studio允许我们安装客户端库,例如Font Awesome,一种流行的图标字体包。

ASP.NET核心之路微服务第01部分:构建视图_第9张图片

6:添加客户端库菜单

ASP.NET核心之路微服务第01部分:构建视图_第10张图片

7:添加客户端库对话框

既然已经安装了字体文件,我们必须在_Layout.cshtml文件中引用它们:


清单5:添加Font Awesome引用到_Layout.cshtml文件

让我们看看如何添加我们的第一个图标。在Home/Index.cshtml视图中,我们添加了一个带有fa fa-shopping-cart类的HTML元素。


    
    Add to basket

清单6:在catalogIndex视图中使用Font Awesome图标

这将自动显示添加到购物篮按钮左侧的购物车图标。

再次运行应用程序,我们看到如何呈现购物车图标:

ASP.NET核心之路微服务第01部分:构建视图_第11张图片

8Font Awesome icons

品牌

通过打开_Layout.cshtml文件,我们可以使用我们公司的名称更改品牌。

© 2019 - The Grocery Store - Privacy

清单7:在_Layout.cshtml中更改公司名称

现在,由于默认的ASP.NET Core MVC模板不包含品牌,我们自己包含它。

我们还必须在顶部栏中包含公司logo,首先为导航栏定义背景CSS规则:

9logo.png文件

a.navbar-brand {
    ...
    background: url('../images/logo.png');
    ...
}

清单8:在导航栏背景中定义公司logo

部分视图

如果你看看我们的catalog index Razor文件,你会发现它变得庞大而复杂,这可能会影响其内容的可读性和理解。

使用ASP.NET Core,我们可以使用部分视图轻松地将大型标记文件(例如我们的catalog视图)拆分为更小的组件。

部分视图是一个Razor文件(.cshtml),它将HTML元素呈现在另一个标记文件的渲染输出中。

而不是单个视图文件,现在我们的catalog视图将由各种逻辑部分组成:

  • Views/Catalog
    • Index.cshtml
    • _SearchProducts.cshtml
    • _Categories.cshtml
    • _ProductCard.cshtml

通过使用隔离的部分作为部分视图,每个文件现在具有比一体化视图文件更高的可维护性。

要将部分视图应用于我们的应用程序,首先我们将大部分标记内容提取到新的_Categories.cshtml文件。请注意_Categories.cshtml以下划线开头,这是部分视图的默认命名约定。

原始的Index.cshtml文件必须包含_Categories.cshtml标记的元素。标签实际上是一个标签辅助器(Microsoft.AspNetCore.Mvc.PartialTagHelper类),它在服务器上运行并在该位置呈现类别。

@{
    ViewData["Title"] = "Catalog";
    var products = Enumerable.Range(0, 30);
}

清单9Catalog/Index.cshtml文件

PartialTagHelper之外,还可以参考部分视图HtmlHelper。使用HtmlHelper的最佳做法是调用PartialAsync。在以下代码片段中,PartialAsync方法返回包含在Task中的IHtmlContent类型。通过在等待的调用前添加一个@字符来引用该方法,以指示Razor引擎这是一个C#代码:

@{
    ViewData["Title"] = "Catalog";
    var products = Enumerable.Range(0, 30);
}

@await Html.PartialAsync("_Categories", products);

代码清单9.1Catalog/Index.cshtml文件中的Html.PartialAsync变体

请注意,代码清单9.1中的代码产生与清单9中的代码完全相同的结果。另请注意我们如何将products模型作为方法的参数传递。

另一方面,_Categories.cshtml文件看起来与任何普通的Razor标记文件完全相同:我们可以定义@model 指令,HTML元素,标记辅助器,C#代码等。您也可以使用标记帮助程序包含内部部分视图,如下面的文件中所示:

@model IEnumerable;

@{
    var products = Model;
    const int productsPerCategory = 5;
    const int PageSize = 4;
}

@for (int category = 0; category < (products.Count() / productsPerCategory); category++)
{
    

Category @(category + 1)

}

清单10Catalog/_Categories.cshtml文件

现在,最后一个catalog部分视图必须是具有产品卡详细信息的视图。

@model int;

@{ 
    var productIndex = Model;
}

清单11Catalog/ _ProductCard.cshtml文件

请注意如何通过将产品代码与图像路径的其余部分连接起来,通过适当的路径提供产品图像URL

搜索产品部分视图

Catalog index视图不仅用于显示,还用于搜索产品。上半部分将显示一个表单,用户将在其中输入并提交搜索文本,以便只有匹配的产品或类别名称才会显示在catalog中。

同样,我们应该在主Index.cshtml Razor文件中添加一个新的部分视图标记辅助器()

@ {
    var products = Enumerable.Range(0, 30);
}



清单12Catalog/ _ProductCard.cshtml文件

请注意Index视图如何保持干净和简单。由于_SearchProducts部分视图不需要任何数据,因此不会向其传递任何参数。

_SearchProducts部分视图基本上是与将信息发送到服务器所需的一些元素(标签+文本字段+提交按钮)的表单。

Search products

清单13Catalog/ _SearchProducts.cshtml文件

到目前为止,表单没有做任何事情。但我们将在下一篇文章中实现搜索功能。

Basket视图

用户选择任何产品后,必须将他/她重定向到我的购物篮视图。此视图负责购物车功能,并将保存订单商品信息列表,例如:

  • 产品图象
  • 产品名称
  • 物品数量
  • 单价
  • 小计

到目前为止,我们只有HomeController,我们的catalog Index()操作所在的位置。我们也可以使用HomeController 来保存Basket Index,但是不要让我们应用程序中唯一的控制器混乱,让我们为catalog保留一个控制器,为basket保留另一个控制器。

但由于HomeController并没有太多的内容,所以让它通过将其名称改为CatalogController” 来使其更具描述性。这还需要我们将View/Home文件夹重命名为View/Catalog

ASP.NET核心之路微服务第01部分:构建视图_第12张图片

10:将Home controller重命名为Catalog

而且,由于CatalogController还具有用于显示Error视图的一般操作,因此最好将该操作提取到父类,该类是CatalogControllerBasketController都要继承的基类:

public abstract class BaseController : Controller
{
    [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
    public IActionResult Error()
    {
        return View(new ErrorViewModel 
           { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
    }
}

清单14BaseController.cs文件

现在让我们让两个控制器继承这个基类:

public class CatalogController : BaseController
{
    public IActionResult Index()
    {
        return View();
    }
}

public class BasketController : BaseController
{
    public IActionResult Index()
    {
        return View();
    }
}

清单15CatalogControllerBasketController

此时,如果您尝试再次执行应用程序,您会注意到应用程序崩溃的方式,因为它仍在寻找位于名为HomeController的控制器的Index操作。这称为我们使用MVC项目模板创建新项目时配置的默认路由 ”

现在,我们必须通过将默认控制器从Home” 重命名为Catalog来更改默认路由:

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Catalog}/{action=Index}/{id?}");
});

清单16:在Startup.cs中设置新的默认路由到CatalogController

至于Basket视图,我们再次使用Bootstrap组件来创建用户界面。它基本上是一个Bootstrap 组件,包括一个卡头,包含多个用于basket项名称的列标题,一个用于basket项详细信息的卡片主体,以及一个用于总计/项计数的卡片页脚。

ASP.NET核心之路微服务第01部分:构建视图_第13张图片

11Bootstrap 4卡组件

我们可以看到,到目前为止篮子项数据只是在视图中声明的数组。稍后,此数据将替换为来自控制器的数据。

@{
    ViewData["Title"] = "My Basket";

    var items = new[]
    {
        new { Id = 1, ProductId = 1, Name = "Broccoli", UnitPrice = 59.90, Quantity = 2 },
        new { Id = 2, ProductId = 5, Name = "Green Grapes", UnitPrice = 59.90, Quantity = 3 },
        new { Id = 3, ProductId = 9, Name = "Tomato", UnitPrice = 59.90, Quantity = 4 }
    };
}



My Basket

Item
Unit Price
Quantity
Subtotal
@foreach (var item in items) {
@item.Name
@item.UnitPrice.ToString("C")
@((item.Quantity * item.UnitPrice).ToString("C"))

}

清单17Catalog/Index.cshtml文件

作为最后一步,我们现在可以通过添加CSS规则来对齐basket项:

.row-center {
    display: flex;
    align-items: center;
}

清单18site.css文件

请注意我们如何使用flexbox布局,与Bootstrap 4中使用的布局完全相同。

ASP.NET核心之路微服务第01部分:构建视图_第14张图片

12basket清单

Basket部分视图

再一次,我们通过将其拆分为部分视图来分解大的Basket视图,就像我们使用Catalog标记一样。

在处理部分视图之前,让我们创建一个新类来保存basket项数据:

public class BasketItem
{
    public int Id { get; set; }
    public int ProductId { get; set; }
    public string Name { get; set; }
    public decimal UnitPrice { get; set; }
    public int Quantity { get; set; }
}

清单19BasketController.cs中定义的模拟Basket项类

部分视图的优点之一是可重用性。我们的basket项有两个部分,一个在basket列表卡下面,另一个在basket列表卡下面,两个部分都有完全相同的控制按钮:

  • 添加更多产品
  • 填写登记表

清单20Basket/ _BasketControls.cshtml文件

我们可以看到,这些标记是重复的。幸运的是,部分视图允许我们避免这种重复。

basket图现在看起来更简单,_BasketControls部分视图在basket列表部分视图的上方和下方都实现。

@using MVC.Controllers
@{
    ViewData["Title"] = "My Basket";
    List items = new List
    {
        new BasketItem { Id = 1, ProductId = 1, Name = "Broccoli", 
                         UnitPrice = 59.90m, Quantity = 2 },
        new BasketItem { Id = 2, ProductId = 5, Name = "Green Grapes", 
                         UnitPrice = 59.90m, Quantity = 3 },
        new BasketItem { Id = 3, ProductId = 9, Name = "Tomato", 
                         UnitPrice = 59.90m, Quantity = 4 }
    };
}



My Basket


清单21Basket / Index.cshtml文件

这是提取到新的部分视图(_BasketList.cshtml)的basket列表标记:

@using MVC.Controllers
@model List;

@{
    var items = Model;
}

Item
Unit Price
Quantity
Subtotal
@foreach (var item in items) { }

清单22Basket/ _BasketList.cshtml文件

对于购物篮项详细信息,我们然后创建最后一个部分视图,_BasketItem.cshtml文件。注意如何通过将数量乘以单位价格来计算小计:



@using MVC.Controllers

@model BasketItem

@{
    var item = Model;
}

@item.Name
@item.UnitPrice.ToString("C")
@((item.Quantity * item.UnitPrice).ToString("C"))

清单23Basket/ _BasketItem.cshtml文件

登记视图

在用户决定将哪些产品和数量包括在购物车中之后,用户可以选择继续完成订单。但首先,需要一些个人信息,这通常是典型电子商务程序所必需的,例如计费,发票和运输等。

ASP.NET核心之路微服务第01部分:构建视图_第15张图片

13registration 视图

using Microsoft.AspNetCore.Mvc;

namespace MVC.Controllers
{
    public class RegistrationController : BaseController
    {
        public IActionResult Index()
        {
            return View();
        }
    }
}

清单24RegistrationController

接下来,registration 视图必须包含收集个人信息所需的所有字段:

Registration

清单25Registration/Index.cshtml文件

请注意我们如何再次省略表单操作,因为将来会提供数据库更新功能。

结帐视图

一旦客户填写了个人信息,我们就假设该过程一切正常,然后将他/她重定向到新的网页,通知我们的客户订单已经下达,并要求他/她等待进一步的指示直到订单被处理并生成。

就目前而言,Checkout控制器也是一个非常简单的类,与其他类似:

public class CheckoutController : BaseController
{
    public IActionResult Index()
    {
        return View();
    }
}

清单26CheckoutController

该视图只是几行标记,带有post-basket 指令的静态内容。这里唯一的动态信息是客户电子邮件地址。

@{ 
    ViewData["Title"] = "Checkout";
    var email = "[email protected]";
}

Order Has Been Placed!

Your order has been placed.

Soon you will receive an e-mail at @email including all order details.

Back to product catalog

清单27Checkout/Index.cshtml文件

ASP.NET核心之路微服务第01部分:构建视图_第16张图片

14Checkout视图

我们的申请流程要求在购物车结账时不立即处理订单,而是在未来的某个时间点异步处理。

通知视图

public class NotificationsController : BaseController
{
    public IActionResult Index()
    {
        return View();
    }
}

清单28NotificationsController

随着客户不断购买,异步订购流程可能需要一些时间才能保存实际的数据库订单数据详细信息。因此,我们有一个通知视图,客户可以检查他/她以前的购买,从这一点,获取有关实际订单的更多信息,如开票,发货等。

@{
    ViewData["Title"] = "Notifications";
}

User Notifications


Message
Date / Time
New order placed successfully: 2
13/04/2019   18:04

清单29Notifications/Index.cshtml文件

ASP.NET核心之路微服务第01部分:构建视图_第17张图片

15:通知视图

JSON产品加载

到目前为止,我们有一个catalog,它不显示实际产品,而是显示模型数据。让我们开始一个新的重构周期,以便我们可以将更多真实数据注入catalog视图。

这种数据通常来自数据库或Web服务。但在我们的例子中,让我们通过阅读静态JSON文件来检索它们。该products.json文件被放置在我们的根项目文件夹,其内容是这样的:

[
  {
    "number": 1,
    "name": "Oranges",
    "category": "Fruits",
    "price": 5.90
  },
  {
    "number": 2,
    "name": "Lemons",
    "category": "Fruits",
    "price": 5.90
  },
  .
  .
  .
]

清单30products.json文件

在现实世界的场景中,我们的catalog数据库最初将使用此JSON文件数据进行填充。这个过程叫做seeding。我们将使用JSON文件seeding数据库。但由于我们还没有数据库,我们将使用种子数据作为catalog视图的直接来源。

我们对“MVC”中的“M”部分仍然没有做太多。对于模型,我们创建了两个类:ProductCategory。由于这两个类都具有Id属性,因此我们可以将其移动到超类以由模型类继承。

using System.Runtime.Serialization;

namespace MVC.Models
{
    public abstract class BaseModel
    {
        public int Id { get; set; }
    }
}

清单31BaseModel

public class Category : BaseModel
{
    public Category(int id, string name)
    {
        Id = id;
        Name = name;
    }

    public string Name { get; private set; }
}

清单32Category

对于Product类,我们可以提供一个新的只读ImageURL属性来计算图像路径。这将剥夺构建路径的责任。

public class Product : BaseModel
{
    public Category Category { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public string ImageURL { get { return $"/images/catalog/large_{Code}.jpg"; } }

    public Product(int id, string code, string name, decimal price, Category category)
    {
        Id = id;
        Code = code;
        Name = name;
        Price = price;
        Category = category;
    }
}

清单33Product

以下类负责读取products.json文件,将其反序列化为product对象集合,然后返回product列表。

public class SeedData
{
    public static async Task> GetProducts()
    {
        var json = await File.ReadAllTextAsync("products.json");
        var data = JsonConvert.DeserializeObject>(json);

        var dict = new Dictionary();

        var categories = 
            data
            .Select(i => i.category)
            .Distinct();

        foreach (var name in categories)
        {
            var category = new Category(dict.Count + 1, name);
            dict.Add(name, category);
        }

        var products = new List();

        foreach (var item in data)
        {
            Product product = new Product(
                products.Count + 1,
                item.number.ToString("000"),
                item.name,
                item.price,
                dict[item.category]);
            products.Add(product);
        }

        return products;
    }
}

public class ProductData
{
    public int number { get; set; }
    public string name { get; set; }
    public string category { get; set; }
    public decimal price { get; set; }
}

清单34SeedData

但是,当然,我们也有一些代码需要重构。要修改的第一个组件是catalog控制器。

我们将产品列表加载到部分变量中,然后将其作为模型参数传递给View

public class CatalogController : BaseController
{
    public async Task Index()
    {
        var products = await SeedData.GetProducts();
        return View(products);
    }
}

清单35CatalogController

此外,必须在catalog Index视图中修改模型为List类型。

@model List;
@using MVC.Models;
@{
    ViewData["Title"] = "Catalog";
}



清单36Index.cshtml

现在我们必须用带有模型数据的C#表达式替换产品字段:

  • @(product.ImageURL)
  • @product.Name
  • @product.Price.ToString("C")
@model Product;
@using MVC.Models;

@{ 
    var product = Model;
}

清单37Catalog / _ProductCard.cshtml文件

此外,_Categories部分视图将被重构。首先,我们将模型类型更改为List,并将类别变量赋值更改为LINQ查询,该查询仅为我们提供product列表中的不同类别对象。

@model List;

@{
    var products = Model;
    const int PageSize = 4;
    var categories = products.Select(p => p.Category).Distinct();
}
.
.
.
@foreach (var category in categories)
{
    

@category.Name

清单38Catalog/_Categories.cshtml文件

由于我们使用不同的Bootstrap 4 Carousel组件,因此必须通过category id属性(@category.Id)来标识它们。

现在,productsInCategory本地变量包含每个类别中的产品集合,我们将这些产品分组,以便可以适当地填充每个轮播。

应用导航

到目前为止,每个视图仍然是孤立的,并且没有链接将视图相互连接。让我们使用AnchorTagHelper提供导航以生成正确的链接。

虽然HTML标签具有相同的外观,但事实上AnchorTagHelper在服务器端运行,它根据以下属性计算锚点URL

  • asp-controllerMVC控制器名称。省略时,将假定当前控制器。
  • asp-action:路径名称。省略时,将假定默认操作(Index)。
  • asp-route-*:动作参数。每个动作参数必须单独提供。

第一个链接将从catalog视图到购物篮(basket)列表。每次客户选择产品时,必须显示购物车,显示所选项目,并显示一个数量。

我们如何将普通的HTML锚点元素更改为AnchorTagHelper

首先,我们采用当前的锚点元素......

清单39:标记帮助器之前的Anchor元素

并替换href属性添加新asp-controller属性:

清单40:标记助手之后的锚元素

源代码中的这一小变化产生了很大的影响:当ASP.NET Core使用Razor SDK编译视图时,这将注意到asp-controller属性,因此新链接将不再作为HTML锚点元素处理。相反,就像任何其他标记帮助器一样,它现在是一个服务器端组件,它在服务器上运行并呈现实际的HTML链接:

清单41:由服务器端的标记帮助器呈现的锚点元素

现在让我们也应用AnchorTagHelperbasket 控件部分视图:

.
.
.
    

清单42Basket/_BasketControls.cshtml文件

结论

因此结束了文章系列的第一部分。如果你读到了这里,非常感谢你的耐心等待。

我们已经了解了如何使用Visual Studio创建新的ASP.NET Core项目,使用Razor引擎开发基本视图,为视图提供基本模型,并使用锚点标记助手将它们链接在一起。我们将使用相同的项目作为下一篇文章的起点,我们将在其中处理View Components

 

原文地址:https://www.codeproject.com/Articles/3132654/ASP-NET-Core-Road-to-Microservices-Part-01-Buildin

你可能感兴趣的:(ASP.NET,CORE,ASP.NET,CORE)