ASP.NET Core:如何从中间件返回视图

9

我在开发一个aspnetcore2.0中间件,想要执行某些razor视图。

实际上我需要一个错误处理的中间件,可以显示来自razor视图的漂亮页面。我知道可以使用基于状态码的UseStatusCodePagesWithReExecute来实现。但我需要更通用的方法来处理我的中间件中的异常,有时将其委派给错误视图。

我意识到DeveloperExceptionPageMiddleware执行的内容与我所需的类似。但是即使深入研究其源代码,我仍然无法理解它的工作方式。

这是该中间件返回视图的位置 - https://github.com/aspnet/Diagnostics/blob/dev/src/Microsoft.AspNetCore.Diagnostics/DeveloperExceptionPage/DeveloperExceptionPageMiddleware.cs#L206

但是我不明白这是什么类型的视图。它既不是razor页面(因为它没有@page指令),也不是mvc视图(但我不确定)。

在项目中有两个文件用于该视图:ErrorPage.cshtmlErrorPage.Designer.cs。那个Designer.cs是如何创建的?它看起来像是一个生成的文件。但由于它,项目中有一个正常的类(ErrorPage),可以显式地使用它。它继承自Microsoft.Extensions.RazorViews.BaseView类,来自Microsoft.Extensions.RazorViews.Sources包。

因此,该中间件只需执行该视图:

var errorPage = new ErrorPage(model);
return errorPage.ExecuteAsync(context);

如何在我的项目中实现这一点?

它必须是中间件吗?一个操作或结果过滤器也可以做到。https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/filters - Tseng
@Tseng,主要是因为它更加健壮(中间件可以作为管道的第一个)并且需要处理像404这样的错误(它们不是异常,所以MVC过滤器会忽略它们)。 - Shrike
1个回答

7

更新[2018.06]: 请注意,此文章是针对.NET Core 2.0编写的,而在.NET Core 2.1中,RazorEngine存在重大变化。

事实证明这很容易实现。Aspnet项目有一个名为RazorPageGenerator的内部工具(参见https://github.com/aspnet/Razor/tree/dev/src/RazorPageGenerator),它可以用于编译视图。使用此工具进行编译后,我们将得到可用于中间件的普通类。

但在此之前,我们需要获取RazorPageGenerator并进行轻微的自定义。

1.创建一个新的控制台项目。

dotnet new console -o MyRazorGenerator

2.将NuGet.config放置于此文件夹中。

<configuration>
  <config>
    <add key="globalPackagesFolder" value="./packages" />
  </config>
  <packageSources>
    <add key="aspnetcore-dev" value="https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json " />
  </packageSources>
</configuration>

3.在csprj中添加以下内容(因为dotnet add package不支持安装预发布包)

<ItemGroup>
  <PackageReference Include="RazorPageGenerator" Version="2.1.0-*" />
  <PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.Extensions" Version="2.1.0-*" />
  <PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="2.1.0-*" />
</ItemGroup>

4.恢复dotnet restore以检查您是否已获得RazorPageGenerator。

5.添加到Program.cs

    public static int Main(string[] args)
    {
        if (args == null || args.Length < 1)
        {
            Console.WriteLine("Invalid argument(s).");
            return 1;
        }

        var rootNamespace = args[0];
        var targetProjectDirectory = args.Length > 1 ? args[1] : Directory.GetCurrentDirectory();

        var razorEngine = RazorPageGenerator.Program.CreateRazorEngine(rootNamespace, builder => {
            FunctionsDirective.Register(builder);
            InheritsDirective.Register(builder);
            SectionDirective.Register(builder);
        });
        var results = RazorPageGenerator.Program.MainCore(razorEngine, targetProjectDirectory);

        foreach (var result in results)
        {
            File.WriteAllText(result.FilePath, result.GeneratedCode);
        }

        Console.WriteLine();
        Console.WriteLine($"{results.Count} files successfully generated.");
        Console.WriteLine();
        return 0;
    }

6.现在我们有了自己的生成器,可以编译视图。

7.创建一个 Razor 视图 (.cshtml)。

8.运行我们的生成器来编译视图:

dotnet run --project .\MyRazorPageGenerator\MyRazorPageGenerator.csproj Croc.XFW3.Web .\Middleware

我假设这个视图在 Middleware\Views 文件夹中。

9.生成器会创建一个类似于 ErrorPage.Designer.cs 的文件(如果视图是 ErrorPage.cshtml),我们可以使用它:

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next.Invoke(context);
            if (context.Response.StatusCode == StatusCodes.Status404NotFound)
            {
                var statusCodeFeature  = context.Features.Get<IStatusCodePagesFeature>();
                if (statusCodeFeature == null || !statusCodeFeature.Enabled)
                {
                    if (!context.Response.HasStarted)
                    {
                        var view = new ErrorPage(new ErrorPageModel());
                        await view.ExecuteAsync(context);
                    }
                }
            }
        }
    }

如果出现404错误和缺少StatusCodePagesMiddleware,我们将返回我们的视图。这对嵌入式UI库可能很有用。

生成的代码使用需要添加到您的项目中的工具。为了获取它,我们需要获取nuget包Microsoft.Extensions.RazorViews.Sources。同样,它不在nuget.org上,因此我们需要从https://dotnet.myget.org/feed/aspnetcore-dev/package/nuget/Microsoft.Extensions.RazorViews.Sources安装它。


请参阅我的这篇文章:https://techblog.dorogin.com/handling-errors-in-aspnet-core-middleware-e39872496d51 - Shrike
当您使用@model指令时,似乎无法正常工作。它将其视为文字。我们该如何解决这个问题? - Nabin Karki Thapa
我认为你需要在CreateRazorEngine的回调函数中注册一个额外的指令。我猜测它是ModelDirective(https://github.com/aspnet/Razor/blob/dev/src/Microsoft.AspNetCore.Mvc.Razor.Extensions/ModelDirective.cs)。 - Shrike
是的,谢谢。我成功完成了。 :) - Nabin Karki Thapa
我不理解这个,我从来没有在 asp.net core 项目里看到过 nuget.config 文件,而且 Program.cs 没有任何 BuildWebHost() 方法。你的视图在哪里?我不能使用我的传统视图吗?你有任何官方参考资料作为指导吗? - ibubi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接