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" ]
}
Related
I have setup Serilog with .net 6 asp.net core project but the IP Address is not showing up in the logs using the json formatter.
var logger = new LoggerConfiguration()
.Enrich.WithClientIp()
.Enrich.WithClientAgent()
.Enrich.FromLogContext()
.ReadFrom.Configuration(builder.Configuration)
.CreateLogger();
builder.Logging.ClearProviders();
builder.Logging.AddSerilog(logger);
And I have setup my appsettings like this. But I'm not sure why the IP Address is not showing up in the logs for any request (get, put, etc.)
"Serilog": {
"Using": [ "Serilog.Sinks.File, Serilog.Enrichers.ClientInfo" ],
"MinimumLevel": {
"Default": "Information"
},
"Enrich": [ "FromLogContext", "WithMachineName", "WithProcessId", "WithThreadId", "WithClientIP", "WithClientAgent" ],
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "===> {Timestamp:HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}"
}
},
{
"Name": "File",
"Args": {
"path": "C://Temp//appLog_.json",
"formatter": "Serilog.Formatting.Json.JsonFormatter, Serilog",
"rollOnFileSizeLimit": true,
"fileSizeLimitBytes": 4194304,
"retainedFileCountLimit": 10,
"rollingInterval": "Day"
}
}
]
},
In Program.cs, I change the code into below code:
builder.Host.UseSerilog((ctx, lc) => lc
.WriteTo.Console()
.ReadFrom.Configuration(ctx.Configuration));
...
app.UseSerilogRequestLogging(options =>
{
// Customize the message template
options.MessageTemplate = "{RemoteIpAddress} {RequestScheme} {RequestHost} {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms";
// Emit debug-level events instead of the defaults
options.GetLevel = (httpContext, elapsed, ex) => LogEventLevel.Debug;
// Attach additional properties to the request completion event
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{
diagnosticContext.Set("RequestHost", httpContext.Request.Host.Value);
diagnosticContext.Set("RequestScheme", httpContext.Request.Scheme);
diagnosticContext.Set("RemoteIpAddress", httpContext.Connection.RemoteIpAddress);
};
});
Read net6.0 Serilog example and Adding IP logging to know more.
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'm trying to get Identity server 4 to work in an ASP Net Core 3 application with an Angular 8 SPA using "oidc-client": "1.10.1".
If I add the following to my appsettings.json
"IdentityServer": {
"Key": {
"Type": "File",
"FilePath": "acertificate.pfx",
"Password": "notmyrealpassword..orisit?"
},
"Clients": {
"dev-client": {
"Profile": "IdentityServerSPA",
}
}
}
Using this client:
{
authority: 'https://localhost:5001/',
client_id: 'dev-client',
redirect_uri: 'http://localhost:4200/auth-callback',
post_logout_redirect_uri: 'http://localhost:4200/',
response_type: 'id_token token',
scope: 'openid profile API',
filterProtocolClaims: true,
loadUserInfo: true
}
I get: Invalid redirect_uri: http://localhost:4200/auth-callback
adding.
"dev-client": {
"Profile": "IdentityServerSPA",
"RedirectUris": [ "http://localhost:4200/auth-callback" ]
}
does nothing. If I add the Client config copied (almost) from the documentation
"Clients": [
{
"Enabled": true,
"ClientId": "dev-client",
"ClientName": "Local Development",
"AllowedGrantTypes": [ "implicit" ],
"AllowedScopes": [ "openid", "profile", "API" ],
"RedirectUris": [ "http://localhost:4200/auth-callback" ],
"RequireConsent": false,
"RequireClientSecret": false
}
]
I get: System.InvalidOperationException: 'Type '' is not supported.' at startup
If I try to configure the client in code, and only keep the "Key" section in appsettings
services
.AddIdentityServer(options =>
{
options.Cors.CorsPolicyName = _CorsPolicyName;
})
.AddInMemoryClients(new IdentityServer4.Models.Client[] {
new IdentityServer4.Models.Client
{
ClientId = "dev-client",
ClientName = "JavaScript Client",
ClientUri = "http://localhost:4200",
AllowedGrantTypes = { IdentityModel.OidcConstants.GrantTypes.Implicit },
AllowAccessTokensViaBrowser = true,
RedirectUris = { "http://localhost:4200/auth-callback" },
PostLogoutRedirectUris = { "http://localhost:4200" },
AllowedCorsOrigins = { "http://localhost:4200" },
AllowedScopes =
{
IdentityServer4.IdentityServerConstants.StandardScopes.OpenId,
IdentityServer4.IdentityServerConstants.StandardScopes.Profile,
IdentityServer4.IdentityServerConstants.StandardScopes.Email,
"API"
}
}
})
I get: Unknown client or not enabled: dev-client.
Someone help me keep my sanity and point out my, most likely obvious, error.
ASP.NET Identity overrides the documented method for IdentityServer Clients configuration, expecting a dictionary of well-known values. You can bypass this by creating a section that is not named Clients and reading from that section explicitly. Additionally, AddApiAuthorization exposes the Clients collection on the ApiAuthorizationOptions, which can be used to add other clients:
.AddApiAuthorization<...>(options =>
{
options.Clients.AddRange(Configuration.GetSection("IdentityServer:OtherClients").Get<Client[]>());
});
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.