Search code examples
c#asp.net-coremodel-view-controllermiddleware.net-5

HttpContext GetEndpoint on modified request path .net 5


I'm trying to create a middleware to handle country code in the url. The code i have works great for removing the country code, so it's routed to the correct endpoint in the mvc pipeline.

The problem i have is that i need to do some actions depending on if the endpoint has a certain attribute or not.

I see that HttpContext has a method GetEndpoint, and this is exactly what i need.

When the countryCode is in the url (mysite.com/us/home/Index), the GetEndpoint returns null.

But if i enter the site without the countryCode in the url (mysite.com/home/Index), then the GetEndpoint works.

How can i use the GetEndpoint() method on the modified request url?

Is it another property on HttpContext i need to change?

public async Task InvokeAsync(HttpContext httpContext)
{
    // mysite.com/us/home/Index
    var currentAddress = httpContext.Request.Path; 
    
    // mysite.com/home/Index
    httpContext.Request.Path = ExtractCountryCodeFromUrl(currentAddress); 

    var endpoint = httpContext.GetEndpoint(); // null

    var hasMyAttribute = endPoint.Metadata.GetMetadata<MyAttribute>();
    // Do something...

    await next(httpContext);
}

Solution

  • I found a solution to this,

    private static ControllerActionDescriptor GetControllerByUrl(HttpContext httpContext)
    {
        var pathElements = httpContext.Request.Path.ToString().Split("/").Where(m => m != "");
        string controllerName = (pathElements.ElementAtOrDefault(0) == "" ? null : pathElements.ElementAtOrDefault(0)) ?? "w";
        string actionName = (pathElements.ElementAtOrDefault(1) == "" ? null : pathElements.ElementAtOrDefault(1)) ?? "Index";
    
        var actionDescriptorsProvider = httpContext.RequestServices.GetRequiredService<IActionDescriptorCollectionProvider>();
        ControllerActionDescriptor controller = actionDescriptorsProvider.ActionDescriptors.Items
        .Where(s => s is ControllerActionDescriptor bb
                    && bb.ActionName == actionName
                    && bb.ControllerName == controllerName
                    && (bb.ActionConstraints == null
                        || (bb.ActionConstraints != null
                            && bb.ActionConstraints.Any(x => x is HttpMethodActionConstraint cc
                            && cc.HttpMethods.Any(m => m.ToLower() == httpContext.Request.Method.ToLower())))))
        .Select(s => s as ControllerActionDescriptor)
        .FirstOrDefault();
        return controller;
    }
    

    Then i can do this

    ControllerActionDescriptor controller = GetControllerByUrl(httpContext);
    var countryCodeAttribute = controller.MethodInfo.GetCustomAttribute<MyAttribute>();
    
    

    I don't know how well this scales, but it will work for now. I found the main part of the code here: Changing Request Path in .Net Core 3.1