17

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

2 Answers 2

24

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)
    {

    }
Sign up to request clarification or add additional context in comments.

Comments

8

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);
}

1 Comment

I haven't tried, but action contraints, such as in the accepted answer, are a cleaner way to do it

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.