Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Header based routing in ASP.NET Core

I'm trying to route requests to different controllers based on a HTTP header sent to the service.

in my Configure method, I have

app.MapWhen(ctx => !string.IsNullOrWhiteSpace(ctx.Request.Headers["Magic"]), DoStuff);

My issue is that I have no idea how to specify controllers, or even modify the route

 private static void DoStuff(IApplicationBuilder app)
 {
    /// ?!? \\\
 }

Ideally, I'd like that if the header Magic: Missile is sent, to rewrite the route to be /Missile

like image 463
ESG Avatar asked Sep 03 '16 00:09

ESG


2 Answers

While writing a custom middleware is certainly a good option, another option would be to create an action constraint (by implementing the IActionContraint interface). This would allow you to decorate your methods with an attribute which dictates the header / value that must be provided in the request in order for a given method to be invoked.

I found this to be particularly helpful when working with WebHooks where the external system uses a single URL for all hooks, and uses a header to differentiate the different action types.

Here is what a simple implementation of this attribute could look like:

public class HttpHeaderAttribute : Attribute, IActionConstraint
{
    public string Header { get; set; }
    public string Value { get; set; }

    public HttpHeaderAttribute (string header, string value)
    {
        Header = header;
        Value = value;
    }

    public bool Accept(ActionConstraintContext context)
    {
        if (context.RouteContext.HttpContext.Request.Headers.TryGetValue(Header, out var value))
        {
            return value[0] == Value;
        }

        return false;
    }

    public int Order => 0;
}

And here's a sample of how you could use it:

    [HttpPost, Route("webhook"), HttpHeader("X-Drchrono-Event", "PATIENT_CREATE")]
    public IActionResult DrChronoPatientCreate(WebHookData data)
    {

    }

    [HttpPost, Route("webhook"), HttpHeader("X-Drchrono-Event", "PATIENT_MODIFY")]
    public IActionResult DrChronoPatientModify(WebHookData data)
    {

    }
like image 66
RMD Avatar answered Oct 03 '22 06:10

RMD


I ended up writing a custom middleware to rewrite the uri before hitting the mvc layer. Works nicely!

public Task Invoke(HttpContext context)
{
    const string apiPath = "/api";

    var magic context.Request.Headers["Magic"];

    if (string.IsNullOrWhiteSpace(magic))
    {
        return _next.Invoke(context);
    }

    var path = context.Request.Path.ToUriComponent();

    if (!path.StartsWith(apiPath))
    {
        return _next.Invoke(context);
    }

    path = string.Concat(path.Skip(apiPath.Length));
    path = $"{apiPath}/{magic}{path}";
    context.Request.Path = path;

    return _next.Invoke(context);
}
like image 39
ESG Avatar answered Oct 03 '22 05:10

ESG