Asp .Net Core 实现微服务:集成 Ocelot+Nacos+Swagger+Cors实现网关、服务注册、服务发现

什么是 Ocelot ?

Ocelot是一个开源的ASP.NET Core微服务网关,它提供了API网关所需的所有功能,如路由、认证、限流、监控等。

Ocelot是一个简单、灵活且功能强大的API网关,它可以与现有的服务集成,并帮助您保护、监控和扩展您的微服务。

以下是Ocelot的一些主要功能:

  1. 路由管理:Ocelot允许您定义路由规则,将请求路由到正确的微服务。
  2. 认证和授权:Ocelot支持多种认证机制,如JWT、OAuth等,并允许您定义访问控制策略,确保只有授权的用户才能访问特定的API。
  3. 限流和速率限制:Ocelot提供了一些内置的限流和速率限制功能,以确保您的服务不会受到过度的请求压力。
  4. 监控和日志:Ocelot可以收集和显示各种度量指标,帮助您了解您的服务的性能和行为。此外,它还可以将日志记录到各种日志源,以便您进行分析和故障排除。
  5. 集成:Ocelot可以与现有的服务集成,包括Kubernetes、Consul等。
  6. 易于扩展:Ocelot的设计使其易于扩展,您可以编写自己的中间件来处理特定的逻辑,例如修改请求或响应、添加自定义的认证机制等。
  7. 可扩展的配置:Ocelot使用JSON配置文件进行配置,这意味着您可以轻松地根据需要进行配置更改,而无需重新编译代码。

总之,Ocelot是一个功能强大且易于使用的API网关,可以帮助您保护、监控和扩展您的微服务。

官网:Ocelot 23.4 Documentation --- Ocelot Gateway 23.4 documentation

什么是 Nacos ?

Nacos是一个易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它是Dynamic Naming and Configuration Service的首字母简称。Nacos提供了一组简单易用的特性集,包括动态服务发现、服务配置、服务元数据及流量管理等功能,帮助用户快速实现微服务的发现、配置和管理。Nacos还支持多种服务注册方式和服务发现方式,如DNS、RPC、原生SDK和OpenAPI等。

此外,Nacos致力于提供更敏捷和容易的微服务平台构建、交付和管理。它是构建以"服务"为中心的现代应用架构(例如微服务范式、云原生范式)的服务基础设施,能够支持动态DNS服务权重路由和动态DNS服务等特性。

官网:Nacos 快速开始 | Nacos 官网

什么是 Swagger ?

Swagger是一种规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务。它是一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法、参数和模型紧密集成到服务器端的代码,允许API始终保持同步。

此外,Swagger还提供了一个文档工具,可自动生成Web服务的API文档,使开发人员能够更轻松地理解和使用API。它还提供了一个测试工具,可以模拟对Web服务的API请求并验证响应。

什么是 Cors ?

CORS(跨来源资源共享,Cross-Origin Resource Sharing)是一种机制,允许Web应用程序在未经服务器明确许可的情况下,通过浏览器向服务器发送跨域请求。CORS是一种W3C规范,旨在解决Web应用程序中的跨域问题,以促进Web应用程序的安全性和可扩展性。

在Web应用程序中,浏览器会遵循同源策略(Same-Origin Policy),即默认只允许来自同一域的页面之间进行通信。然而,随着Web应用程序的发展,越来越多的应用程序需要与不同域的资源进行交互,例如使用第三方API或进行跨域请求。为了解决这个问题,CORS规范允许服务器通过设置适当的HTTP标头来明确地允许跨域请求。

当浏览器向服务器发送跨域请求时,服务器可以在响应头中包含一个Access-Control-Allow-Origin标头,指定允许哪些源进行跨域请求。浏览器会检查这个标头,如果允许的源与请求的源匹配,则允许跨域请求。此外,CORS规范还定义了其他一些相关的标头,如Access-Control-Allow-MethodsAccess-Control-Allow-Headers等,以进一步控制跨域请求的行为。

通过使用CORS机制,Web应用程序可以更安全、更有效地进行跨域请求,提高了应用程序的可扩展性和用户体验。

Asp .Net Core 集成 Ocelot

要在ASP.NET Core中集成Ocelot,您可以按照以下步骤进行操作:

  1. 安装Ocelot NuGet包:
    在您的ASP.NET Core项目中,打开终端或NuGet包管理器控制台,并运行以下命令来安装Ocelot的NuGet包:

    dotnet add package Ocelot

  2. 添加Ocelot配置文件:

    {
    "Routes": [ //这里注意一下版本(旧版本用ReRoutes)
    {
    "DownstreamPathTemplate": "/api/{controller}", //下游路径模板
    "DownstreamScheme": "http", //下游方案
    //"DownstreamHostAndPorts": [
    // {
    // "Host": "localhost",
    // "Port": "5014"
    // }
    //], //下游主机和端口
    "UpstreamPathTemplate": "/api/product/{controller}", //上游路径模板
    "UpstreamHttpMethod": [], //上游请求方法,可以设置特定的 HTTP 方法列表或设置空列表以允许其中任何方法
    "ServiceName": "api-product-service", //请求服务名称
    "LoadBalancerOptions": {
    "Type": "LeastConnection" //负载均衡算法:目前 Ocelot 有RoundRobin 和LeastConnection算法
    }
    }
    ],
    "GlobalConfiguration": {
    "BaseUrl": "http://localhost:5015", //进行标头查找和替换以及某些管理配置
    "ServiceDiscoveryProvider": {
    "Type": "Nacos"
    }
    },
    "Nacos": {
    "ServerAddresses": [ "http://127.0.0.1:8848" ], //服务地址
    "UserName": "nacos", //用户名
    "Password": "nacos", //密码
    "ServiceName": "api-gateway", //服务名称
    //"Namespace": "", //命名空间
    //"GroupName": "DEFAULT_GROUP" //组名,
    //"ClusterName": "DEFAULT", // 集群名称
    "ListenInterval": 1000, //监听
    "RegisterEnabled": true, // 注册是否启动
    "InstanceEnabled": true //实例是否启动
    },
    "Url": "http://*:5015"
    }

  3. 配置Ocelot服务:

    builder.Services.AddOcelot();

Configure方法中配置请求管道并添加Ocelot中间件:

复制代码
app.UseOcelot().Wait();

网关集成 Nacos

要将Naocs集成Ocelot到中,您可以按照以下步骤进行操作:

  1. 下载Ocelot.Provider.Nacos 源码,导入Ocelot.Provider.Nacos 项目

    github:https://github.com/softlgl/Ocelot.Provider.Nacos

    修改Ocelot.Provider.Nacos 源码,在Ocelot 22版本中 IServiceDiscoveryProvider接口中的Get方法变成了GetAsync

    升级各Nuget包

    复制代码
    using System;
    using System.Linq;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Ocelot.ServiceDiscovery.Providers;
    using Ocelot.Values;
    using Nacos.V2;
    using Microsoft.Extensions.Options;
    using Ocelot.Provider.Nacos.NacosClient.V2;
    using NacosConstants = Nacos.V2.Common.Constants;
    
    namespace Ocelot.Provider.Nacos
    {
        public class Nacos : IServiceDiscoveryProvider
        {
            private readonly INacosNamingService _client;
            private readonly string _serviceName;
            private readonly string _groupName;
            private readonly List<string> _clusters;
    
            public Nacos(string serviceName, INacosNamingService client, IOptions<NacosAspNetOptions> options)
            {
                _serviceName = serviceName;
                _client = client;
                _groupName = string.IsNullOrWhiteSpace(options.Value.GroupName) ? 
                   NacosConstants.DEFAULT_GROUP : options.Value.GroupName;
                _clusters = (string.IsNullOrWhiteSpace(options.Value.ClusterName) ? NacosConstants.DEFAULT_CLUSTER_NAME : options.Value.ClusterName).Split(",").ToList();
            }
    
            public async  Task<List<Service>> GetAsync()
            {
                var services = new List<Service>();
    
                var instances = await _client.GetAllInstances(_serviceName, _groupName, _clusters);
    
                if (instances != null && instances.Any())
                {
                    services.AddRange(instances.Select(i => new Service(i.InstanceId, new ServiceHostAndPort(i.Ip, i.Port), "", "", new List<string>())));
                }
    
                return await Task.FromResult(services);
            }
        }
    }
  2. 配置Ocelot:

    在Ocelot的配置中,您需要指定Nacos作为服务发现和配置的提供者。在Ocelot的配置文件(例如appsettings.json)中,添加以下内容:

    {
    "GlobalConfiguration": {
    "BaseUrl": "http://localhost:5015", //进行标头查找和替换以及某些管理配置
    "ServiceDiscoveryProvider": {
    "Type": "Nacos" //指定Nacos
    }
    },
    "Nacos": {
    "ServerAddresses": [ "http://127.0.0.1:8848" ], //服务地址
    "UserName": "nacos", //用户名
    "Password": "nacos", //密码
    "ServiceName": "api-gateway", //服务名称
    //"Namespace": "", //命名空间
    //"GroupName": "DEFAULT_GROUP" //组名,
    //"ClusterName": "DEFAULT", // 集群名称
    "ListenInterval": 1000, //监听
    "RegisterEnabled": true, // 注册是否启动
    "InstanceEnabled": true //实例是否启动
    }
    }

  3. 启动Ocelot:
    在您的ASP.NET Core应用程序中启动Ocelot。您可以在Startup.cs文件中添加以下代码:

    builder.Services.AddOcelot().AddNacosDiscovery("Nacos");

下游配置 Nacos

  1. 安装必要的NuGet包:

    在Visual Studio中打开你的项目,通过NuGet包管理器安装Nacos.AspNetCore包。可以通过NuGet包管理器控制台运行以下命令来安装:

    Install-Package Nacos.AspNetCore

  2. 配置Nacos客户端:

    appsettings.json文件中添加Nacos服务的配置信息,例如服务器地址、端口、命名空间等信息。示例配置如下:

    {
    "Nacos": {
    "ServerAddresses": [ "http://127.0.0.1:8848" ],
    //命名空间GUID,public默认没有
    //"Namesapce": "",
    "UserName": "nacos",
    "Password": "nacos",
    // 配置中心
    //"Listeners": [
    // {
    // "Group": "dev",
    // "DataId": "api-product-service",
    // "Optional": false
    // }
    //],
    // 服务发现
    "Ip": "localhost", // Nacos 注册时如果没有指定IP,那么就按照本机的IPv4 Address
    "Port": "5014", //端口
    "ServiceName": "api-product-service" // 服务名称
    //"GroupName": "",
    // 权重
    //"Weight": 100
    }
    }

  3. 配置依赖注入:

    如果你需要在你的应用程序中使用Nacos服务,可以在Startup.csConfigureServices方法中注册Nacos服务的依赖注入。示例如下:

    builder.Services.AddNacosAspNet(builder.Configuration,"Nacos");

配置跨域(Cors)

复制代码
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MCode.Common.Extensions.Cors
{
    public static class CorsServiceExtensions
    {
        private readonly static string PolicyName = "MCodeCors";

        /// <summary>
        /// 添加跨域
        /// </summary>
        /// <param name="services">服务集合</param>
        /// <returns></returns>
        public static IServiceCollection AddMCodeCors(this IServiceCollection services)
        {
            if (services == null) throw new ArgumentNullException(nameof(services));
            //origin microsoft.aspnetcore.cors      
            return services.AddCors(options =>
            {
                options.AddPolicy(PolicyName, policy =>
                {
                    policy.SetIsOriginAllowed(_ => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials();
                });
            });
        }
        /// <summary>
        /// 使用跨域
        /// </summary>
        /// <param name="app">应用程序建造者</param>
        /// <returns></returns>
        public static IApplicationBuilder UseMCodeCors(this IApplicationBuilder app)
        {
            return app.UseCors(PolicyName);
        }
    }
}

网关和微服务中配置Swagger

SwaggerOptions

复制代码
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MCode.Common.Extensions.Swagger
{
    /// <summary>
    /// Swagger配置
    /// </summary>
    public class SwaggerOptions
    {
        /// <summary>
        /// 服务名称
        /// </summary>
        public string ServiceName { get; set; }

        /// <summary>
        /// API信息
        /// </summary>
        public OpenApiInfo ApiInfo { get; set; }

        /// <summary>
        /// Xml注释文件
        /// </summary>
        public string[] XmlCommentFiles { get; set; }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="serviceName">服务名称</param>
        /// <param name="apiInfo">API信息</param>
        /// <param name="xmlCommentFiles">Xml注释文件</param>
        public SwaggerOptions(string serviceName, OpenApiInfo apiInfo, string[] xmlCommentFiles = null)
        {
            ServiceName = !string.IsNullOrWhiteSpace(serviceName) ? serviceName : throw new ArgumentException("serviceName parameter not config.");
            ApiInfo = apiInfo;
            XmlCommentFiles = xmlCommentFiles;
        }
    }
}

SwaggerEndPoint

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MCode.Common.Extensions.Swagger
{
    /// <summary>
    /// Swagger终端
    /// </summary>
    public class SwaggerEndPoint
    {
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 地址
        /// </summary>
        public string Url { get; set; }
    }
}

OcelotSwaggerOptions

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MCode.Common.Extensions.Swagger
{
    /// <summary>
    /// 网关Swagger配置
    /// </summary>
    public class OcelotSwaggerOptions
    {
        public List<SwaggerEndPoint> SwaggerEndPoints { get; set; }
    }
}

SwaggerServiceExtensions

复制代码
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

namespace MCode.Common.Extensions.Swagger
{
    /// <summary>
    /// Swagger 服务扩展
    /// </summary>
    public static class SwaggerServiceExtensions
    {
        /// <summary>
        /// 添加 Swagger 服务
        /// </summary>
        /// <param name="services"></param>
        /// <param name="swaggerOptions"></param>
        /// <returns></returns>
        public static IServiceCollection AddMCodeSwagger(this IServiceCollection services, SwaggerOptions swaggerOptions)
        {
            services.AddSingleton(swaggerOptions);

            SwaggerGenServiceCollectionExtensions.AddSwaggerGen(services, c =>
            {
                c.SwaggerDoc(swaggerOptions.ServiceName, swaggerOptions.ApiInfo);

                if (swaggerOptions.XmlCommentFiles != null)
                {
                    foreach (string xmlCommentFile in swaggerOptions.XmlCommentFiles)
                    {
                        string str = Path.Combine(AppContext.BaseDirectory, xmlCommentFile);
                        if (File.Exists(str)) c.IncludeXmlComments(str, true);
                    }
                }

                SwaggerGenOptionsExtensions.CustomSchemaIds(c, x => x.FullName);

                c.AddSecurityDefinition("bearerAuth", new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.Http,
                    Scheme = "bearer",
                    BearerFormat = "JWT",
                    Description = "请输入 bearer 认证"
                });


                c.AddSecurityRequirement(new OpenApiSecurityRequirement
                                              {
                                                  {
                                                      new OpenApiSecurityScheme
                                                      {
                                                          Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "bearerAuth" }
                                                      },
                                                      new string[] {}
                                                  }
                                              });
            });

            return services;
        }

        /// <summary>
        /// 使用 Swagger UI
        /// </summary>
        /// <param name="app"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseMCodeSwagger(this IApplicationBuilder app)
        {
            string serviceName = app.ApplicationServices.GetRequiredService<SwaggerOptions>().ServiceName;

            SwaggerUIBuilderExtensions.UseSwaggerUI(SwaggerBuilderExtensions.UseSwagger(app), c =>
            {
                c.SwaggerEndpoint("/swagger/" + serviceName + "/swagger.json", serviceName);
            });
            return app;
        }


        public static IServiceCollection AddMCodeOcelotSwagger(this IServiceCollection services, OcelotSwaggerOptions ocelotSwaggerOptions)
        {
            services.AddSingleton(ocelotSwaggerOptions);
            SwaggerGenServiceCollectionExtensions.AddSwaggerGen(services);
            return services;
        }

        public static IApplicationBuilder UseMCodeOcelotSwagger(this IApplicationBuilder app)
        {
            OcelotSwaggerOptions ocelotSwaggerOptions = app.ApplicationServices.GetService<OcelotSwaggerOptions>();

            if (ocelotSwaggerOptions == null || ocelotSwaggerOptions.SwaggerEndPoints == null)
            {
                return app;
            }

            SwaggerUIBuilderExtensions.UseSwaggerUI(SwaggerBuilderExtensions.UseSwagger(app), c =>
            {
                foreach (SwaggerEndPoint swaggerEndPoint in ocelotSwaggerOptions.SwaggerEndPoints)
                {
                    c.SwaggerEndpoint(swaggerEndPoint.Url, swaggerEndPoint.Name);
                }
            });
            return app;
        }
    }
}

效果

相关推荐
测试人社区-小明4 小时前
智能测试误报问题的深度解析与应对策略
人工智能·opencv·线性代数·微服务·矩阵·架构·数据挖掘
Tadas-Gao5 小时前
GraphQL:下一代API架构的设计哲学与实践创新
java·分布式·后端·微服务·架构·graphql
小安同学iter7 小时前
天机学堂-优惠券功能-day09(七)
java·spring cloud·微服务·jenkins·优惠券·天机学堂
眠りたいです9 小时前
基于脚手架微服务的视频点播系统-服务端开发部分(补充)文件子服务问题修正
c++·微服务·云原生·架构
是垚不是土10 小时前
基于Blackbox Exporter的网络服务黑盒监控体系实践
网络·数据库·安全·http·微服务·prometheus
Haooog10 小时前
微服务篇面试题(不定时更新)
微服务·面试·架构
2501_9240641110 小时前
2025年微服务全链路性能瓶颈分析平台对比与最佳实践
微服务·云原生·性能优化·架构
mobº10 小时前
K8s 集群部署微服务 - DevOps(二)
微服务·kubernetes·devops
武藤一雄11 小时前
[.NET] 中 System.Collections.Generic命名空间详解
windows·微软·c#·asp.net·.net·.netcore
Su-RE11 小时前
springboo打包--微服务打包
微服务·架构·pycharm