Generate route templates from URL using ASP.NET Core - asp.net-core

I've been reading about URL generation based on values in a route dictionary.
app.Run(async (context) =>
{
var dictionary = new RouteValueDictionary
{
{ "operation", "create" },
{ "id", 123}
};
var vpc = new VirtualPathContext(context, null, dictionary,
"Track Package Route");
var path = routes.GetVirtualPath(vpc).VirtualPath;
context.Response.ContentType = "text/html";
await context.Response.WriteAsync("Menu<hr/>");
await context.Response.WriteAsync(
$"<a href='{path}'>Create Package 123</a><br/>");
});
Is it possible to perform the reverse action and generate a route template from a URL? I would think ASP.NET core would need to do this in order to check an incoming request URL to see if it matches a route in the route table in middleware. So if a route was mapped using say "/package/{operation}/{id}", it would have to have some mechanism to see that the URL "/package/create/123" matches this route.
I have a need to perform this type of action to see if a URL matches a custom route template that I have stored in the database when a request comes in.

Related

ASP.NET Core Endpoint - route all calls to specific route first

What do I need to change this to route all requests to /api/ShibAuth?
endpoints.MapGet("/", async context =>
{
context.Response.Redirect("/api/ShibAuth");
});
The code above obviously routes any calls to root URL and I've already tried what I though was appropriate wildcard.
What do I need to change this to route all requests to /api/ShibAuth?
Well, if I correctly understand the requirement, you would like all of your request to redirect to this /api/ShibAuth route at the begining.
Certainly, we can implement above scenario using UriBuilder class which provides the functionality to modify HttpRequest.Path. Finally, rebuild the request URI and redirect to your expected path. You can do as following
Solution:
app.MapGet("/", async context =>
{
var originalUrl = context.Request.GetDisplayUrl();
var routeToCallFirst = "api/ShibAuth";
var updatedUrl = (new UriBuilder(originalUrl) { Host = context.Request.Host.Host, Path = routeToCallFirst }).Uri;
context.Response.Redirect(updatedUrl.AbsoluteUri);
});
Output:
Note: Here, I am redirecting the all landing request to /api/ShibAuth controller from the middleware.

Check if request is made to Razor Page

How can I check within middleware code if current request is made to Razor Page not to any other resource (static file, or API)?
All my APIs are located within api folder, so if (!context.Request.Path.StartsWithSegments("/api")) {} filters out APIs, but that will not work for static content as these files and libraries are placed within number of folders what results in number of URL segments.
Could not find any relevant property in context.
First step - place the middleware after app.UseRouting(). Any request for a static file will never reach your middleware because the static files middleware will short circuit the request. Also, after this point, the routing middleware will have selected the endpoint and populated the endpoint metadata. Then you can test the endpoint metadata collection to see if it includes PageRouteMetaData, which tells you that this is a Razor page route:
app.Use((context, next) => {
var endpoint = context.GetEndpoint();
if (endpoint != null)
{
foreach(var md in endpoint.Metadata)
{
if( md is PageRouteMetadata)
{
// this is a page route
}
}
}
return next(context);
});

Route matching from static Uri + route data extract

I have a .Net Core 2.2 MVC app with routes defined as route attributes on my MVC actions.
I would like to find the matching route (if any) and what the route data are from a given Uri (i.e. not the current HTTP request but a static Uri coming from a database for instance).
I already use the LinkGenerator.GetPathByAction() method to get the "route URL" for a specific action with route data. What I am after would be the opposite: a method that takes a URL/Uri and return the matching route and its route data.
For instance if I have a route registered with the following template:
[Route("/my-action/{id:int}/{name}")]
the URL "/my-action/5/my-test-name" would return the following route data:
id: 5
name: my-test-name
I went through the routing documentation but I haven't found anything.
https://github.com/aspnet/AspNetCore.Docs/blob/master/aspnetcore/fundamentals/routing.md
The only option that I see would be to somehow call the RouteMiddleware (https://github.com/aspnet/AspNetCore/blob/master/src/Http/Routing/src/RouterMiddleware.cs) with a mock HttpContext which seems overkill if even doable?
I would like to find the matching route (if any) and what the route data are from a given Uri.
If you want to get the route data in the action with the matching route , you could directly use GetRouteData in the current HttpContext object like below :
[Route("/GetRouteData/{id:int}/{name}")]
public void GetRouteData()
{
var routeData = HttpContext.GetRouteData();
var routeCollection = routeData.Values;
var id = routeData?.Values["id"]?.ToString();
var name = routeData?.Values["name"]?.ToString();
}
About finding out if a URL matches an action in ASP.NET MVC Core , you could refer to the following links :
https://joonasw.net/view/find-out-if-url-matches-action
https://weblog.west-wind.com/posts/2019/May/15/Accessing-RouteData-in-an-ASPNET-Core-Controller-Constructor
https://rimdev.io/asp-net-core-routes-middleware/

Passing Controller Action output as SupplyData in UseSpaPrerendering of .Net Core

In .Net Core application, I have below code in Configure method of Startup.cs file.
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
spa.UseSpaPrerendering(options =>
{
options.BootModulePath = $"{spa.Options.SourcePath}/dist-server/main.js";
options.BootModuleBuilder = env.IsDevelopment() ? new AngularCliBuilder(npmScript: "build:ssr") : null;
options.ExcludeUrls = new[] { "/sockjs-node" };
});
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
UseSpaPrerendering has an option to provide SupplyData callback which lets you pass arbitrary, per-request, JSON-serializable data.
In my case there are pages in my Angular application which makes http requests to fetch data. Since these requests are made to the same application. I see a potential of optimization i.e. if we could just call the corresponding Controller Action method and supply its data to Angular, so that we dont have to make an http request for SSR.
Can anyone please guide how to achieve this.
I know that below is how we pass data using SupplyData
options.SupplyData = (context, data) =>
{
// Creates a new value called isHttpsRequest that's passed to TypeScript code
data["isHttpsRequest"] = context.Request.IsHttps;
};
But how to we pass the results/output of a Controller Actions (which returns json).
I wrote a package to determine the currently activated SPA route from the supplydata delegate.
https://github.com/MusicDemons/AspNetSpaPrerendering
You have to define all your SPA routes using the SpaRouteBuilder and then you can check which route was activated and get the route data (like an id). Based on that you get data from your database through your repositories and add this data to the array. A complete example is included.

Filter for static file middleware

Is there a way to intercept the request before it got serve, so I can edit a picture or create dynamic pdf on demand? I tried using MapArea and redirect the request to a controller, but when I use staticfiles middleware, it catch the request, and my controller wont handle the request.
If your static files does not exist and you want generate them on-thy-fly - it's better to create your own middelware and register it before UseStaticFiles.
If files exist, but you want "slightly" modify response (for different users for example) - you may use OnPrepareResponse handler in static file options:
var staticFileOptions = new StaticFileOptions
{
OnPrepareResponse = (context) =>
{
var fn = context.File.Name.ToLowerInvariant();
if (fn.EndsWith(".pdf"))
{
SomeService.LogPdfDownload(context.Context.Response);
}
else
{
context.Context.Response.Headers.Add("Cache-Control", "public, max-age=15552000"); // 180 days
}
}
};
app.UseStaticFiles(staticFileOptions);
From docs: OnPrepareResponse is called after the status code and headers have been set, but before the body has been written
Is there a way to intercept the request before it got serve
Yes. You can write your own middleware and add it to IApplicationBuilder before you call UseStaticFiles. See https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware#ordering
See also https://learn.microsoft.com/en-us/aspnet/core/fundamentals/routing. You may also be able to solve this problem by writing routes instead of middleware.