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?
Related
I am using Ocelot gateway and for swagger document using "MMLib.SwaggerForOcelot" library.
For some swagger Key, swagger UI is showing "No operations defined in spec!" and swagger JSON is coming without paths like
{
"openapi": "3.0.1",
"info": {
"title": "Admin API",
"version": "v1"
},
"paths": {},
"components": {
"schemas": {}
}
}
Ocelot Configuration Route is
{
"DownstreamPathTemplate": "/api/admin/v{version}/{everything} ",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 5000
}
],
"UpstreamPathTemplate": "/api/admin/v{version}/{everything}",
"UpstreamHttpMethod": [],
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 1000,
"TimeoutValue": 900000
},
"SwaggerKey": "AdminAPI"
}
and Swagger Configuration is
"SwaggerEndPoints": [
{
"Key": "AdminAPI",
"Config": [
{
"Name": "Admin API",
"Version": "v1",
"Url": "http://localhost:5000/swagger/v1/swagger.json"
}
]
}
]
after reviewing the MMLib.SwaggerForOcelot source code, it looks like something to do with the version in the downstream path, any clue on how this can be fixed?
The issue is that MMLib.SwaggerForOcelot is not considering {version} while doing Ocelot transformation.
RouteOptions has a property TransformByOcelotConfig which is set to true by default, so once swagger JSON is obtained from downstream, the transformation will be done.
here, it will try to find the route from the route configuration like below and if not found it will delete the route from swagger JSON
private static RouteOptions FindRoute(IEnumerable<RouteOptions> routes, string downstreamPath, string basePath)
{
string downstreamPathWithBasePath = PathHelper.BuildPath(basePath, downstreamPath);
return routes.FirstOrDefault(p
=> p.CanCatchAll
? downstreamPathWithBasePath.StartsWith(p.DownstreamPathWithSlash, StringComparison.CurrentCultureIgnoreCase)
: p.DownstreamPathWithSlash.Equals(downstreamPathWithBasePath, StringComparison.CurrentCultureIgnoreCase));
}
The problem is StartsWith will return false since swagger JSON path will be like
/api/admin/v{version}/Connections
and route config is like
/api/admin/v{version}/{everything}
and version will replace with the version given in swagger options so that it will become
/api/admin/v1/{everything}
Fix to this problem will be
Either set "TransformByOcelotConfig":false in swagger option
"SwaggerEndPoints": [
{
"Key": "AdminAPI",
"TransformByOcelotConfig":false,
"Config": [
{
"Name": "Admin API",
"Version": "v1",
"Url": "http://localhost:5000/swagger/v1/swagger.json"
}
]
}
]
Or Change the route, just to have {everything} keyword
{
"DownstreamPathTemplate": "/api/admin/{everything} ",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 5000
}
],
"UpstreamPathTemplate": "/api/admin/{everything}",
"UpstreamHttpMethod": [],
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 1000,
"TimeoutValue": 900000
},
"SwaggerKey": "AdminAPI"
}
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?
{
"DownstreamPathTemplate": "/ProceedToBuy/PostWishList",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 8003
}
],
"UpstreamPathTemplate": "/AddToWishlist",
"UpstreamHttpMethod": [ "POST" ]
}
I want to send json body with this route request.
Actually you don't have to route json the request will automatically route to the downstream path, we can just directly make request to upstream path with json and it'll work!
I'm creating a project with microservices and now I want to be able to test the API gateway that I created as an orchestrator with ocelot, but I can't test it through the browser, the API can't access it, can someone help?
ocelot.json:
{
"Routes": [
// Billing.API
{
"DownstreamPathTemplate": "/api/v1/Billing",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "billingapi",
"Port": "80"
}
],
"UpstreamPathTemplate": "/Billing",
"UpstreamHttpMethod": [ "GET" ]
}
]
}
When I run the project and in the browser URL I pass /api/v1/Billing
as I passed the DownstreamPathTemplate property.
Nothing is returned to me.
I would suggest to first try general template and see if it works:
{
"DownstreamPathTemplate": "/{everything}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "billingapi",
"Port": 80,
}
],
"UpstreamPathTemplate": "/billingapi/{everything}",
"UpstreamHttpMethod": [ "Get", "Post" ]
}
From the Ocelot documentation about Downstream:
The DownstreamPathTemplate, DownstreamScheme and DownstreamHostAndPorts define the URL that a request will be forwarded to.
And for the Upstream:
The UpstreamPathTemplate is the URL that Ocelot will use to identify which DownstreamPathTemplate to use for a given request.
You should pass UpstreamPathTemplate URL on the browser to route billing API. In your case, the request HTTP://<gateway.address>/Billings will be routed to HTTP://billingapi/api/v1/Billing.
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.