将终结点图添加到你的ASP.NET Core应用程序中

在本文中,我将展示如何使用DfaGraphWriter服务在ASP.NET Core 3.0应用程序中可视化你的终结点路由。上面文章我向您演示了如何生成一个有向图(如我上篇文章[译]使用DOT语言和GraphvizOnline来可视化你的ASP.NETCore3.0终结点01中所示),可以使用GraphVizOnline将其可视化。最后,我描述了应用程序生命周期中可以检索图形数据的点。

作者:依乐祝

原文地址:https://www.cnblogs.com/yilezhu/p/13335749.html

译文地址:https://andrewlock.net/adding-an-endpoint-graph-to-your-aspnetcore-application/

在本文中,我仅展示如何创建图形的“默认”样式。在我的下一批那文章中,我再创建一个自定义的writer来生成自定义的图如上篇文章所示。

使用DfaGraphWriter可视化您的终结点

ASP.NET Core附带了一个方便的类DfaGraphWriter可用于可视化ASP.NET Core 3.x应用程序中的终结点路由:

public class DfaGraphWriter
{public void Write(EndpointDataSource dataSource, TextWriter writer);
}

此类只有一个方法WriteEndpointDataSource包含描述您的应用程序的Endpoint集合,TextWriter用于编写DOT语言图(如您在前一篇文章中所见)。

现在,我们将创建一个中间件,该中间件使用DfaGraphWriter将该图编写为HTTP响应。您可以使用DI 将DfaGraphWriterEndpointDataSource注入到构造函数中:

public class GraphEndpointMiddleware
{// inject required services using DIprivate readonly DfaGraphWriter _graphWriter;private readonly EndpointDataSource _endpointData;public GraphEndpointMiddleware(RequestDelegate next,DfaGraphWriter graphWriter,EndpointDataSource endpointData){_graphWriter = graphWriter;_endpointData = endpointData;}public async Task Invoke(HttpContext context){// set the responsecontext.Response.StatusCode = 200;context.Response.ContentType = "text/plain";// Write the response into memoryawait using (var sw = new StringWriter()){// Write the graph_graphWriter.Write(_endpointData, sw);var graph = sw.ToString();// Write the graph to the responseawait context.Response.WriteAsync(graph);}}
}

这个中间件非常简单-我们使用依赖注入将必要的服务注入到中间件中。将图形写入响应有点复杂:您必须在内存中将响应写到一个 StringWriter,再将其转换为 string然后将其写到图形。

这一切都是必要的,因为DfaGraphWriter写入TextWriter使用同步 Stream API调用,如Write,而不是WriteAsync。如果有异步方法,理想情况下,我们将能够执行以下操作:

// Create a stream writer that wraps the body
await using (var sw = new StreamWriter(context.Response.Body))
{// write asynchronously to the streamawait _graphWriter.WriteAsync(_endpointData, sw);
}

如果DfaGraphWriter使用了异步API,则可以如上所述直接写入Response.Body,而避免使用in-memory string。不幸的是,它是同步的,出于性能原因您不应该使用同步调用直接写入Response.Body。如果您尝试使用上面的模式,则可能会得到如下所示内容的InvalidOperationException异常,具体取决于所写图形的大小:

System.InvalidOperationException: Synchronous operations are disallowed. Call WriteAsync or set AllowSynchronousIO to true instead.

如果图形很小,则可能不会出现此异常,但是如果您尝试映射中等规模的应用程序(例如带有Identity的默认Razor Pages应用程序),则可以看到此异常。

让我们回到正轨上-我们现在有了一个图形生成中间件,所以让我们把它添加到管道中。这里有两个选择:

  • 使用终结点路由将其添加为终结点。

  • 从中间件管道中将其添加为简单的“分支”。

通常建议使用前一种方法,将终结点添加到ASP.NET Core 3.0应用程序,因此从这里开始。

将图形可视化器添加为终结点

为了简化终结点注册代码,我将创建一个简单的扩展方法以将GraphEndpointMiddleware作为终结点添加:

public static class GraphEndpointMiddlewareExtensions
{public static IEndpointConventionBuilder MapGraphVisualisation(this IEndpointRouteBuilder endpoints, string pattern){var pipeline = endpoints.CreateApplicationBuilder().UseMiddleware<GraphEndpointMiddleware>().Build();return endpoints.Map(pattern, pipeline).WithDisplayName("Endpoint Graph");}
}

然后,我们可以在Startup.Configure()中的UseEndpoints()方法中调用MapGraphVisualisation("/graph")将图形终结点添加到我们的ASP.NET Core应用程序中:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{app.UseRouting();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapHealthChecks("/healthz");endpoints.MapControllers();// Add the graph endpointendpoints.MapGraphVisualisation("/graph");});
}

这就是我们要做的。该DfaGraphWriter已经在DI中可用,因此不需要额外的配置。导航至http://localhost:5000/graph将以纯文本形式生成我们的终结点图:

digraph DFA {0 [label="/graph/"]1 [label="/healthz/"]2 [label="/api/Values/{...}/ HTTP: GET"]3 [label="/api/Values/{...}/ HTTP: PUT"]4 [label="/api/Values/{...}/ HTTP: DELETE"]5 [label="/api/Values/{...}/ HTTP: *"]6 -> 2 [label="HTTP: GET"]6 -> 3 [label="HTTP: PUT"]6 -> 4 [label="HTTP: DELETE"]6 -> 5 [label="HTTP: *"]6 [label="/api/Values/{...}/"]7 [label="/api/Values/ HTTP: GET"]8 [label="/api/Values/ HTTP: POST"]9 [label="/api/Values/ HTTP: *"]10 -> 6 [label="/*"]10 -> 7 [label="HTTP: GET"]10 -> 8 [label="HTTP: POST"]10 -> 9 [label="HTTP: *"]10 [label="/api/Values/"]11 -> 10 [label="/Values"]11 [label="/api/"]12 -> 0 [label="/graph"]12 -> 1 [label="/healthz"]12 -> 11 [label="/api"]12 [label="/"]
}

我们可以使用GraphVizOnline进行可视化显示如下:

在终结点路由系统中将图形公开为终结点具有如下优点和缺点:

  • 您可以轻松地向终结点添加授权。您可能不希望任何人都能查看此数据!

  • 图形终结点显示为系统中的终结点。这显然是正确的,但可能会很烦人。

如果最后一点对您来说很重要,那么您可以使用传统的方法来创建终结点,即使用分支中间件。

将图形可视化工具添加为中间件分支

在您进行终结点路由之前,将分支添加到中间件管道是创建“终结点”的最简单方法之一。它在ASP.NET Core 3.0中仍然可用,它比终结点路由系统要更为简单,但不能轻松添加授权或高级路由。

要创建中间件分支,请使用Map()命令。例如,您可以使用以下命令添加分支:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{// add the graph endpoint as a branch of the pipelineapp.Map("/graph", branch =>branch.UseMiddleware<GraphEndpointMiddleware>());app.UseRouting();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapHealthChecks("/healthz");endpoints.MapControllers();});
}

使用此方法的优缺点在本质上与终结点路由版本相反:图形中没有/graph终结点,您无法轻松地将授权应用于此终结点!

对我来说,像这样公开应用程序的图形是没有意义的。在下一节中,我将展示如何通过小型集成测试来生成图形。

从集成测试生成终结点图

ASP.NET Core对于运行内存集成测试有很好的设计,它可以在不需要进行网络调用的情况下运行完整的中间件管道和API控制器/Razor页面。

除了可以用来确认应用程序整体正确运行的传统“端到端”集成测试之外,我有时还喜欢编写“健全性检查”测试,以确认应用程序配置正确。您可以使用,在Microsoft.AspNetCore.Mvc.Testing中暴露的底层DI容器中的WebApplicationFactory<>设施实现。这样,您就可以在应用程序的DI上下文中运行代码,而无需通过单元测试。

现在,让我们来试下吧

  • 使用VS或dotnet new xunit来运行一个新的xUnit项目(我选择的测试框架)

  • 通过运行dotnet add package Microsoft.AspNetCore.Mvc.Testing安装Microsoft.AspNetCore.Mvc.Testing

  • 将测试项目的<Project>元素更新为<Project Sdk="Microsoft.NET.Sdk.Web">

  • 从测试项目中引用您的ASP.NET Core项目

现在,我们可以创建一个简单的测试来生成终结点图,并将其写入测试输出。在下面的示例中,我将默认值WebApplicationFactory<>作为类基础设施;如果您需要自定义工厂,请参阅文档以获取详细信息。

除了WebApplicationFactory<>,我还注入了ITestOutputHelper。您需要使用此类来记录xUnit的测试输出。直接写Console不会起作用。。

public class GenerateGraphTest: IClassFixture<WebApplicationFactory<ApiRoutes.Startup>>
{// Inject the factory and the output helperprivate readonly WebApplicationFactory<ApiRoutes.Startup> _factory;private readonly ITestOutputHelper _output;public GenerateGraphTest(WebApplicationFactory<Startup> factory, ITestOutputHelper output){_factory = factory;_output = output;}[Fact]public void GenerateGraph(){// fetch the required services from the root container of the appvar graphWriter = _factory.Services.GetRequiredService<DfaGraphWriter>();var endpointData = _factory.Services.GetRequiredService<EndpointDataSource>();// build the graph as beforeusing (var sw = new StringWriter()){graphWriter.Write(endpointData, sw);var graph = sw.ToString();// write the graph to the test output_output.WriteLine(graph);}}
}

测试的大部分内容与中间件相同,但是我们没有编写响应,而是编写了xUnit的ITestOutputHelper以将记录测试的结果输出。在Visual Studio中,您可以通过以下方式查看此输出:打开“测试资源管理器”,导航到GenerateGraph测试,然后单击“为此结果打开其他输出”,这将以选项卡的形式打开结果:

我发现像这样的简单测试通常足以满足我的目的。在我看来有如下这些优点:

  • 它不会将此数据公开为终结点

  • 对您的应用没有影响

  • 容易产生

不过,也许您想从应用程序中生成此图,但是您不想使用到目前为止显示的任何一种中间件方法将其包括在内。如果是这样,请务必小心在哪里进行。

您无法在IHostedService中生成图形

一般而言,您可以在应用程序中任何使用依赖项注入或有权访问实例的任何位置通过IServiceProvider访问DfaGraphWriterEndpointDataSource服务。这意味着在请求的上下文中(例如从MVC控制器或Razor Page生成)图很容易,并且与您到目前为止所看到的方法相同。

如果您要尝试在应用程序生命周期的早期生成图形,则必须小心。尤其是IHostedService

在ASP.NET Core 3.0中,Web基础结构是在通用主机的基础上重建的,这意味着您的服务器(Kestrel)作为一个IHostedService在你的应用程序中运行的。在大多数情况下,这不会产生太大影响,但是与ASP.NET Core 2.x相比,它改变了应用程序的生成顺序

在ASP.NET Core 2.x中,将发生以下情况:

  • 中间件管道已建立。

  • 服务器(Kestrel)开始侦听请求。

  • IHostedService实现启动。

而是在ASP.NET Core 3.x上,如下所示:

  • IHostedService实现启动。

GenericWebHostService

启动:

  • 中间件管道已建立

  • 服务器(Kestrel)开始侦听请求。

需要注意的重要一点是,直到您的IHostedServices的执行后中间件管道才会建立。由于UseEndpoints()尚未被调用,EndpointDataSource将不包含任何数据!

如果您尝试从一个IHostedService中的DfaGraphWriter生成图表,该EndpointDataSource是空的。

如果尝试使用其他标准机制来注入早期行为,情况也是如此,如IStartupFilterStartup.Configure()执行之前 调用 ,因此EndpointDataSource将为空。

同样,您不能只是在Program.Main调用IHostBuilder.Build()来构建一个Host,然后使用IHost.Services:来访问服务,直到您调用IHost.Run,并且服务器已启动,否则您的终结点列表将为空!

这些限制可能不是问题,具体取决于您要实现的目标。对我来说,单元测试方法可以解决我的大多数问题。

无论使用哪种方法,都只能生成本文中显示的“默认”终结点图。这隐藏了很多真正有用的信息,例如哪些节点生成了终结点。在下一篇文章中,我将展示如何创建自定义图形编写器,以便您可以生成自己的图形。

总结

在这篇文章中,我展示了如何使用DfaGraphWriterEndpointDataSource创建应用程序中所有终结点的图形。我展示了如何创建中间件终结点来公开此数据,以及如何将这种中间件与分支中间件策略一起用作终结点路由。

我还展示了如何使用简单的集成测试来生成图形数据而无需运行您的应用程序。这避免了公开(可能敏感)的终结点图,同时仍然允许轻松访问数据。

最后,我讨论了何时可以在应用程序的生命周期中生成图形。该EndpointDataSource未填充,直到后Server(Kestrel)已经开始,所以你主要限于在请求上下文访问数据。IHostedServiceIStartupFilter执行得太早以至于无法访问数据,IHostBuilder.Build()只是构建DI容器,而没有构建中间件管道。

往期精彩回顾

【推荐】.NET Core开发实战视频课程 ★★★

.NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划

【.NET Core微服务实战-统一身份认证】开篇及目录索引

Redis基本使用及百亿数据量中的使用技巧分享(附视频地址及观看指南)

.NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了

10个小技巧助您写出高性能的ASP.NET Core代码

用abp vNext快速开发Quartz.NET定时任务管理界面

在ASP.NET Core中创建基于Quartz.NET托管服务轻松实现作业调度

现身说法:实际业务出发分析百亿数据量下的多表查询优化

关于C#异步编程你应该了解的几点建议

C#异步编程看这篇就够了

给我好看 
您看此文用  · 秒,转发只需1秒呦~
好看你就点点我

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/308729.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

再见,RabbitMQ,你好,Kafka!

容器、Kubernetes、DevOps、微服务、云原生&#xff0c;这些技术名词的频繁出现&#xff0c;预兆着新的互联网技术时代的到来&#xff0c;大数据高并发将不再遥远&#xff0c;而是大部分项目都必须具备的能力了&#xff0c;而消息队列是必备的了。成熟的消息队列产品很多&#…

[SpringBoot2]数据访问_数据库场景的自动配置分析和整合测试

数据访问 1、导入jdbc场景 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jdbc</artifactId></dependency>数据库驱动&#xff1f; 为什么导入JDBC场景&#xff0c;官方不导入驱动&…

【视频回放与课件】搭上AI快车-在线公益课堂:基于移动应用的人工智能开发...

今天&#xff0c;受广州图书馆邀请&#xff0c;通过腾讯会议推出搭上AI快车系列在线公益讲座第三讲《基于移动应用的人工智能开发》&#xff0c;本次课程实时参与参与直播互动人数超过300人&#xff0c;虽然课程10点钟才正式开始&#xff0c;但是9点已经有许多小伙伴已经进来直…

dotNET:怎样处理程序中的异常(理论篇)?

平时在软件开发的过程中&#xff0c;首先是要保证功能可以正常运行&#xff0c;满足业务需求&#xff0c;除此之外&#xff0c;还需要考虑代码在异常的时候怎么处理&#xff0c;让程序能够健壮地运行。正确合理地处理异常可以减少程序的 Bug、保证代码质量&#xff0c;当然也不…

[Spring5]IOC容器_底层原理

IOC的概念和原理 什么是IOC 1.控制反转&#xff0c;把对象创建和对象之间的调用过程&#xff0c;交给Spring进行管理 2.使用IOC目的&#xff1a;为了耦合度减低 IOC底层原理 xml解析工厂模式反射 底层原理示例图 目的&#xff1a;将耦合度降低到最低限度 IOC过程 1.xml…

使用Azure DevOps Pipeline实现.Net Core程序的CI

上次介绍了Azure Application Insights&#xff0c;实现了.net core程序的监控功能。这次让我们来看看Azure DevOps Pipeline功能。Azure DevOps Pipeline 是Azure DevOps里面的一个组件&#xff0c;对于12个月试用账号同样永久免费。持续集成CI持续集成指的是&#xff0c;频繁…

Blazor带我重玩前端(四)

布局Blazor中的布局和MVC中的布局是类似的。创建布局新建一个Razor页面&#xff0c;所有新增的布局都要继承LayoutComponentBase&#xff0c;同时标识自定义内容的输出位置&#xff0c;即标识Body的位置。如图所示&#xff1a;应用布局我们修改一下index.razor页面中的代码&…

kubernetes+Azure DevOps实现.Net Core项目的自动化部署均衡负载

1. 前言前前后后学习kubernetes也有一个来月了&#xff0c;关于kubernetes的博客也写了有十多篇。但是技术如果无法落地到实际的应用场景终归是纸上谈兵&#xff0c;所以就有了这一出&#xff1a;通过结合kubernetes和azure devops实现项目的CI/CD以及均衡负载写完这篇后kubern…

ASP.NET Core 消息传递:MediatR

MediatR[1] 是参考中介者模式实现的一个 .NET 工具类库&#xff0c;支持在进程内以单播或多播的形式进行消息传递&#xff0c;通过使用 MediatR 可实现消息的发送和处理充分解耦。在介绍 MediatR 之前&#xff0c;先简单了解下中介者模式。中介者模式主要是指定义一个中介对象来…

[Spring5]IOC容器_Bean管理XML方式_注入其他类型属性

xml注入其他属性 bean: package com.atguigu.spring;/*** 演示使用set方法进行注入属性*/ public class Book {private String bname;private String bauthor;private String address;public Book(String address) {this.address address;}public String getBname() {return…

ERP平台的自动化测试技术实践

源宝导读&#xff1a;ERP是“业务密集”的大型复杂软件&#xff0c;而且对于业务逻辑与数据的精确度要求几乎是零容忍&#xff0c;其质量保障的挑战很大。本文将介绍ERP平台通过自动化测试保障质量的技术实践。一、自动化测试概念介绍测试金字塔原理1.1、测试的成本UI自动化依赖…

[Spring5]IOC容器_Bean管理XML方式_注入集合类型属性

xml注入集合属性 1.注入数组类型属性 2.注入List集合类型属性 3.注入Map集合类型属性 &#xff08;1&#xff09;创建类&#xff0c;定义数组&#xff0c;list&#xff0c;map&#xff0c;set类型属性&#xff0c;生成对应set方法 package com.atguigu.collectiontype;imp…

Logging with ElasticSearch, Kibana, ASP.NET Core and Docker

“好久不见&#xff0c;前两周经历了人生第一次"伪牛市"&#xff0c;基金和股市大起大落&#xff0c;更加坚信“你永远赚不到超出你认知范围之外的钱,除非靠着运气”&#xff0c;老韭菜诚不欺我也。当能力与野心不匹配&#xff0c;只能多看书&#xff0c;收割那些不求…

[Spring5]IOC容器_Bean管理_工厂Bean

IOC操作Bean管理&#xff08;FactoryBean&#xff09; 1.Spring有两种类型bean&#xff0c;一种普通bean&#xff0c;另外一种工厂bean&#xff08;FactoryBean&#xff09; 2.普通bean&#xff1a;在配置文件中定义bean类型就是返回类型 3.工厂bean&#xff1a;在配置文件定…

Redis 6.0 新特性 ACL 介绍

Redis 6.0 新特性 ACL 介绍Intro在 Redis 6.0 中引入了 ACL&#xff08;Access Control List) 的支持&#xff0c;在此前的版本中 Redis 中是没有用户的概念的&#xff0c;其实没有办法很好的控制权限&#xff0c;redis 6.0 开始支持用户&#xff0c;可以给每个用户分配不同的权…

[Spring5]IOC容器_Bean管理_bean的作用域和bean的生命周期

IOC操作Bean管理&#xff08;bean作用域&#xff09; 1.在Spring里面&#xff0c;设置创建bean实例是单实例还是多实例 2.在Spring里面&#xff0c;默认情况下&#xff0c;bean是单实例对象 package com.atguigu.spring.test;import com.atguigu.spring.collectiontype.Book…

手动造轮子——为Ocelot集成Nacos注册中心

前言近期在看博客的时候或者在群里看聊天的时候&#xff0c;发现很多都提到了Ocelot网关的问题。我之前也研究过一点&#xff0c;网关本身是一种通用的解决方案&#xff0c;主要的工作就是拦截请求统一处理&#xff0c;比如认证、授权、熔断、限流、注册发现、负载均衡等等。随…

程序员修神之路--简约而不简单的分布式通信基石

点击“蓝字”关注&#xff0c;领取架构书籍菜菜哥&#xff0c;请教一个问题呗面试又被卡住了&#xff1f;还是你了解我呀&#xff0c;tcp协议面向连接是怎么回事呢&#xff1f;这个说详细起来&#xff0c;那本好几百页的tcp协议的书籍你倒是可以看看分布式系统可以总结为是处于…

[Spring5]IOC容器_Bean管理注解方式_创建对象

IOC操心Bean管理&#xff08;基于注解方式&#xff09; 1.什么是注解 &#xff08;1&#xff09;注解是代码特殊标记&#xff0c;格式&#xff1a;注解名称(属性名称属性值,属性名称属性值…) &#xff08;2&#xff09;使用注解&#xff0c;注解作用在类上面&#xff0c;方…

一个有趣的问题, 你知道SqlDataAdapter中的Fill是怎么实现的吗

一&#xff1a;背景1. 讲故事最近因为各方面原因换了一份工作&#xff0c;去了一家主营物联柜的公司&#xff0c;有意思的是物联柜上的终端是用 wpf 写的&#xff0c;代码也算是年久失修&#xff0c;感觉技术债还是蛮重的&#xff0c;前几天在调试一个bug的时候&#xff0c;看到…