ASP.NET 5 regex route endpoint for files and non-files

1.1k Views Asked by At

I have this possible urls:

mywebsite/a/b/c.txt
mywebsite/x/t
mywebsite/z
mywebsite/z/i.jpg

So when the url ends with a file, I need to return 404, otherwise I call the controller. To this I have

endpoints.MapControllerRoute(
    name: "default",
    pattern: "{*.}",
    defaults: new { controller = "Home", action = "Index" });

And this how I'm trying to check for router files

// endpoints.MapGet("{file:regex(.(css|txt|js|jpg|png|ico|json)$)}", async context =>
// endpoints.MapGet(@"(.*\.)(jpe?g|css|txt|js|png|ico|json)$", async context =>
/* endpoints.MapGet(@"{file:regex((.*\.)(jpe?g|css|txt|js|png|ico|json)$)}", async context =>
{
    context.Response.StatusCode = 404;
}); */

endpoints.MapControllerRoute(
    name: "File",
    // pattern: @"(.*\.)(jpe?g|css|txt|js|png|ico|json)$",
    // pattern: @"{controller:(.*\.)(jpe?g|css|txt|js|png|ico|json)$}",
    // pattern: "{ssn}",
    // constraints: new { ssn = @"(.*\.)(jpe?g|css|txt|js|png|ico|json)$" },
    pattern: @".*\.(css|js|gif|jpg)(/.)?",
    defaults: new { controller = "NotFound", action = "Index" });

The problem is that https://localhost:5001/style.css works, but https://localhost:5001/z/style.css it doesn't.

So what could I be missing here?

2

There are 2 best solutions below

0
King King On

You cannot use the catch-all notation (expressed by {*...} or {**...}) to solve this issue. It does not support that way because the catch-all will match everything to the end of the path. The name following * is the route data key which can be used to get the captured value in your code later.

To solve this, you can use a middleware for this purpose. The route is mapped normally to some of your patterns. But later in the processing pipeline, we can examine the Request.Path to accordingly modify the `Request.Path (like URL rewriting), like this:

//inside Startup.Configure
app.Use((context, next) => {
            if(Regex.IsMatch(context.Request.Path, @".+(\.css|\.js|\.gif|\.jpg)$", 
                             RegexOptions.Compiled | RegexOptions.IgnoreCase, TimeSpan.FromSeconds(3)))
            {
                var linkGenerator = context.RequestServices.GetRequiredService<LinkGenerator>();
                var path = linkGenerator.GetPathByAction("Index", "NotFound") ?? "/NotFound";
                context.Request.Path = path;
            }
            return next();
        });

The above code must be placed before app.UseStaticFiles, otherwise the static files middleware will handle and short-circuit the requests first.

Note that LinkGenerator.GetPathByAction can return null if the controller or action does not exist. So in that case we fallback to the default conventional path of /NotFound.

0
JeffC On

This regex

[^\/]+\w*\.\w*$

will return all URLs that end in file names. File names being defined as (word characters)(period)(word characters).

See it working on regex101.com.