1

We have a bunch of endpoints where we'd like to do the exact same thing for each of them: Register them as a route and verify that the user has access to them. Very condensed our issue can be condensed to us having something like this:

[HttpGet, Route(EntityId.First)]
[HttpGet, Route(EntityId.Second)]
[VerifyAccessFilter(EntityId.First, EntityId.Second)]
public async Task<IActionResult> Endpoint()
{   
    return Ok();
}

But would much rather like something like:

[RouteAndVerify(EntityId.First, EntityId.Second)]
public async Task<IActionResult> Endpoint()
{   
    return Ok();
}

As you can tell this is very simplified, but I hope the intent gets across. The hard part seems to be registering the route without using the default Route-attribute.

7
  • That's not a good idea. Those attributes do very different things, used by different middleware components. You don't need to specify HttpGet twice btw Commented Nov 13, 2018 at 14:17
  • What is VerifyAccessFilter btw? The only hit when googling it is this question Commented Nov 13, 2018 at 14:19
  • Sorry for being unclear. VerifyAccessFilter is a custom filter. The reason for having 2 is that we'd like this method to be responsible for 2 different routes. As you can see the parameters are very similar, so if possible we'd like to merge the 2 attributes. Commented Nov 14, 2018 at 15:20
  • Just because the parameters look the same doesn't mean the attributes do the same thing. In any case, attributes themselves don't do anything. It's the middleware components that read them and perform some action. The routing middleware sees the Route attribute and registers that route. BTW if you use constants for the routes you don't need attributes. You could add them just as easily in the routing configuration Commented Nov 14, 2018 at 15:30
  • 1
    I have no gotten around to testing the 2 proposed solutions. Will do that today. Commented Nov 20, 2018 at 6:53

2 Answers 2

4

You can achieve this with a custom IActionModelConvention implementation. The official documentation explains the concept of an action model convention: Work with the application model in ASP.NET Core - Conventions. In a nutshell, by implementing IActionModelConvention, you can make changes to the application model and add filters, routes, etc to an action at runtime.

This is best explained with a sample implementation, which follows below. As you want to combine your existing MVC filter with the ability to configure routes for an action, the implementation below implements both IResourceFilter (this can be whatever filter type you're using) and IActionModelConvention:

public class VerifyAccessFilterAttribute : Attribute, IActionModelConvention, IResourceFilter
{
    public VerifyAccessFilterAttribute(params string[] routeTemplates)
    {
        RouteTemplates = routeTemplates;
    }

    public string[] RouteTemplates { get; set; }

    public void Apply(ActionModel actionModel)
    {
        actionModel.Selectors.Clear();

        foreach (var routeTemplate in RouteTemplates)
        {
            actionModel.Selectors.Add(new SelectorModel
            {
                AttributeRouteModel = new AttributeRouteModel { Template = routeTemplate },
                ActionConstraints = { new HttpMethodActionConstraint(new[] { "GET" }) }
            });
        }
    }

    public void OnResourceExecuting(ResourceExecutingContext ctx) { ... }

    public void OnResourceExecuted(ResourceExecutedContext ctx) { ... }
}

In this example, it's all about the Apply method, which simply adds a new SelectorModel for each routeTemplate (as I've named it), each of which is constrained to HTTP GET requests.

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

Comments

-2

Generally speaking, you cannot "merge" attributes, because attributes do not execute code. Attributes are only markers. Like "this method is marked red and blue". Then other code will come along, one looking for all red marks and doing something and another looking for all blue marks and doing something else. Building a purple mark by merging red and blue is just going to confuse the code looking for the markup, because purple is neither red nor blue.

However, AOP (aspect oriented programming) is available from third parties for C# and means attributes (called aspects because they do more than the normal marker attributes) can execute code.

You could write an aspect that decorates the method it's sitting on with the attributes you need, so you can write it once (and test it) and then you can set it on every method without worrying about forgetting an attribute or setting it wrong.

There are multiple AOP providers for C#, the most popular one seems to be PostSharp. You can see how write an aspect that adds attributes to a class or method at compile time with PostSharp here.

Comments

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.