I'm new to API gateway and follow the below link to start with.
https://www.c-sharpcorner.com/article/building-api-gateway-using-ocelot-in-asp-net-core/
When I try to run the application it throws an error in Startup.cs file in the following line of code.
Startup.cs
public Startup(IHostingEnvironment env){
var builder = new Microsoft.Extensions.Configuration.ConfigurationBuilder();
builder.SetBasePath(env.ContentRootPath)
.AddJsonFile("configuration.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services){
Action<ConfigurationBuilderCachePart> settings = (x) =>{
x.WithMicrosoftLogging(log =>{
log.AddConsole(LogLevel.Debug);
}).WithDictionaryHandle();
};
//services.AddOcelot(Configuration, settings);
services.AddOcelot(Configuration);
}
public async void Configure(IApplicationBuilder app, IHostingEnvironment env){
await app.UseOcelot(); // Error in this line number
}
}
Error:
Unable to start Ocelot, errors are: When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!,When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!,When not using service discovery DownstreamHostAndPorts must be set and not empty or Ocelot cannot find your service!
configuration.json
{</br>
"ReRoutes": [</br>
{</br>
"DownstreamPathTemplate": "/api/customers",
"DownstreamScheme": "http",
"DownstreamHost": "localhost",
"DownstreamPort": 9001,
"UpstreamPathTemplate": "/customers",
"UpstreamHttpMethod": [ "Get" ]</br>
},</br>
{</br>
"DownstreamPathTemplate": "/api/customers/{id}",
"DownstreamScheme": "http",
"DownstreamHost": "localhost",
"DownstreamPort": 9001,
"UpstreamPathTemplate": "/customers/{id}",
"UpstreamHttpMethod": [ "Get" ]</br>
},</br>
{</br>
"DownstreamPathTemplate": "/api/products",
"DownstreamScheme": "http",
"DownstreamPort": 9002,
"DownstreamHost": "localhost",
"UpstreamPathTemplate": "/api/products",
"UpstreamHttpMethod": [ "Get" ]</br>
}</br>
],</br>
"GlobalConfiguration": {
"RequestIdKey": "OcRequestId",
"AdministrationPath": "/administration"
}</br>
}</br></br>
It seems you've updated to the latest Ocelot package and as a result your configuration.json file is incorrect. Please refer to - http://ocelot.readthedocs.io/en/latest/features/routing.html.
You need to have your DownStreamHost/Port set as:
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 9001
}
],
I have used Ocelot 8.0.1 and there was a bug on that version. Upgraded to 8.0.2, now its working fine.
Related
I have a web API project where I use SignalR. Recently I added and configured Ocelot to be my gateway towards another web API. Ocelot routes the requests correctly but has messed up my SignalR configuration somehow.
Program.cs:
builder.Services.AddOcelot();
var app = builder.Build();
app.UseCors("AllowAllApps");
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.UseAuthentication();
app.UseAuthorization();
app.UseMvc();
app.UseOcelot().Wait();
app.MapHub<MyHub>("/myHub");
app.Run();
Client-side code:
// url = "http://localhost:5001/myHub"
connect(url: string, accessToken: string, debug: boolean): Promise<void> {
this.connection = new signalR.HubConnectionBuilder()
.withUrl(url, { accessTokenFactory: () => accessToken })
.configureLogging(debug ? signalR.LogLevel.Information : signalR.LogLevel.Error)
.withAutomaticReconnect()
.build();
return this.connection.start();
}
Ocelot.json
{
"Routes": [
{
"DownstreamPathTemplate": "/{route}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "myotherapi",
"Port": 80
}
],
"UpstreamPathTemplate": "/myotherapi/{route}",
"UpstreamHttpMethod": [ "Post" ],
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": []
},
"AddHeadersToRequest": {
"UserId": "Claims[sub] > value"
}
}
],
"GlobalConfiguration": {
"BaseUrl": "http://localhost:5001"
}
}
I need Ocelot only for HTTP requests. It shouldn't affect websocket connections with this configuration as far as I understand.
If I comment out the Ocelot lines in my Program.cs SignalR works. If I leave it there my POST request to http://localhost:5001/myHub/negotiate?negotiateVersion=1 fails with 404.
Is it something to do with the order of the middleware that is causing the issue? I have tried multiple different orderings but to no avail.
This is a .NET 6 project with Ocelot version 18.0. Both projects are containerized with Docker.
try adding this
{
"DownstreamPathTemplate": "/myHub/negotiate",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "myotherapi",
"Port": "80"
}
],
"UpstreamPathTemplate": "/myHub/negotiate",
"UpstreamHttpMethod": [ "POST" ]
}
Actually in my Api service i have a method controller like:
[Produces("application/json")]
[ApiController]
[Route("api/v1/[controller]")]
public class LoginController: Controller
{
public LoginController(IConfiguration config)
{
...
}
[Route("authentication"),HttpPost]
public async Task<IActionResult> Authentication(LogRequest request)
{
...
}
}
My configuration:
"ReRoutes": [
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "tresfilos.users.service",
"Port": 7002
}
],
"UpstreamPathTemplate": "/api/{version}/user/{everything}",
"UpstreamHttpMethod": [ "POST", "PUT", "GET" ]
},
],
"GlobalConfiguration": {
"BaseUrl": "https://localhost:7001"
}
I consume in postman like:
http://localhost:7000/api/v1/user/Login/authentication
And i dont know if my definition on configuration file for ocelot is right for POST method.
I define now like:
"Routes": [
{
"DownstreamPathTemplate": "/api/{version}/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "tresfilos.users.service",
"Port": 7002
}
],
"UpstreamPathTemplate": "/api/{version}/User/{everything}"
},
],
"GlobalConfiguration": {
"BaseUrl": "https://localhost:7001"
}
Change ReRoutes to Routes because i use ocelot 16.0.1
And in postman i send the data in Body like json and not like paramaters.
I'm implementing custom Aggregator using Ocelot in ASP.NET it's throwing error in Startup.cs Ocelot middleware. However, both Microservices are working fine and fetching the data independently.
When I'm calling them from my API Gateway it's throwing below error.
Startup.cs
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddOcelot()
.AddSingletonDefinedAggregator<MyAggregator>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public async void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
await app.UseOcelot();
}
}
Here is my ocelot.json file for routes of different Microservices.
ocelot.json
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/user/getUser",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": "44378"
}
],
"UpstreamPathTemplate": "/getUser",
"Key": "User"
},
{
"DownstreamPathTemplate": "/product/getProduct",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": "44357"
}
],
"UpstreamPathTemplate": "/getProduct",
"Key": "Product"
}
],
"Aggregates": [
{
"ReRouteKeys": [
"User",
"Product"
],
"UpstreamPathTemplate": "/UserAndProduct"
}
],
"GlobalConfiguration": {
"BaseUrl": "http://localhost:5000/"
}
}
My custom aggregator class
MyAggregator.cs
public class MyAggregator : IDefinedAggregator
{
public async Task<DownstreamResponse> Aggregate(List<HttpContext> responses)
{
var one = await responses[0].Items.DownstreamResponse().Content.ReadAsStringAsync();
var two = await responses[1].Items.DownstreamResponse().Content.ReadAsStringAsync();
var contentBuilder = new StringBuilder();
contentBuilder.Append(one);
contentBuilder.Append(two);
var stringContent = new StringContent(contentBuilder.ToString())
{
Headers = { ContentType = new MediaTypeHeaderValue("application/json") }
};
return new DownstreamResponse(stringContent, HttpStatusCode.OK, new List<KeyValuePair<string, IEnumerable<string>>>(), "OK");
}
}
You forgot to mention your custom aggregator in your ocelot.json file. Ocelot needs to know your custom aggregator whenever you hit /UserAndProduct.
"Aggregates": [
{
"ReRouteKeys": [
"User",
"Product"
],
"UpstreamPathTemplate": "/UserAndProduct"
}
]
And there is a breaking change in ocelot's latest version. Use the key
Routes instead of ReRoutes. You can use the following json file.
{
"Routes": [
{
"DownstreamPathTemplate": "/user/getUser",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": "44378"
}
],
"UpstreamPathTemplate": "/getUser",
"Key": "User"
},
{
"DownstreamPathTemplate": "/product/getProduct",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": "44357"
}
],
"UpstreamPathTemplate": "/getProduct",
"Key": "Product"
}
],
"Aggregates": [
{
"RouteKeys": [
"User",
"Product"
],
"UpstreamPathTemplate": "/UserAndProduct",
"Aggregator": "MyAggregator"
}
],
"GlobalConfiguration": {
"BaseUrl": "http://localhost:5000/"
}
}
I am new to Ocelot API Gateway and trying to figure out how to perform a response host rewrite?
My ocelot api gateway is hosted on localhost:5000, the downstream server is on another host example.com. I am able to proxy from localhost:5000 to example.com, however, when example.com sends a response I get redirected to example.com. I need to stay within one domain (localhost:5000).
Any help is appreciated
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/example/{all}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "example.com",
"Port": 443
}
],
"UpstreamPathTemplate": "/example/{all}",
}
],
"GlobalConfiguration": {
"BaseUrl": "https://localhost:5000"
}
}
A bit late, but I think you need a "DownstreamHeaderTransform", which will change back example.com to localhost:9000. (not sure if the port will be mapped correctly). In our use case, we use the IP address of the downstream server, and then map that back to the host that the outside world sees. For you, try something along these lines:
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/example/{all}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "example.com",
"Port": 443
}
],
"DownstreamHeaderTransform": {
"Referrer": "https://example.com, https://localhost:5000",
"Host": "example.com, localhost:9000",
"Origin": "https://example.com, https://localhost:5000"
},
"UpstreamPathTemplate": "/example/{all}",
}
],
"GlobalConfiguration": {
"BaseUrl": "https://localhost:5000"
}
}
Personally, I found Ocelot to be rather finiky, and would try Kestrel instead.
I've a healthcheck endpoint in my .NetCore(2.2) WebAPI project for which I don't want to generate and write any logs. I'm writing the logs to WindowsEventLog. For that, I'm using a separate config file just for the logging purpose which I'm loading in my startup.
This is the endpoint that I've in the controller for healthcheck:
[Route("healthcheck")]
[AllowAnonymous]
public IActionResult Get()
{
return Ok("");
}
This is how I'm loading the config file (SeriLogSettings.json) that I created for holding my Serilog configuration:
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddJsonFile("SeriLogSettings.json", optional: false)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
And this is how my SerilogSettings.json looks like:
{
"Serilog": {
"Using": [ "Serilog.Settings.Configuration", "Serilog.Sinks.EventLog" ],
"Filter": [
{
"Name": "ByExcluding",
"Args": {
"expression": "EndsWith(RequestPath, '%/healthcheck')"
}
}
],
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"Enrich": [ "FromLogContext" ],
"WriteTo": [
{
"Name": "EventLog",
"Args": {
"source": "WebAPI",
"logName": "Application",
"managedEventSource": true
}
}
],
"Properties": {
"Application": "ApplicationAPI"
}
}
}
Despite using the ByExcluding expression to exclude the logs for /healthcheck endpoint, I'm getting the logs in the WindowsEventLog.
I'm not able to fix why this is happening and not sure what am I doing wrong?
The problem is the % character in the path you're checking, it should just be /healthcheck.
"expression": "EndsWith(RequestPath, '/healthcheck')"
If you take a look at the documentation on the GitHub page, you'll see they also just mention /SomeEndpoint when testing the request path.
may be simple -
"EndsWith(RequestPath, '/healthcheck')" should become
"StartsWith(RequestPath, '/healthcheck')"
Because your requst may by
/healthcheck/live, /healthcheck/ready
Or more puzzle situation
You did not show packages for your project.
Maybe you are using package Serilog.Expressions instead of package Serilog.Filters.Expressions(deprecated)
Those packages use different formats for string 'expression'
Old
"expression": "EndsWith(RequestPath, '/healthcheck')"
New
"expression": "RequestPath like '/healthcheck%')"
The documentation