I am using ASP.NET Microservices plus a separate Ocelot API Gateway which serves for authentication/ authorization and rerouting.
This is my config file:
{
"Routes": [
// *** User API ***
{
"UpstreamPathTemplate": "/User/{id}",
"UpstreamHttpMethod": [ "Get"],
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 7268
}
],
"DownstreamPathTemplate": "/api/User/{id}",
"DownstreamHttpMethod": "Get",
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": [
"Cart.API",
"Catalogue.API",
"Identity.API",
"offline_access"
]
},
"RouteClaimsRequirement": {
"Administrator": "true"
}
},
{
"UpstreamPathTemplate": "/User/{id}",
"UpstreamHttpMethod": [ "Put" ],
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 7268
}
],
"DownstreamPathTemplate": "/api/User/{id}",
"DownstreamHttpMethod": "Put",
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": [
"Cart.API",
"Catalogue.API",
"Identity.API",
"offline_access"
]
},
"RouteClaimsRequirement": {
"Administrator": "true"
}
},
{
"UpstreamPathTemplate": "/User/{id}",
"UpstreamHttpMethod": [ "Delete" ],
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 7268
}
],
"DownstreamPathTemplate": "/api/User/{id}",
"DownstreamHttpMethod": "Delete",
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": [
"Cart.API",
"Catalogue.API",
"Identity.API",
"offline_access"
]
},
"RouteClaimsRequirement": {
"Administrator": "true"
}
},
//edit,get logged user
{
"UpstreamPathTemplate": "/User/LoggedUser",
"UpstreamHttpMethod": [ "Put" ],
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 7268
}
],
"DownstreamPathTemplate": "/api/User/LoggedUser",
"DownstreamHttpMethod": "Put",
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": [
"Cart.API",
"Catalogue.API",
"Identity.API",
"offline_access"
]
}
},
{
"UpstreamPathTemplate": "/User/LoggedUser",
"UpstreamHttpMethod": [ "Get" ],
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 7268
}
],
"DownstreamPathTemplate": "/api/User/LoggedUser",
"DownstreamHttpMethod": "Get",
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": [
"Cart.API",
"Catalogue.API",
"Identity.API",
"offline_access"
]
}
}
//edit,get logged user
],
"GlobalConfiguration": {
"BaseUrl": "https://localhost:7193"
}
}
Now here is the problem.
I have the endpoint [GET] /User/LoggedUser (at the bottom) and it is configured without any authorization and it calls /api/User/LoggedUser.
I also have the endpoint [GET] /User/{id} and it is configured WITH authorization and it calls /api/User/{id}
If I use the current configuration as a whole and try calling [GET] /User/LoggedUser with authenticated user, but without the claim Administrator : true -> ** it does not work**
If I delete the rerouting for [GET] /User/{id}, which as you can see, is a totally separate endpoint rerouting - the above problem disappears.
It is as if Ocelot treats these 2 endpoints as one. But I do not understand why.
And here is the console log:
The problem is only with these 2 endpoints. If I call [PUT] /User/LoggedUser it does not work until I delete [PUT] /User/{id} .No other problems were detected. At least for now.
Update 1
So I renamed my API endpoints from /api/User/LoggedUser to be /LoggedUser (downstream path), changed my upstream path to /Test/LoggedUser and it worked.
So, clearly it is an url conflict. How should I handle such problem?
Update 2
Again, I changed the endpoints to the following:
{
"UpstreamPathTemplate": "/Get/LoggedUser",
"UpstreamHttpMethod": [ "Get" ],
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 7268
}
],
"DownstreamPathTemplate": "/api/User/Get/LoggedUser",
"DownstreamHttpMethod": "Get",
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": [
"Cart.API",
"Catalogue.API",
"Identity.API",
"offline_access"
]
}
},
{
"UpstreamPathTemplate": "/Edit/LoggedUser",
"UpstreamHttpMethod": [ "Put" ],
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 7268
}
],
"DownstreamPathTemplate": "/api/User/Edit/LoggedUser",
"DownstreamHttpMethod": "Put",
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": [
"Cart.API",
"Catalogue.API",
"Identity.API",
"offline_access"
]
}
}
And still, there is conflict.
Update 3
I have other endpoints which are overlapping to some extend. For example:
{
"UpstreamPathTemplate": "/User/Login",
"UpstreamHttpMethod": [ "Post" ],
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 7268
}
],
"DownstreamPathTemplate": "/api/User/Login",
"DownstreamHttpMethod": "Post"
},
{
"UpstreamPathTemplate": "/User/Register",
"UpstreamHttpMethod": [ "Post" ],
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 7268
}
],
"DownstreamPathTemplate": "/api/User/Register",
"DownstreamHttpMethod": "Post"
}
Yet, no problem with these endpoints. They are both POST, they both start with api/User and /User. I do not see what is the problem with the other endpoints.
I think the problem should be fixed from the routing configuration of the backend api, rather than the ocelot configuration. You should configure the default routes order (precedence) from specific to generic. In the above case /api/User/LoggedUser should be matched before /api/User/{id}. Take a look at this article for some more info about routing precedence in asp.net mvc and web.api.
Related
I have ASP.NET Web API microservices and currently I am implementing API gateway for routing and authentication.
From my 3 APIs, one of them is for User (crud) operations, so I decided to use it for a gateway, as well. And while I was implementing the routing I came across a strange problem.
With this (test) configuration (again, using my User API running on port 7268)
{
"Routes": [
//User API
{
"UpstreamPathTemplate": "/User/Login",
"UpstreamHttpMethod": [ "Post" ],
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 7124
}
],
"DownstreamPathTemplate": "/api/Product",
"DownstreamHttpMethod": "Get"
}
],
"GlobalConfiguration": {
"BaseUrl": "https://localhost:7268"
}
}
... I am able to get list of products from the Product API on port 7124, when I navigate to /User/Login.
Now if I change the configuration to access any endpoint of the same User API (which matches the BaseUrl path), it does not work. Example:
{
"Routes": [
//User API
{
"UpstreamPathTemplate": "/User/Login",
"UpstreamHttpMethod": [ "Post" ],
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 7268
}
],
"DownstreamPathTemplate": "/api/User/Login",
"DownstreamHttpMethod": "Post"
}
],
"GlobalConfiguration": {
"BaseUrl": "https://localhost:7268"
}
}
It gives me 404 Not Found and the following message in the console:
Is this because I am using an existing API to re-route itself and it is expected behavior, or I am doing something wrong?
I need to integrate .net core gateway with rabbit mq.
Iam using angular 10 as client.
The architecture is=> Client(Angular 10) -> Gateway(Ocelot) -> Rabbitmq with easy netq -> .Net core Console app.
.Net core gateway route structure is given below
{
"Routes": [
{
"DownstreamPathTemplate": "/api/product",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 44337
}
],
"UpstreamPathTemplate": "/gateway/product",
"UpstreamHttpMethod": [ "POST", "PUT", "GET" ]
},
{
"DownstreamPathTemplate": "/api/product/{id}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 44337
}
],
"UpstreamPathTemplate": "/gateway/product/{id}",
"UpstreamHttpMethod": [ "GET", "DELETE" ]
}
],
"GlobalConfiguration": {
"BaseUrl": "http://localhost:44382"
}
}
We know that rabbit mq pattern is messaging with publish and subscribe method.
My question is do i need to add the code of consumer(subscribe) section of rabbit mq in gatway api and publish code in console app?
I want to send slack notifications to a channel as soon as any log with loglevel ERROR appears in my index. I have configured watcher in the following way but it is giving me errors. The slack message must have the log message.
I am not able to configure this exactly.
{
"trigger": {
"schedule": {
"interval": "10s"
}
},
"input": {
"search": {
"request": {
"search_type": "query_then_fetch",
"indices": [
"index-log*",
"index-beat*"
],
"rest_total_hits_as_int": true,
"body": {
"query": {
"match": {
"loglevel": "ERROR"
},
"range": {
"#timestamp": {
"from": "{{ctx.trigger.scheduled_time}}||-5m",
"to": "{{ctx.trigger.triggered_time}}"
}
}
}
}
}
}
},
"condition": {
"compare": {
"ctx.payload.hits.total": {
"gt": 0
}
}
},
"actions": {
"send_trigger": {
"webhook": {
"scheme": "https",
"host": "hooks.slack.com",
"port": 443,
"method": "post",
"path": "/services/XXXX/XXXX/XXXX",
"params": {},
"headers": {
"Content-type": "application/json"
},
"body": """{ "text": "{{ctx.payload}}"}"""
}
}
}
}
below is the structure of my logs in kibana
{
"_index": "index-beat",
"_type": "_doc",
"_id": "P3Toa34B1LVeuWotaVOY",
"_version": 1,
"_score": 1,
"_source": {
"#timestamp": "2022-01-18T06:38:19.559Z",
"name": "communication",
"loglevel": "ERROR",
"log": {
"file": {
"path": "/home/ubuntu/abc/abc/logs/communication.log"
},
"offset": 0
},
"timestamp": "2022-01-18T06:38:15.384279",
"exception": {
"ex_type": "None",
"ex": "None",
"tb": ""
},
"message": "{'err': 'Test'}"
},
"fields": {
"exception.ex_type": [
"None"
],
"loglevel.keyword": [
"ERROR"
],
"name.keyword": [
"communication"
],
"log.offset": [
0
],
"message": [
"{'err': 'Test'}"
],
"exception.tb": [
""
],
"exception.ex": [
"None"
],
"#timestamp": [
"2022-01-18T06:38:19.559Z"
],
"exception.tb.keyword": [
""
],
"loglevel": [
"ERROR"
],
"log.file.path": [
"/home/ubuntu/abc/abc/logs/communication.log"
],
"message.keyword": [
"{'err': 'Test'}"
],
"name": [
"communication"
],
"exception.ex_type.keyword": [
"None"
],
"exception.ex.keyword": [
"None"
],
"log.file.path.keyword": [
"/home/ubuntu/abc/abc/logs/communication.log"
],
"timestamp": [
"2022-01-18T06:38:15.384Z"
]
}
}
Please help me out in this one.
I have implemented Ocelot API gateway in my project which was working fine for 'GET' requests but when I tried to send 'POST' request I am getting the error:
previousRequestId: no previous request id, message: Error Code: UnableToFindDownstreamRouteError Message: Failed to match ReRoute configuration for upstream path: /api/patient/CreateAppointment, verb: POST. errors found in ResponderMiddleware. Setting error response for request path:/api/patient/CreateAppointment, request method: POST
Following is my ocelot.json:
{
"Routes": [
{
"DownstreamPathTemplate": "/api/patient/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "patientservice",
"Port": 81
}
],
"UpstreamPathTemplate": "/api/patient/{everything}",
"UpstreamHttpMethod": [ "GET", "POST" ],
"UpstreamHost": "*"
},
{
"DownstreamPathTemplate": "/api/actor",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "postgresqldapper",
"Port": 5001
}
],
"UpstreamPathTemplate": "/api/actor",
"UpstreamHttpMethod": [ "GET" ]
},
{
"DownstreamPathTemplate": "/api/product/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "productservice",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/product/{everything}",
"UpstreamHttpMethod": [ "GET" ]
},
{
"DownstreamPathTemplate": "/api/weatherforecast",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "postgresqldapper",
"Port": 5001
}
],
"UpstreamPathTemplate": "/api/weatherforecast",
"UpstreamHttpMethod": [ "GET" ]
},
{
"DownstreamPathTemplate": "/api/user",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "loginservicedapr",
"Port": 80
}
],
"UpstreamPathTemplate": "/api/user",
"UpstreamHttpMethod": [ "GET" ]
},
{
"DownstreamPathTemplate": "/api/user/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "loginservicedapr",
"Port": 5001
}
],
"UpstreamPathTemplate": "/api/user/{id}",
"UpstreamHttpMethod": [ "GET" ]
}
],
"GlobalConfiguration": {
"RequestIdKey": "OcRequestId",
"AdministrationPath": "/administration"
}
}
The API to which I am doing the 'POST' request is working fine for the 'POST' request when invoked directly from Swagger or Postman.
Please let me know what I should change in ocelot.json file so that the 'POST' request can through?
Your error is effectively saying that Ocelot is unable to route requests to
POST /api/patient/CreateAppointment
In your screenshot, your curl command (which is working) is a request to:
POST /api/patient
Your /{everything} path suffix is telling Ocelot that whatever suffix you call into the gateway with will be present on the downstream service.
My theory is that you have not defined a CreateAppointment endpoint operation on your downstream patient service API. Once you define this path on your service, your /api/patient/{everything} route mapping should work fine.
I'm using MMLib.SwaggerForOcelot for gateway. in .Net core
I got this error. Any idea?
My Upstream and DownStream:
"SwaggerEndPoints": [
{
"Key": "skIndustry",
"Config": [
{
"Name": "Industry API",
"Version": "v1",
"Url": "http://industryapi:80/swagger/v1/swagger.json"
}
]
}
]
My ReRoutes:
"ReRoutes": [
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "industryapi/",
"Port": 80
}
],
"UpstreamPathTemplate": "/Industry/{everything}",
"UpstreamHttpMethod": [ "POST", "PUT", "GET" ],
"SwaggerKey": "skIndustry"
}
]
Thanks,
Croos.
Please try new version 1.3.1.
This PR probably solve similar problem.