How define ocelot configuration for POST method controllers net core? - asp.net-core

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.

Related

Why is Ocelot ignoring the first route in my JSON file?

I have implemented Ocelot API Gateway in my Net Core 7 application. One of the JSON files containing routes is set up as follows:
{
"Routes": [
{
"DownstreamPathTemplate": "/storedfiletype",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "<<<HOST DELETED FOR SECURITY>>>",
"Port": 443
}
],
"UpstreamPathTemplate": "/fileserver/storedfiletype",
"UpstreamHttpMethod": [ "Post" ]
},
{
"DownstreamPathTemplate": "/storedfiletype/fileextension",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "<<<HOST DELETED FOR SECURITY>>>",
"Port": 443
}
],
"UpstreamPathTemplate": "/fileserver/storedfiletype/fileextension",
"UpstreamHttpMethod": [ "Post" ]
}
]
}
Now when I try and post to the first route (/storedfiletype in this case), I get a 404 error but the second route works. When I swap them over (making /storedfiletype/fileextension the first route), /storedfiletype works and /storedfiletype/fileextension gives me the 404 error.
Any ideas?

RabbitMQ/Krakend/NestJS - How can I post the consumed message through Krakend API's async agent?

This is my config for krakend.json
"async_agent": [
{
"name": "test-agent",
"connection": {
"max_retries": 10,
"backoff_strategy": "exponential-jitter"
},
"consumer": {
"topic": "krakend",
"workers": 5
},
"backend": [
{
"url_pattern": "/greeted",
"method": "POST",
"host": [ "http://127.0.0.1:2999" ],
"disable_host_sanitize": false
}
],
"extra_config": {
"async/amqp": {
"host": "amqp://guest:guest#localhost:5672/",
"name": "krakend",
"exchange": "ApiGatewayExchange",
"durable": true,
"delete": false,
"exclusive": false,
"no_wait": false,
"auto_ack": false
}
}
}
]
Messages are sent from service-a like so:
export class AppService {
constructor(#Inject('GREETING_SERVICE') private client: ClientProxy){}
getHello(): ResponseDTO {
const responseDTO: ResponseDTO = {
action: 'Hello',
service: 'from service A'
}
this.client.emit('', responseDTO);
return responseDTO;
}
}
And imported GREETING_SERVICE config like so:
imports: [
ClientsModule.register([
{
name: 'GREETING_SERVICE',
transport: Transport.RMQ,
options: {
urls: ['amqp://test:test#localhost:5672/'],
queue: 'krakend'
}
}
])
],
Lastly, this is the endpoint in another service (let's call this service-c) that gets that message from the consumer:
#Post('greeted')
TestHello(#Body() data: any) {
console.log(data)
return data
}
The message is successfully consumed as set by the async_agent in my krakend file, but the message isn't posted as a body to that endpoint. When I did a console.log of that data supposedly passed, it just prints {}.
Am I doing anything wrong here? Been scratching my head for hours.
The async part of your krakend.json configuration looks good to me, but I am suspecting about the problem you might have.
Most of the javascript frameworks today will require you to pass specific headers to work their magic like Content-Type or Accept. You have to take into account that KrakenD will pass a very reduced set of headers to your NestJS application (Accept-Encoding and User-Agent as far as I can remember).
I am unfamiliar with NestJS, but I would bet that you need to pass the Content-Type and you are good to go. Here's my suggestion of configuration:
"async_agent": [
{
"name": "test-agent",
"connection": {
"max_retries": 10,
"backoff_strategy": "exponential-jitter"
},
"consumer": {
"topic": "krakend",
"workers": 5
},
"backend": [
{
"url_pattern": "/greeted",
"method": "POST",
"host": [
"http://127.0.0.1:2999"
],
"disable_host_sanitize": false,
"extra_config": {
"modifier/martian": {
"header.Modifier": {
"scope": [
"request"
],
"name": "Content-Type",
"value": "application/json"
}
}
}
}
],
"extra_config": {
"async/amqp": {
"host": "amqp://guest:guest#localhost:5672/",
"name": "krakend",
"exchange": "ApiGatewayExchange",
"durable": true,
"delete": false,
"exclusive": false,
"no_wait": false,
"auto_ack": false
}
}
}
]
}
Hope this helps

Adding Ocelot to web API breaks SignalR

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" ]
}

Calling Get method through ocelot gives 404 error

In the code, calling the get method through ocelot gives 404 error but the get method that passes id works just fine. But when making API call directly(Bypassing ocelot), both empty and id get method works without any error.
// GET: api/<BillRegisterController>
[HttpGet]
[HttpGet("{id}")]
public IActionResult GetBillRegister(int? id)
{
JsonResponse response = new JsonResponse();
if (id != null)
response = _repo.GetById(id);
else
response = _repo.GetAll();
return GO(response);
}
In ocelot config json
{
"DownstreamPathTemplate": "/api/billregister/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": "39765"
}
],
"UpstreamPathTemplate": "/billregister/{everything}",
"UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE" ],
"Key": "User",
"AuthenticationOptions": {
"AuthenticationProviderKey": "GetwayAuth",
"AllowedScopes": []
}
}
What am I doing wrong?

Ocelot API Gateway Custom Aggregator Issue in ASP.NET Core 3.1

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/"
}
}