Search code examples
c#routescustom-attributes

Combine Route Attribute With Arbitrary String


I'm trying to create a controller on my gateway that does pretty much nothing except proxy a request to another API. (It does some other very basic prep work but that's kinda beyond the scope of this so I've left it out). This was previously handled by ~20 controllers that all did the same thing so I'm trying to simplify things.

[Route("api/reporting/route1")]
[Route("api/reporting/route2")]
public class TestController : Controller
{
    [HttpPost("{clientId:int")]
    public async Task<ActionResult<MemoryStream>> GetReport(int clientId, [FromBody] object requestBody)
    {
            var report     = await ReportingClient.DownloadReport(/*HERE*/, requestBody);
            return File(report, "application/pdf");                       
    }
}

The destination is determined purely by the route that hits this controller.

i.e. route1 could go to api/reporting/test/1 and route2 could go to api/test/2.

What's already there isn't as consistent as I'd like but it's out of my scope to go make them all consistent.

As such, I'd like to be able to decorate my controller with attributes that combine Route with an arbitrary Target string.

Something like this:

[ProxyRoute("api/reporting/route1", "api/reporting/test/1")]
[ProxyRoute("api/reporting/route2", "api/test/2")]
public class TestController : Controller
{
    [HttpPost("{clientId:int")]
    public async Task<ActionResult<MemoryStream>> GetReport(int clientId, [FromBody] object requestBody)
    {
            var report     = await ReportingClient.DownloadReport(Target, requestBody);
            return File(report, "application/pdf");                       
    }
}

I could, of course, include a static readonly dictionary in my class to achieve this, but I feel like that's messier so I'd like to do it with an attribute if I can.

How would I achieve this?


Solution

  • So I actually solved this... I think I was overcomplicating it in my head. All it required was for me to inherit from the RouteAttribute itself and then add my extra string as a public property:

        [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
        protected class ReportingProxyRouteAttribute : RouteAttribute
        {
            public string Destination    { get; }
            public ReportingProxyRouteAttribute(string template, string destination) : base(template)
            {
               Destination    = destination;
            }
        } 
    

    I can then get the extra string myself:

        private ReportingProxyRouteAttribute GetRouteForRequest()
        {
           var possibleRoutes = Attribute.GetCustomAttributes(GetType(), typeof(ReportingProxyRouteAttribute)).Cast<ReportingProxyRouteAttribute>();
           return possibleRoutes.First(r => Request.Path.ToString().ToLowerInvariant().Contains(r.Template.ToLowerInvariant()));
        }