ASP.NET Core 2.0文档翻译

创建一个 web 应用程序

作者:Rick Anderson 和 Ryan Nowak
翻译:陈广 时间:2018-2-5


Razor页面是ASP.NET Core MVC中的新功能,它在以页面为中心场景中编写代码变得更为简单及高效。 本文提供Razor页面的介绍,并非详细教程,如果你发现某些部分难以理解,请参考:Getting started with Razor Pages

ASP.NET Core 2.0先决条件

安装ASP.NET Core 2.0.0或之后版本。 如果你使用的是Visual Studio,请安装Visual Studio 2017的15.3或之后的版本,并安装以下功能:

  • ASP.NET and web development
  • .NET Core cross-platform development

创建Razor页面项目

  • Visual Studio 参考Getting started with Razor Pages获取如何使用Visual Studio创建一个Razor页面项目的详细信息。
  • Visual Studio for Mac 在命令行运行dotnet new razor,从Visual Studio for Mac打开生成的*.csproj*文件
  • Visual Studio Code 在命令行运行dotnet new razor
  • .NET Core CLI 在命令行运行dotnet new razor ##Razor页面 Razor页面是在Startup.cs中启用的:
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Includes support for Razor Pages and controllers.
        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseMvc();
    }
}

考虑以下基本页面:

@page

<h1>Hello, world!</h1>
<h2>The time on the server is @DateTime.Now</h2>

上面的代码看起来很象Razor视图文件。唯一不同的是@page指令。@page将文件变成一个MVC动作 - 这意味着它直接处理请求,而不需要经过controller(控制器)。@page必须是页面的第一个Razor指令。@page影响着其它Razor的构造行为。

以下两个文件显示了使用了PageModel类的类似页面。 Pages/Index2.cshtml文件:

@page
@using RazorPagesIntro.Pages
@model IndexModel2

<h2>Separate page model</h2>
<p>
    @Model.Message
</p>

"code-behind" 文件Pages/Index2.cshtml.cs

using Microsoft.AspNetCore.Mvc.RazorPages;
using System;

namespace RazorPagesIntro.Pages
{
    public class IndexModel2 : PageModel
    {
        public string Message { get; private set; } = "PageModel in C#";

        public void OnGet()
        {
            Message += $" Server time is { DateTime.Now }";
        }
    }
}

按照惯例,PageModel类文件的名称和Razor页面文件相同,并带.cs后缀。例如之前Razor页面名称为Pages/Index2.cshtmlPageModel类所在文件名称则为Pages/Index2.cshtml.cs

页面所关联的url路径取决于页面在文件系统中的位置。下表显示了Razor页面路径所对应的URL:

文件名及路径 对应的URL
/Pages/Index.cshtml //Index
/Pages/Contact.cshtml /Contact
/Pages/Store/Contact.cshtml /Store/Contact
/Pages/Store/Index.cshtml /Store/Store/Index

注意:

  • 运行时,默认在Pages目录里寻找Razor页面文件。
  • 当URL未包含页面时将Index作为默认页

编写一个基本窗体

Razor页面的特点是在web浏览器中使用公共模板变得更为容易。Model binding(模型绑定), Tag Helpers, 和HTML helpers都在一个Razor页面类工作并定义属性。假设一个页面为Contact模型实现了一个基本的“contact us”窗体: 本文例子中的DbContext在Startup.cs类中初始化

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using RazorPagesContacts.Data;

namespace RazorPagesContacts
{
    public class Startup
    {
        public IHostingEnvironment HostingEnvironment { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<AppDbContext>(options =>
                              options.UseInMemoryDatabase("name"));
            services.AddMvc();
        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseMvc();
        }
    }
}

data model(数据模型):

using System.ComponentModel.DataAnnotations;

namespace RazorPagesContacts.Data
{
    public class Customer
    {
        public int Id { get; set; }

        [Required, StringLength(100)]
        public string Name { get; set; }
    }
}

db context

using Microsoft.EntityFrameworkCore;

namespace RazorPagesContacts.Data
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions options)
            : base(options)
        {
        }

        public DbSet<Customer> Customers { get; set; }
    }
}

Pages/Create.cshtml视图文件:

@page
@model RazorPagesContacts.Pages.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></div>
        <input type="submit" />
    </form>
</body>
</html>

视图的code-behind文件Pages/Create.cshtml.cs

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;

namespace RazorPagesContacts.Pages
{
    public class CreateModel : PageModel
    {
        private readonly AppDbContext _db;

        public CreateModel(AppDbContext db)
        {
            _db = db;
        }

        [BindProperty]
        public Customer Customer { get; set; }

        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _db.Customers.Add(Customer);
            await _db.SaveChangesAsync();
            return RedirectToPage("/Index");
        }
    }
}

根据惯例,PageModel类被<PageName>Model调用,并在页面中处于同一命名空间。

PageModel类允许将页逻辑与其表现分离。它定义了页面操作者,用于处理发送至页面和渲染页面数据的请求。这种分离允许你通过依赖注入独立管理页面和进行单元测试。

引页面拥有一个OnPostAsync处理方法,在Post请求时运行(当用户提交表单)。你可以为任何HTTP动词添加处理方法。最常用的处理有:

  • OnGet 用于初始化页面所需的状态。OnGet示例。
  • OnPost 用于处理表单提交。

Async命名后缀是可选的,但通常由异步函数约定使用。上面例子中的OnPostAsync代码看上去和在一个controller(控制器)中所写的类似。上面的代码是典型的Razor页面。大多数的MVC原语如model binding、validation和动作结果是共享的。

OnPostAsync基本流程: 检查验证错误。

  • 如果没有错误,保存数据并重定向。
  • 如果有错误,再次显示带有验证信息的页面。客户端验证与传统ASP.NET Core MVC应用程序相同。大多数情况下,验证错误将在客户端检测,永远不会提交到服务器。

当数据成功进入,OnPostAsync处理方法调用RedirectToPage helper方法来返回一个RedirectToPageResult实例。RedirectToPage是一个新的action结果,这点和RedirectToActionRedirectToRoute类似,由它是由页面定制的。在上例中,它重定向到根索引页(/Index)。RedirectToPage是会在URL generation for Pages这一节详细描述。

当提交的表单有验证错误时(已经提交至服务器),OnPostAsync处理方法调用page的helper方法。page返回PageResult实例。返回页类似于controllers返回的view中的操作。PageResult是处理方法返回的默认类型。返回void的处理方法则渲染页面。

Customerr属性使用[BindProperty]特性来加入model binding(模型绑定)。

public class CreateModel : PageModel
{
    private readonly AppDbContext _db;

    public CreateModel(AppDbContext db)
    {
        _db = db;
    }

    [BindProperty]
    public Customer Customer { get; set; }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _db.Customers.Add(Customer);
        await _db.SaveChangesAsync();
        return RedirectToPage("/Index");
    }
}

Razor页面默认情况下仅为non-GET动词绑定属性。绑定属性可减少代码数量。可通过使用相同属性来渲染表示域(<input asp-for="Customer.Name" />)并接收输入。

home页面(Index.cshtml):

@page
@model RazorPagesContacts.Pages.IndexModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<h1>Contacts</h1>
<form method="post">
    <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var contact in Model.Customers)
            {
                <tr>
                    <td>@contact.Id</td>
                    <td>@contact.Name</td>
                    <td>
                        <a asp-page="./Edit" asp-route-id="@contact.Id">edit</a>
                        <button type="submit" asp-page-handler="delete" 
                                asp-route-id="@contact.Id">delete</button>
                    </td>
                </tr>
            }
        </tbody>
    </table>

    <a asp-page="./Create">Create</a>
</form>

code behind文件index.cshtml.cs文件:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;

namespace RazorPagesContacts.Pages
{
    public class IndexModel : PageModel
    {
        private readonly AppDbContext _db;

        public IndexModel(AppDbContext db)
        {
            _db = db;
        }

        public IList<Customer> Customers { get; private set; }

        public async Task OnGetAsync()
        {
            Customers = await _db.Customers.AsNoTracking().ToListAsync();
        }

        public async Task<IActionResult> OnPostDeleteAsync(int id)
        {
            var contact = await _db.Customers.FindAsync(id);

            if (contact != null)
            {
                _db.Customers.Remove(contact);
                await _db.SaveChangesAsync();
            }

            return RedirectToPage();
        }
    }
}

index.cshtml文件包含以下标记,为每个联系人创建一个edit链接:

<a asp-page="./Edit" asp-route-id="@contact.Id">edit</a>

Anchor Tag Helper使用asp-route-{value}属性来生成到Edit页面的链接。这个链接包含带有联系人ID的路由数据。例如,http://localhost:5000/Edit/1

*Pages/Edit.cshtml/*文件:

@page "{id:int}"
@model RazorPagesContacts.Pages.EditModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

@{
    ViewData["Title"] = "Edit Customer";
}

<h1>Edit Customer - @Model.Customer.Id</h1>
<form method="post">
    <div asp-validation-summary="All"></div>
    <input asp-for="Customer.Id" type="hidden" />
    <div>
        <label asp-for="Customer.Name"></label>
        <div>
            <input asp-for="Customer.Name" />
            <span asp-validation-for="Customer.Name" ></span>
        </div>
    </div>
 
    <div>
        <button type="submit">Save</button>
    </div>
</form>

第一行的@page "{id:int}"指令中,路由约束{id:int}告诉页面只能接收包含int路由数据的请求。如果一个到页面的请求不包含可以转换为int的路由数据,运行时会返回一个HTTP 404(未找到)错误。

Pages/Edit.cshtml.cs文件:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using RazorPagesContacts.Data;

namespace RazorPagesContacts.Pages
{
    public class EditModel : PageModel
    {
        private readonly AppDbContext _db;

        public EditModel(AppDbContext db)
        {
            _db = db;
        }

        [BindProperty]
        public Customer Customer { get; set; }

        public async Task<IActionResult> OnGetAsync(int id)
        {
            Customer = await _db.Customers.FindAsync(id);

            if (Customer == null)
            {
                return RedirectToPage("/Index");
            }

            return Page();
        }

        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _db.Attach(Customer).State = EntityState.Modified;

            try
            {
                await _db.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                throw new Exception($"Customer {Customer.Id} not found!");
            }

            return RedirectToPage("/Index");
        }
    }
}

Index.cshtml文件还包含为每个客户联系创建一个删除按钮的标记:

<button type="submit" asp-page-handler="delete" 
        asp-route-id="@contact.Id">delete</button>

当删除按钮在HTML中渲染时,它的格式包含以下参数:

  • asp-route-id属性指定客户联系ID。
  • asp-page-handler属性指定handler。 下例为联系ID为1的客户呈现一个删除按钮:
<button type="submit" formaction="/?id=1&amp;handler=delete">delete</button>

当点击按钮,表单POST请求被发送至服务器。依照惯例,处理程序的名字是依照OnPost[handler]Async结构中的handler参数值进行选择的。

因为本例中的handlerdeleteOnPostDeleteAsync处理方法用于处理POST请求。如果asp-page-handler设置为不同的值,比如remove,将会选择名字为OnPostRemoveAsync的页面处理方法。

public async Task<IActionResult> OnPostDeleteAsync(int id)
{
    var contact = await _db.Customers.FindAsync(id);

    if (contact != null)
    {
        _db.Customers.Remove(contact);
        await _db.SaveChangesAsync();
    }

    return RedirectToPage();
}

OnPostDeleteAsync方法:

  • 从查询字符串接收id
  • 使用FindAsync为在数据库查询客户联系人。
  • 如果找到客户联系人,则从客户联系人列表中删除他们。数据库更新。
  • 调用RedirectToPage来重定向到根索引页(/Index)。

XSRF/CSRF和Razor页面

你无需为防伪验证(antiforgery validation)编写任何代码,防伪令牌的产生和验证自动包含在Razor页面中。

在Razor页面中使用Layouts、partials、templates和Tag Helpers

页面使用Razor视图引擎的所有功能。Layouts、partials、templates、Tag Helpers、_ViewStart.cshtml、_ViewImports.cshtml和传统Razor页面具有相同的工作方式。

让我们利用其中的一些特性来整理这个页面。 在Pages/_Layout.cshtml中添加一个layout(布局)页:

<!DOCTYPE html>
<html>
<head> 
    <title>Razor Pages Sample</title>      
</head>
<body>    
   <a asp-page="/Index">Home</a>
    @RenderBody()  
    <a asp-page="/Customers/Create">Create</a> <br />
</body>
</html>

Layout:

  • 控制每个页面的布局
  • 导入HTML结构,如JavaScript和sytlesheets。 参考layout page获取更多信息。

Layout属性在Pages/_ViewStart.cshtml中设置:

@{
    Layout = "_Layout";
}

注意:layout(布局)在Pages文件夹中。页面从当前页的同一文件夹开始按层次查找其它视图(layouts,templates,partials),一个Pages目录下的layout可在此目录下的任意Raxor页面中使用。

我们推荐不要将layout文件放入Views/Shared文件夹。Views/Shared是一种MVC视图模板。Razor页面意味着目录层次,而不是路径约定。

视图从Pages目录所包含的Razor页面搜索而来,MVC和传统Razor视图所使用的layouts、templates、partials就可以工作了。

添加一个Pages/_ViewImportscshtml文件:

@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

@namespace在稍后解释。@addTagHelper指令将内置的Tag Helpers引入到Pages目录中的所有页面。

@namespace指令在页面中被显示使用:

@page
@namespace RazorPagesIntro.Pages.Customers

@model NameSpaceModel

<h2>Name space</h2>
<p>
    @Model.Message
</p>

该指令设置页面的命名空间。@model指令不需要包含命名空间。

@namespace指令包含在*_ViewImports.cshtml时,它所指定的命名空间会为页面生成命名空间前缀。生成的命名空间后缀部分是包含_ViewImports.cshtml*的目录和包含页面的目录之间的点分隔相对路径。

例如,code behind文件Pages/Customers/Edit.cshtml.cs显式地设置命名空间:

namespace RazorPagesContacts.Pages
{
    public class EditModel : PageModel
    {
        private readonly AppDbContext _db;

        public EditModel(AppDbContext db)
        {
            _db = db;
        }

        // Code removed for brevity.

Pages/_ViewImports.cshtml文件设置如下命名空间:

@namespace RazorPagesContacts.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

为Razor页面Pages/Customers/Edit.cshtml生成的命名空间和code behind文件一样。@namespace指令被设计为如C#类一样加入项目,并且在没有为code behind文件添加一个@using指令时页面生成代码也可以工作。

注意@namespace也可以工作于传统Razor视图。

原本的Pages/Create.cshtml视图文件:

@page
@model RazorPagesContacts.Pages.CreateModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></div>
        <input type="submit" />
    </form>
</body>
</html>

更新后的Pages/Create.cshtml视图文件:

@page
@model CreateModel

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></div>
        <input type="submit" />
    </form>
</body>
</html>

Razor Pages starter project包含的Pages/_ValidationScriptsPartial.cshtml文件用于连接客户端验证。

为页面生成URL

之前的Create页面使用RedirectToPage

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    _db.Customers.Add(Customer);
    await _db.SaveChangesAsync();
    return RedirectToPage("/Index");
}

应用程序的文件/目录结构如下:

  • /Pages
    • Index.cshtml
    • /Customer
      • Create.cshtml
      • Edit.cshtml
      • Index.cshtml

Pages/Customers/Crearte.cshtmlPages/Customers/Edit.cshtml页面在成功后重定向到Pages/Index.cshtml。字符串/Index可被用于生成到Pages/Index.cshtml页面的URL。例如:

  • Url.Page("/Index", ...)
  • <a asp-page="/Index">My Index Page</a>
  • RedirectToPage("/Index") 页面名称是相对于/Pages根目录的路径(包含一个引导符/,如/Index)。上述的URL生成示例比仅仅硬编码URL要丰富得多。URL生成使用路由,可以根据在目标路径中定义路由的方式生成和编码参数。

页的URL生成支持相对名称。下表展示了在Pages/Customers/Create.cshtml中使用不同的RedirerctToPage参数时,选中的是哪个Index页面。

RedirectToPage(x) Page
RedirectToPage("/Index") Pages/Index
RedirectToPage("./Index") Pages/Customers/Index
RedirectToPage("../Index") Pages/Index
RedirectToPage("Index") Pages/Customers/Index

RedirectToPage("Index")RedirectToPage("./Index")RedirectToPage("../Index")是相对名称。RedirectToPage的参数结合当前页面路径来计算目标页面的名称。

当建一个复杂结构的站点时,相对名称链接非常有用。如果使用相对名称一个文件夹中的页之间链接,当重命名该文件夹时,所有链接仍然有效(因为它们没有包括文件夹名称)。

TempData

ASP.NET Core在controller(控制器)中曝露了TempData属性。此属性在读取数据之前存储数据。KeepPeek方法可用于在数据不被删除的情况下进行检查。当不止一个请求需要数据时,Tempdata对于重定向非常有用。

[TempData]是ASP.NET Core 2.0的新特性,它支持在Controllers和pages中使用。

以下代码使用TempData设置Message的值:

public class CreateDotModel : PageModel
{
    private readonly AppDbContext _db;

    public CreateDotModel(AppDbContext db)
    {
        _db = db;
    }

    [TempData]
    public string Message { get; set; }

    [BindProperty]
    public Customer Customer { get; set; }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        _db.Customers.Add(Customer);
        await _db.SaveChangesAsync();
        Message = $"Customer {Customer.Name} added";
        return RedirectToPage("./Index");
    }
}

Pages/Customers/Index.cshtml文件下的以下标记使用TempData来显示Message的值。

<h3>Msg: @Model.Message</h3>

code-behind文件Pages/Customers/Index.cshtml.cs中给Message属性赋予了[TempData]特性。

[TempData]
public string Message { get; set; }

参数TempData获取更多信息。

每页多个处理程序

下面的页面使用Tag Helper(标签助手)为两个页面处理程序生成标记:

@page
@model CreateFATHModel

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></div>
        <input type="submit" asp-page-handler="JoinList" value="Join" />
        <input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
    </form>
</body>
</html>

之前的例子中的表单有两个提交按钮,每个都使用FormActionTagHelper来提交至不同的URL。asp-page-handler属性与asp-page相匹配。asp-page-handler生成提交至每一个由页面定义的处理方法的URL。没有指定asp-page,因为示例链接至当前页。

code-behind文件:

using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesContacts.Data;

namespace RazorPagesContacts.Pages.Customers
{
    public class CreateFATHModel : PageModel
    {
        private readonly AppDbContext _db;

        public CreateFATHModel(AppDbContext db)
        {
            _db = db;
        }

        [BindProperty]
        public Customer Customer { get; set; }

        public async Task<IActionResult> OnPostJoinListAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _db.Customers.Add(Customer);
            await _db.SaveChangesAsync();
            return RedirectToPage("/Index");
        }

        public async Task<IActionResult> OnPostJoinListUCAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }
            Customer.Name = Customer.Name?.ToUpper();
            return await OnPostJoinListAsync();
        }
    }
}

上述代码使用了named handler methods(命名处理方法)。命名处理方法的创建规则是将处理名称放至On<HTTP动词>之后,Async之前(如果存在)。上例中的页面方法为OnPostJoinListAsync和OnPostJoinListUCAsync。将OnPostAsync移除,处理名称为JoinListJoinListUC

<input type="submit" asp-page-handler="JoinList" value="Join" />
<input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />

上述代码提交至OnPostJoinListAsync的URL路径为http://localhost:5000/Customers/CreateFATH?handler=JoinList。提交至OnPostJoinListUCAsync的URL路径为http://localhost:5000/Customers/CreateFATH?handler=JoinListUC

定制路由

如果你不喜欢在URL中查询字符串?handler=JoinList,可以改变路由,将处理名称放至URL路径部分。你可以通过在@Page指令之后添加包含在双引号中的路由模板来定制路由。

@page "{handler?}"
@model CreateRouteModel

<html>
<body>
    <p>
        Enter your name.
    </p>
    <div asp-validation-summary="All"></div>
    <form method="POST">
        <div>Name: <input asp-for="Customer.Name" /></div>
        <input type="submit" asp-page-handler="JoinList" value="Join" />
        <input type="submit" asp-page-handler="JoinListUC" value="JOIN UC" />
    </form>
</body>
</html>

上述路由将处理名称放置在URL路径而不是查询字符串中。跟随在handler后面的?号意味着路由参数是可选的。

你可以使用@page向页面路由添加其它段和参数,不管是什么,都会附加到页面的默认路由中。不支持使用绝对路径或虚拟路径来更改页面的路由(例如“~/Some/Other/PATH”)。

配置和设置

如果要配置高级选项,请在MVC构建器上使用扩展方法AddRazorPagesOptions

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc()
        .AddRazorPagesOptions(options =>
        {
            options.RootDirectory = "/MyPages";
            options.Conventions.AuthorizeFolder("/MyPages/Admin");
        });
}

现在,你可以使用RazorPagesOPtions来为页面设置路由根目录,或为页面添加应用程序模型约定了。将来我们会以这种方式启用更多的扩展性。

预编译视图,请参考Razor View compilation 下载视图示例代码

将Razor页面指定到内容根中

默认情况下,Razor页面位于*/Pages*目录中。通过在AddMVC中添加WithRazorPagesAtContentRoot来将你的Razor页面指定到应用程序的内容根(content root ContentRootPath)中。

services.AddMvc()
    .AddRazorPagesOptions(options =>
    {
        ...
    })
    .WithRazorPagesAtContentRoot();

将Razor页面指定到自定义根目录中

在AddMvc中添加WithRazorPagesRoot可以将你的Razor页面指定到应用程序的自定义根目录中(提供相对路径):

services.AddMvc()
    .AddRazorPagesOptions(options =>
    {
        ...
    })
    .WithRazorPagesRoot("/path/to/razor/pages");
;

© 2018 - IOT小分队文章发布系统 v0.3