{"id":18656449,"url":"https://github.com/manusoft/api-gateway-sample","last_synced_at":"2025-11-05T21:30:20.412Z","repository":{"id":249768856,"uuid":"829953587","full_name":"manusoft/api-gateway-sample","owner":"manusoft","description":"One api project can control multiple apis in asp.net web api. dotnet-8","archived":false,"fork":false,"pushed_at":"2024-07-24T09:26:09.000Z","size":34,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-12-27T14:44:30.538Z","etag":null,"topics":["cachemanager","gateway-api","jwt-authentication","load-balancer","middlewares","ocelot-api-gateway","rate-limit"],"latest_commit_sha":null,"homepage":"https://manusoft.github.io/api-gateway-sample/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/manusoft.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-17T10:16:03.000Z","updated_at":"2024-10-03T05:26:39.000Z","dependencies_parsed_at":"2024-12-27T14:43:23.927Z","dependency_job_id":"b3f7c417-9f8f-4389-b887-9be5f58415e3","html_url":"https://github.com/manusoft/api-gateway-sample","commit_stats":null,"previous_names":["manusoft/api-gateway-sample"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manusoft%2Fapi-gateway-sample","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manusoft%2Fapi-gateway-sample/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manusoft%2Fapi-gateway-sample/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/manusoft%2Fapi-gateway-sample/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/manusoft","download_url":"https://codeload.github.com/manusoft/api-gateway-sample/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239471814,"owners_count":19644382,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cachemanager","gateway-api","jwt-authentication","load-balancer","middlewares","ocelot-api-gateway","rate-limit"],"created_at":"2024-11-07T07:23:36.141Z","updated_at":"2025-11-05T21:30:20.338Z","avatar_url":"https://github.com/manusoft.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# API-GATEWAY-SAMPLE\n\u003cp align=\"left\"\u003e\u003cimg src=\"https://visitor-badge.laobi.icu/badge?page_id=manusoft.api-gateway-sample\" alt=\"visitor\" style=\"max-width: 100%;\"\u003e\u003c/p\u003e\nOne gateway API calls multiple APIs\n\n``` http\nGET https://localhost:7000/\nGET https://localhost:7000/api/user\nGET https://localhost:7000/api/weather\nGET https://localhost:7000/api/aggregate\nPOST https://localhost:7000/api/account/{email}/{password}\nGET https://localhost:7000/api/account/{email}/{password}\n```\n\n## 1. Gateway Project\n\n### Ocelot Setup\n#### Install nuget package Ocelot and Ocelot.Cache.CacheManager\n``` xml\n\u003cItemGroup\u003e\n  \u003cPackageReference Include=\"Ocelot\" Version=\"23.3.3\" /\u003e\n  \u003cPackageReference Include=\"Ocelot.Cache.CacheManager\" Version=\"23.3.3\" /\u003e\n\u003c/ItemGroup\u003e\n```\n\n#### launchSettings.json\n``` json\n...\n\"https\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      \"launchUrl\": \"\",\n      \"applicationUrl\": \"https://localhost:7000;http://localhost:5275\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n...\n```\n\n#### ocelot.json\n``` json\n{\n  \"Routes\": [\n    {\n      \"DownstreamPathTemplate\": \"/api/user\",\n      \"DownstreamScheme\": \"Http\",\n      \"DownstreamHostAndPorts\": [\n        {\n          \"Host\": \"localhost\",\n          \"Port\": 7001\n        },\n        {\n          \"Host\": \"localhost\",\n          \"Port\": 7004\n        },\n        {\n          \"Host\": \"localhost\",\n          \"Port\": 7005\n        }\n      ],\n      \"UpstreamPathTemplate\": \"/api/user\",\n      \"UpstreamHttpMethod\": [ \"GET\", \"POST\", \"PUT\", \"DELETE\" ],\n      \"FileCacheOptions\": {\n        \"TtlSeconds\": 60,\n        \"Region\": \"default\",\n        \"Header\": \"OC-Caching-Control\",\n        \"EnableContentHashing\": false\n      },\n      \"RateLimitOptions\": {\n        \"ClientWhitelist\": [],\n        \"EnableRateLimiting\": true,\n        \"Period\": \"60s\",\n        \"PeriodTimespan\": 6,\n        \"Limit\": 2\n      },\n      \"Key\": \"UserService\",\n      \"LoadBalancerOptions\": {\n        \"Type\": \"LeastConnection\"\n      }\n    },\n    {\n      \"DownstreamPathTemplate\": \"/api/weatherforecast\",\n      \"DownstreamScheme\": \"Http\",\n      \"DownstreamHostAndPorts\": [\n        {\n          \"Host\": \"localhost\",\n          \"Port\": 7002\n        }\n      ],\n      \"UpstreamPathTemplate\": \"/api/weatherforecast\",\n      \"UpstreamHttpMethod\": [ \"GET\", \"POST\", \"PUT\", \"DELETE\" ],\n      \"FileCacheOptions\": {\n        \"TtlSeconds\": 60,\n        \"Region\": \"default\",\n        \"Header\": \"OC-Caching-Control\",\n        \"EnableContentHashing\": false\n      },\n      \"RateLimitOptions\": {\n        \"ClientWhitelist\": [],\n        \"EnableRateLimiting\": true,\n        \"Period\": \"60s\",\n        \"PeriodTimespan\": 6,\n        \"Limit\": 2\n      },\n      \"Key\": \"WeatherService\"\n    },\n    {\n      \"DownstreamPathTemplate\": \"/api/account/{email}/{password}\",\n      \"DownstreamScheme\": \"Http\",\n      \"DownstreamHostAndPorts\": [\n        {\n          \"Host\": \"localhost\",\n          \"Port\": 7003\n        }\n      ],\n      \"UpstreamPathTemplate\": \"/api/account/{email}/{password}\",\n      \"UpstreamHttpMethod\": [ \"GET\", \"POST\", \"PUT\", \"DELETE\" ]\n    }\n  ],\n  \"Aggregates\": [\n    {\n      \"RouteKeys\": [\n        \"UserService\",\n        \"WeatherService\"\n      ],\n      \"UpstreamPathTemplate\": \"/api/aggregate\"\n    }\n  ],\n  \"GlobalConfiguration\": {\n    \"BaseUrl\": \"https://localhost:7000\"\n  }\n}\n```\n\n#### Program.cs\n``` csharp\nusing Gateway.Middlewares;\nusing Ocelot.DependencyInjection;\nusing Ocelot.Middleware;\nusing Ocelot.Cache.CacheManager;\n\nvar builder = WebApplication.CreateBuilder(args);\n\nbuilder.Configuration.AddJsonFile(\"ocelot.json\", optional: false, reloadOnChange: true);\nbuilder.Services.AddOcelot().AddCacheManager(options =\u003e\n{\n    options.WithDictionaryHandle();\n});\nbuilder.Services.AddCors(options =\u003e\n{\n    options.AddDefaultPolicy(policyBuilder =\u003e policyBuilder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());\n});\n\nvar app = builder.Build();\n\napp.UseCors();\napp.UseHttpsRedirection();\napp.UseMiddleware\u003cTokenCheckerMiddleware\u003e();\napp.UseMiddleware\u003cInterceptionMiddleware\u003e();\napp.UseAuthorization();\napp.UseOcelot().Wait();\napp.Run();\n\n```\n\n### Ocelot Cache Manager Configuration\n#### ocelot.json \n``` json\n...\n\"FileCacheOptions\": {\n  \"TtlSeconds\": 60,\n  \"Region\": \"default\",\n  \"Header\": \"OC-Caching-Control\",\n  \"EnableContentHashing\": false\n}\n...\n```\n\n### Ocelot Rate Limit Configuration\n#### ocelot.jso\n``` json\n...\n\"RateLimitOptions\": {\n  \"ClientWhitelist\": [],\n  \"EnableRateLimiting\": true,\n  \"Period\": \"60s\",\n  \"PeriodTimespan\": 6,\n  \"Limit\": 2\n},\n...\n```\n\n### Ocelot Agregate Configuration\n#### ocelot.json\n``` json\n...\n\"Key\": \"UserService\"\n...\n\"Key\": \"WeatherSearvice\"\n...\n\"Aggregates\": [\n  {\n    \"RouteKeys\": [\n      \"UserService\",\n      \"WeatherService\"\n    ],\n    \"UpstreamPathTemplate\": \"/api/aggregate\"\n  }\n],\n...\n```\n### Ocelot Load Balance Configuration\n#### ocelot.json\n``` json\n\"DownstreamHostAndPorts\": [\n...\n    {\n      \"Host\": \"localhost\",\n      \"Port\": 7004\n    },\n    {\n      \"Host\": \"localhost\",\n      \"Port\": 7005\n    }\n  ],\n...\n\"LoadBalancerOptions\": {\n  \"Type\": \"LeastConnection\"\n}\n...\n```\n\n### Middelwares\n#### InterceptionMiddleware.cs\n``` csharp\nnamespace Gateway.Middlewares;\n\npublic class InterceptionMiddleware(RequestDelegate next)\n{\n    public async Task InvokeAsync(HttpContext context)\n    {\n        context.Request.Headers[\"Referrer\"] = \"Api-Gateway\";\n        await next(context);\n    }\n}\n```\n#### TokenCheckerMiddleware.cs\n``` csharp\nnamespace Gateway.Middlewares;\n\npublic class TokenCheckerMiddleware(RequestDelegate next)\n{\n    public async Task InvokeAsync(HttpContext context)\n    {\n        string requestPath = context.Request.Path.Value!;\n\n        if (requestPath.Contains(\"account/login\", StringComparison.InvariantCultureIgnoreCase)\n            || requestPath.Contains(\"account/register\", StringComparison.InvariantCultureIgnoreCase)\n            || requestPath.Equals(\"/\"))\n        {\n            await next(context);\n        }\n        else\n        {\n            var authHeader = context.Request.Headers.Authorization;\n\n            if (authHeader.FirstOrDefault() == null)\n            {\n                context.Response.StatusCode = StatusCodes.Status401Unauthorized;\n                await context.Response.WriteAsync(\"Sorry, Access denied.\");\n            }\n            else\n            {\n                await next(context);\n            }\n\n        }\n    }\n}\n```\n## 2. Shared Project\n### Middleware\n#### RestrictAccessMiddleware.cs\n``` csharp\nusing Microsoft.AspNetCore.Http;\n\nnamespace Shared.Middlewares;\n\npublic class RestrictAccessMiddleware(RequestDelegate next)\n{\n    public async Task InvokeAsync(HttpContext context)\n    {\n        var referrer = context.Request.Headers[\"Referrer\"].FirstOrDefault();\n\n        if (string.IsNullOrEmpty(referrer))\n        {\n            context.Response.StatusCode = StatusCodes.Status403Forbidden;\n            await context.Response.WriteAsync(\"Hmmm, Can't reach this page.\");\n            return;\n        }\n        else\n        {\n            await next(context);\n        }\n    }\n}\n```\n## 3. Identity Project\n#### launchSettings.json\n``` json\n...\n \"http\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      \"launchUrl\": \"swagger\",\n      \"applicationUrl\": \"http://localhost:7001\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n\"https\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      \"launchUrl\": \"swagger\",\n      \"applicationUrl\": \"https://localhost:7242;http://localhost:7001\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n...\n```\n\n#### Program.cs\n``` csharp\n...\napp.UseMiddleware\u003cRestrictAccessMiddleware\u003e();\n...\n```\n\n## 4. Weather Project\n#### launchSettings.json\n``` json\n...\n \"http\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      \"launchUrl\": \"swagger\",\n      \"applicationUrl\": \"http://localhost:7002\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n\"https\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      \"launchUrl\": \"swagger\",\n      \"applicationUrl\": \"https://localhost:7194;http://localhost:7002\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n...\n```\n\n#### Program.cs\n``` csharp\n...\napp.UseMiddleware\u003cRestrictAccessMiddleware\u003e();\n...\n```\n\n## 5. Authetication Project\n#### launchSettings.json\n``` json\n...\n \"http\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      \"launchUrl\": \"swagger\",\n      \"applicationUrl\": \"http://localhost:7003\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n\"https\": {\n      \"commandName\": \"Project\",\n      \"dotnetRunMessages\": true,\n      \"launchBrowser\": true,\n      \"launchUrl\": \"swagger\",\n      \"applicationUrl\": \"https://localhost:7246;http://localhost:7003\",\n      \"environmentVariables\": {\n        \"ASPNETCORE_ENVIRONMENT\": \"Development\"\n      }\n    },\n...\n```\n\n\n#### AppSettings.json\n``` json\n...\n \"Authentication\": {\n   \"Key\": \"AEkp1N6edDWq7ZBhS9QCtus7+emIxDy0PvoPZxrBmRk=\",\n   \"Issuer\": \"http://localhost:7003\",\n   \"Audience\": \"http://localhost:7003\"\n }\n...\n```\n#### Program.cs\n``` csharp\n...\nbuilder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)\n    .AddJwtBearer(options =\u003e\n    {\n        var key = Encoding.UTF8.GetBytes(builder.Configuration.GetSection(\"Authentication:Key\").Value!);\n        string issuer = builder.Configuration.GetSection(\"Authentication:Issuer\").Value!;\n        string audience = builder.Configuration.GetSection(\"Authentication:Audience\").Value!;\n\n        options.RequireHttpsMetadata = false;\n        options.SaveToken = true;\n        options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters\n        {\n            ValidateIssuer = true,\n            ValidateAudience = true,\n            ValidateLifetime = true,\n            ValidateIssuerSigningKey = true,\n            ValidIssuer = issuer,\n            ValidAudience = audience,\n            IssuerSigningKey = new SymmetricSecurityKey(key),\n        };\n    });\n\nbuilder.Services.AddCors(options =\u003e\n{\n    options.AddDefaultPolicy(builder =\u003e\n    {\n        builder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin();\n    });\n});\n...\napp.UseMiddleware\u003cRestrictAccessMiddleware\u003e();\napp.UseCors();\n...\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanusoft%2Fapi-gateway-sample","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmanusoft%2Fapi-gateway-sample","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmanusoft%2Fapi-gateway-sample/lists"}