专栏首页程序员宇说如何在 asp.net core 的中间件中返回具体的页面

如何在 asp.net core 的中间件中返回具体的页面

前言

在 asp.net core 中,存在着中间件这一概念,在中间件中,我们可以比过滤器更早的介入到 http 请求管道,从而实现对每一次的 http 请求、响应做切面处理,从而实现一些特殊的功能

在使用中间件时,我们经常实现的是鉴权、请求日志记录、全局异常处理等等这种非业务性的需求,而如果你有在 asp.net core 中使用过 swashbuckle(swagger)、health check、mini profiler 等等这样的组件的话,你会发现,这些第三方的组件往往都提供了页面,允许我们通过可视化的方式完成某些操作或浏览某些数据

因为自己也需要实现类似的功能,虽然使用到的知识点很少、也很简单,但是在网上搜了搜也没有专门介绍这块的文档或文章,所以本篇文章就来说明如何在中间件中返回页面,如果你有类似的需求,希望可以对你有所帮助

Step by Step

最终实现的功能其实很简单,当用户跳转到某个指定的地址后,自定义的中间件通过匹配到该路径,从而返回指定的页面,所以这里主要会涉及到中间件是如何创建,以及如何处理页面中的静态文件引用

因为这块并不会包含很多的代码,所以这里主要是通过分析 Swashbuckle.AspNetCore 的代码,了解它是如何实现的这一功能,从而给我们的功能实现提供一个思路

在 asp.net core 中使用 Swashbuckle.AspNetCore 时,我们通常需要在 Startup 类中针对组件做如下的配置,根据当前程序的信息生成 json 文件 =》 公开生成的 json 文件地址 =》 根据 json 文件生成可视化的交互页面

public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // 生成 swagger 配置的 json 文件
        services.AddSwaggerGen(s =>
        {
            s.SwaggerDoc("v1", new OpenApiInfo
            {
                Contact = new OpenApiContact
                {
                    Name = "Danvic Wang",
                    Url = new Uri("https://yuiter.com"),
                },
                Description = "Template.API - ASP.NET Core 后端接口模板",
                Title = "Template.API",
                Version = "v1"
            });

            // 参数使用驼峰的命名方式
            s.DescribeAllParametersInCamelCase();
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // 公开 swagger 生成的 json 文件节点
        app.UseSwagger();

        // 启用 swagger 可视化交互页面
        app.UseSwaggerUI(s =>
        {
            s.SwaggerEndpoint($"/swagger/v1/swagger.json",
                $"Swagger doc v1");
        });
    }
}

可以看到最终呈现给用户的页面,其实是在 Configure 方法中通过调用 UseSwaggerUI 方法来完成的,这个方法是在 Swashbuckle.AspNetCore.SwaggerUI 这个程序集中,所以这里直接从 github 上找到对应的文件夹,clone 下源代码,来看下是如何实现在中间件中返回特定的页面

在 clone 下的代码中,排除掉一些 c#、node.js 使用到的项目性文件,可以看到整个项目中的文件按照功能可以分为三大块,其中最核心的则是在 SwaggerUIMiddleware 类中,因此,这里主要聚焦在这个中间件类的实现

在一个 asp.net core 中间件中,核心的处理逻辑是在 Invoke/InvokeAsync 方法中,结合我们使用 swagger 时的场景,可以看到,在将组件中所包含的页面呈现给用户时,主要存在如下两个处理逻辑

1、当匹配到用户访问的是 /swagger 时,返回 301 的 http 状态码,浏览器重定向到 /swagger/index.html,从而再次触发该中间件的执行

2、当匹配到请求的地址为 /swagger/index.html 时,将嵌入到程序集中的文件通过 stream 流的形式获取到,转换成字符串,再指定请求的响应的类型为 text/html,从而实现将页面返回给用户

public async Task Invoke(HttpContext httpContext)
{
    var httpMethod = httpContext.Request.Method;
    var path = httpContext.Request.Path.Value;

    // If the RoutePrefix is requested (with or without trailing slash), redirect to index URL
    if (httpMethod == "GET" && Regex.IsMatch(path, $"^/?{Regex.Escape(_options.RoutePrefix)}/?$"))
    {
        // Use relative redirect to support proxy environments
        var relativeRedirectPath = path.EndsWith("/")
            ? "index.html"
            : $"{path.Split('/').Last()}/index.html";

        RespondWithRedirect(httpContext.Response, relativeRedirectPath);
        return;
    }

    if (httpMethod == "GET" && Regex.IsMatch(path, $"^/{Regex.Escape(_options.RoutePrefix)}/?index.html$"))
    {
        await RespondWithIndexHtml(httpContext.Response);
        return;
    }

    await _staticFileMiddleware.Invoke(httpContext);
}

这里需要注意,因为类似于这种功能,我们可能会打包成独立的 nuget 包,然后通过 nuget 进行引用,所以为了能够正确获取到页面及其使用到的静态资源文件,我们需要将这些静态文件的属性修改成嵌入的资源,从而在打包时可以包含在程序集中

对于网页来说,在引用这些静态资源文件时存在一种相对的路径关系,因此,这里在中间件的构造函数中,我们需要将页面需要使用到的静态文件,通过构建 StaticFileMiddleware 中间件,将文件映射与网页相同的 /swagger 路径下面,从而确保页面所需的资源可以正确加载

public class SwaggerUIMiddleware
{
    private const string EmbeddedFileNamespace = "Swashbuckle.AspNetCore.SwaggerUI.node_modules.swagger_ui_dist";

    private readonly SwaggerUIOptions _options;
    private readonly StaticFileMiddleware _staticFileMiddleware;

    public SwaggerUIMiddleware(
        RequestDelegate next,
        IHostingEnvironment hostingEnv,
        ILoggerFactory loggerFactory,
        SwaggerUIOptions options)
    {
        _options = options ?? new SwaggerUIOptions();

        _staticFileMiddleware = CreateStaticFileMiddleware(next, hostingEnv, loggerFactory, options);
    }

    private StaticFileMiddleware CreateStaticFileMiddleware(
        RequestDelegate next,
        IHostingEnvironment hostingEnv,
        ILoggerFactory loggerFactory,
        SwaggerUIOptions options)
    {
        var staticFileOptions = new StaticFileOptions
        {
            RequestPath = string.IsNullOrEmpty(options.RoutePrefix) ? string.Empty : $"/{options.RoutePrefix}",
            FileProvider = new EmbeddedFileProvider(typeof(SwaggerUIMiddleware).GetTypeInfo().Assembly, EmbeddedFileNamespace),
        };

        return new StaticFileMiddleware(next, hostingEnv, Options.Create(staticFileOptions), loggerFactory);
    }
}

当完成了页面的呈现后,因为一般我们会创建一个单独的类库来实现这些功能,在页面中,可能会包含前后端的数据交互,由于我们在宿主的 API 项目中已经完成了对于路由规则的设定,所以这里只需要在类库中通过 nuget 引用 Microsoft.AspNetCore.Mvc.Core ,然后与 Web API 一样的定义 controller,确保这个中间件在宿主程序的调用位于路由匹配规则之后即可

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseAuthorization();

	// Endpoint 路由规则设定
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });

    // 自定义中间件
    app.UseMiddleware<SampleUIMiddleware>();
}

参考

  1. ASP.NET Core 应用针对静态文件请求的处理: 以 web 的形式发布静态文件

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 手摸手 Elastic Stack 使用教程 - 环境安装

    在日常的开发中,会涉及到对一些中间件以及基础组件的使用,日志汇总分析、APM 监控、接口的健康检查、站内搜索,以及对于服务器、nginx、iis 等等的监控,最...

    程序员宇说
  • ASP.NET Core 实战:使用 Docker 容器化部署 ASP.NET Core + MySQL + Nginx

      在之前的文章(ASP.NET Core 实战:Linux 小白的 .NET Core 部署之路)中,我介绍了如何在 Linux 环境中安装 .NET Cor...

    程序员宇说
  • [Vue 牛刀小试]:第四章 - 页面元素样式的设定

      前端开发中有三大件:HTML、CSS、JavaScript,在前面的学习中,不管是学习 Vue 的指令系统还是 Vue 的事件修饰符,主要还是针对的是我们在...

    程序员宇说
  • 5分钟了解swagger

    随着互联网技术的发展,现在的网站架构基本都由原来的后端渲染,变成了:前端渲染、先后端分离的形态,而且前端技术和后端技术在各自的道路上越走越远。

    互扯程序
  • 5分钟了解swagger

    landv
  • Spring Boot中使用Swagger CodeGen生成REST client

    Spring Boot中使用Swagger CodeGen生成REST client

    程序那些事
  • Swagger-UI

    顾翔老师开发的bugreport2script开源了,希望大家多提建议。文件在https://github.com/xianggu625/bug2testscr...

    小老鼠
  • 基于SpringCloud的Microservices架构实战案例-在线API管理

    前端和后端的唯一联系,变成了API接口;API文档变成了前后端开发人员联系的纽带,变得越来越重要,swagger就是一款让你更好的书写API文档的框架。 本实战...

    歪脖贰点零
  • SpringBoot结合swagger2快速生成简单的接口文档

    经我多次尝试application.properties中不加任何swagger配置也可生成文档进行正常测试

    道可道非常道
  • spring-boot-starter-swagger 1.2.0.RELEASE:新增分组配置功能

    简介 该项目主要利用Spring Boot的自动化配置特性来实现快速的将swagger2引入spring boot应用来生成API文档,简化原生使用swagge...

    程序猿DD

扫码关注云+社区

领取腾讯云代金券