.NET CORE route Api sub domain to Api controller - c#

I have Api Controllers and MVC controllers in my .NET CORE application.
How can I route sub domain api.mysite.com to point only on Api controllers, and dashboard.mysite.com to point on Web Application all in same project?

If you want to implement this in a single ASP.NET Core application, you can do something like this:
Make Api controllers available say at path /Api. You can achieve this using routes, areas or application branches.
Use a reverse proxy which is capable of URL rewriting (e.g. IIS on Win, Nginx on Linux). Configure the reverse proxy so that the requests arriving at api.mysite.com/path are forwarded to your application as /Api/path.
A remark:
If you want to generate URLs in your Api controllers, you should remove the /Api prefix from the path to get correct URLs (and of course you have to configure your reverse proxy to append the necessary headers like X-Forwarded-Host, etc.) For this purpose you can use this simple middleware.
Update
As it was discussed in the comments, an application branch seems the best solution in this case because it enables separate pipelines for the MVC and API application parts.
Actually, it's very easy to define branches. All you need to do is to put a Map call at the beginning of your main pipeline in the Configure method of your Startup class:
public void Configure(IApplicationBuilder app)
{
app.Map("/Api", BuildApiBranch);
// middlewares for the mvc app, e.g.
app.UseStaticFiles();
// some other middlewares maybe...
app.UseMvc(...);
}
private static void BuildApiBranch(IApplicationBuilder app)
{
// middlewares for the web api...
app.UseMvc(...);
}
Now, when a request arrives and its path starts with /Api, the request gets "deflected" and goes through the branch pipeline (defined in BuildApiBranch method) instead of going through the main pipeline (defined in Configure method, following the Map call).
Some things to keep in mind:
When a request is "captured" by the branch, the prefix /Api is removed from the HttpContext.Request.Path property (and appended to HttpContext.Request.PathBase). So you need to define the API routes in the UseMvc method as if the request path had no prefix at all.
Using this code you have two separate pipelines but they share the components registered in Startup.ConfigureServices. If this is undesired, it's possible to create separate DI containers for each of the pipelines. However, this is a somewhat advanced topic.

Related

Generating links to self when running behind reverse proxy

How can I generate absolute links to other resources in my RESTful API app when the app is meant to be accessed via a reverse proxy that publishes just the paths under /api?
My app is an API with a common layout of routes like /api, /swagger and /health. It is published on my employer's API management under a path of the form /business-area/api-name/v1. Calling the API both directly and through the API gateway overall works: calling https://api-gateway.company.com/business-area/api-name/v1/some-resource results in internal call to https://my-app.company.com/api/some-resource.
The issue is that the links in my app's responses point directly to the backend app (https://my-app.company.com/api/another-resource), not the the API gateway (https://api-gateway.company.com/business-area/api-name/v1/another-resource). They are generated using IUrlHelper.
I solved the domain by the ForwardedHeadersMiddleware and adding the X-Forwarded-Host by a policy on the API management. Sadly, we are allowed to use just extremely simple policies, so if we published the API using multiple gateways, the current solution would generate link to just a single one. But that is an issue to be solved somewhen later; now it works OK.
I could not get the path to work well. I tried changing the paths using a middleware as hinted in the ASP.NET Core behind proxy docs:
app.Use((context, next) =>
{
context.Request.PathBase = "/business-area/api-name/v1";
if (context.Request.Path.StartsWithSegments("/api", out var remainder))
{
context.Request.Path = remainder;
}
return next();
});
When I insert this middleware high in the pipeline, it breaks the routing, but if I insert it low enough, the routing works OK and only link generation is affected. But it seems that only PathBase change really affects link generation as the /api is still in the generated URI. I can see that the Path of the request object is really changed, though, so it is probably just that link generation uses the routing info directly, without passing through my middleware, which makes sense, but it rules out the middleware solution.
Is wrapping the standard IUrlHelper in my own implementation and postprocessing the URLs it returns a good way to go? I don't know how to go about that. I use the IUrlHelper from the ControllerBase.Url property and debugger tells it is actually an instance of Microsoft.AspNetCore.Mvc.Routing.EndpointRoutingUrlHelper. Doing the wrapping in every action seems wrong (repetitive, error-prone).
Changing the routing so that /api moves to the root is my last resort option as it mixes up the namespaces: technical endpoints like /health and /swagger would live among the actual resources of the API. Is there a reasonable way to avoid that while keeping the links working? This all seems like a pretty standard problem and I am surprised I cannot find how to solve it.
We use .NET 5 and we will migrate to .NET 6 as soon as it is out, if that makes any difference.

how does host differentiate MVC request and Web API request

I'm new to asp.net mvc and web api. I'm reading a book which says:
ASP.NET MVC uses: System.Web.HttpRequest
and Web API Equivalent is System.Net.Http.HttpRequestMessage
and below is a picture that describes the request and result flow of web api
So my question is, how does hosting environment(which will typically be IIS) know that it should create a HttpRequestMessage object to represent the request from the client? I mean if the application is a MVC application, IIS should create a HttpRequest object instead of HttpRequestMessage, so how does IIS know which one to create?
As you can see from the picture you posted, the HttpRequestMessage exists only inside the "hosting" environment, web browser client does not know anything about that.
In the "hosting" world, IIS app pool is running the code you have built and deployed which knows very well wich framewok you are using as your code also contains the using assemblies you listed, System.Web... or System.Net...
Consider that even if you have shown separation between hosting, Controller and Action, all of that is running in same App Pool in IIS which, again, runs your code so knows what it is about as your IL assemblies were built from your specific source code.
I am not sure if I understand your question but this might be what you're looking for:
I mean if the application is a MVC application, IIS should create a
HttpRequest object instead of HttpRequestMessage, so how does IIS know
which one to create?
You must remember how you differentiate between a normal MVC Controller and a Web API Controller...
WebAPI Controllers enforces this annotation [ApiController] and must inherits from ControllerBase:
[ApiController]
public class PeopleController : ControllerBase {
//Your API methods here
}
A normal MVC Controller only inherits from Controller base class:
public class PeopleController : Controller {
//Your Action methods here...
}
Those already create configuration for your APP which becomes easier for you Hosting environment to know what is going and what to return when.
I hope you find this helpful.

Default controller is not getting called when Custom middleware is injected using ApplicationBuilder Map in .net core Web API

I trying to add specific middleware for a specific path as follow. The Middleware is getting called but the controller is not getting called and API returns 404 not found.
app.Map("/api/tokenize", subApp =>
{
subApp.UseRequestTokenizationMiddleware();
});
What code/configuration change required so that controller is also get called along with custom middleware.
The key is that in .Net Core you can specify the "path" a request takes. In the Configure method of Startup add a line
app.UseRequestTokenizationMiddleware();
and make sure that it only processes requests that are pertinent. Then the next handler in the chain will get the request. The last thing in Configure is usually
app.UseMvcWithDefaultRoute();
Here's a good start ASP.NET Core Middleware

How can I validate a URL before the request gets to the controller

Our service uses ASP.NET Core and in the application pipeline, we have several middlewares which are configured in StartUp.cs Configure(IApplicationBuilder app) method.
The middlewares are added by this method:
app.UseMiddleware<Type>();
I would like to validate the HttpContext.Request.Path and make sure it can hit one of the controllers. How can I get the list of available routes (controller path) in the middleware code or is there even a simpler way to see if this certain request path will hit one of the registered controller? We used xxxxcontroller : ControllerBase and a [Route("controller/{version}/{id}] attribute to register the controller.
Thanks a lot.
I suggest you to take a look at Asp.net core identity, If I understood what you`re looking for, you need to use roles to guarantee access to certain routes.
I don't know how to get a list of all routes and check that the path is for a valid route but you can use middleware to check the response status code after MVC runs and if the status code is a 404 then you know it wasn't a valid route and you can handle it accordingly.
The UseStatusCodePagesWithReExecute extension method basically uses this approach to handle not only 404 errors but all error status codes.

How do I get a list of all configured WebApi routes in asp.net MVC?

I'm trying to pass configuration values to bootstrap a single page AngularJs app hosted within an MVC app that utilises WebApi (see below from the Razor view, this is achieved by inheriting a BasePage and dependency injecting a Serializer/FinancialYears service).
<script>
var angularInitConfig = #Html.Raw(Serializier.Serialize(new {
financialYears = FinancialYearsService.GetFinancialYears()
}));
</script>
This works perfectly, however I would really like to be able to extend it to include the routes from my WebApi app to avoid having to configure the endpoints in both the WebApi app AND the AngularJs app individually.
Having poked around in the RouteTable.Routes class I can see that the information I require is available and accessible from within the view, however I've been unable to extract it.
So what I'd ideally like to do is generate a collection of objects as defined below from the RouteTable.Routes class, serialize them and spit them out in the bootstrap config for the AngularJS app to consume.
new {
name = "SomeService",
route = "api/{financialYearId}/someservice",
method = "POST"
}
Does anybody have an idea how to extract this information from RoutesTable.Routes? Is there an easier way to generate the data required?
NB. All WebApi routes are configured explicitly using the Routes attribute as such:
[HttpGet]
[Route("api/{financialYearId}/someservice")]
If you create default template asp.net Mvc or WebAPi using Visual Studio, you will get Help in Folder > Areas\HelpPage...and if you access your application in : Http://yourportapplication/api/Help if project webapi...
then, you can see the code how to get information...just for started what you looking for,....

Categories