Include a file extension in ASP.NET Core MVC routes

Viewed 662

I'm porting a legacy application to ASP.NET Core and I'm trying to keep the URLs consistent. Ideally I need to include a "file extension" in the URL, e.g. /Home/Index.ext should route to the Index action on HomeController.

I've started with the standard web application template in Visual Studio and tried modifying the default route:

endpoints.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}.ext/{id?}");

A request for /Home/Index.ext then gives the error:

An unhandled exception occurred while processing the request.
AmbiguousMatchException: The request matched multiple endpoints. Matches:

WebApplication1.Controllers.HomeController.Index (WebApplication1)
WebApplication1.Controllers.HomeController.Privacy (WebApplication1)
WebApplication1.Controllers.HomeController.Error (WebApplication1)
Microsoft.AspNetCore.Routing.Matching.DefaultEndpointSelector.ReportAmbiguity(CandidateState[] candidateState)

I can't see why it's not taking the action name from this URL to select the correct action. Is there some way I can change this route pattern to get it to match this correctly?

3 Answers

You can do this with the IOutboundParameterTransformer that's inside Microsoft.AspNetCore.Routing.

Simply create a transformer as follows (if you want the .ext extension):

public class AddExtensionTransformer : IOutboundParameterTransformer
{
    public string TransformOutbound(object value)
        => value + ".ext";
}

Then you register it as a constraint (I used the name addextension):

services.Configure<RouteOptions>(options =>
{
    options.ConstraintMap["addextension"] = typeof(AddExtensionTransformer);
});

And then in your route, you simply use addextension as a route constraint (using the : delimiter).

For the default route from your post, it will look like this:

endpoints.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action:addextension=Index}/{id?}");

ASP.NET Core will now also automatically generate URLs like /Home/Index.ext whenever you tell it to generate an action URL.

So it not only parses them but also generates them (works both ways).

You can then use an Attribute Route like this:-

[Route("Home/Index.ext")]
public ActionResult Index() {... }

For more information read this Documentation

You can use URL Rewriting Middleware like below(html extension as a sample):

1.Custom a class containing ReWriteRequests

public class RewriteRules
{  
    public static void ReWriteRequests(RewriteContext context)
    {
        var request = context.HttpContext.Request;

        if (request.Path.Value.EndsWith(".html", StringComparison.OrdinalIgnoreCase))
        {
            context.HttpContext.Request.Path = context.HttpContext.Request.Path.Value.Replace(".html", "");

        }
    }
}

2.Startup.cs:

Be sure use URL Rewriting Middleware before app.UseStaticFiles();

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseRewriter(new RewriteOptions()
                    .Add(RewriteRules.ReWriteRequests)
                    );

    app.UseHttpsRedirection();
    app.UseStaticFiles();

    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();
        
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");
        endpoints.MapRazorPages();
    });
}

Reference:

URL Rewriting Middleware in ASP.NET Core

Related