Different routing behavior between Web API and Azure Function HTTP Trigger Causing Problems

asked 2022-01-20T21:27:19.48+00:00
Jon Krupa 21 Reputation points

There seems to be a major difference in the behavior of route templates in Azure Functions HTTP triggers vs. Web APIs. I have a Web API project (.NET 5) that has a number of controllers. I'm being tasked to divide it out into microservices using Azure Functions instead of an app service. There are three endpoints in one controller:

[HttpGet("history/{country}/{stateProvince}/{countyRegion}")]
IActionResult GetByCounty(string country, string stateProvince, string countyRegion)

[HttpGet("history/{country}/{stateProvince}")]
IActionResult GetByState(string country, string stateProvince)

[HttpGet("history/postalcodes/{country}/{postalCode}")]
IActionResult GetByPostalCode(string country, string postalCode)

This is all well and good where everything works as expected. In the function app, I defined the three HTTP triggers:

HttpResponseData GetByCounty([HttpTrigger(Route = "history/{country}/{stateProvince}/{countyRegion}")]

HttpResponseData GetByPostalCode([HttpTrigger(Route = "history/postalcodes/{country}/{postalCode}")]

HttpResponseData GetByState([HttpTrigger(Route = "history/{country}/{stateProvince}")]

GetByState and GetByCounty work fine. However, when trying to get by postal code in the function app, the function GetByCounty is being executed. The Web API app is honoring the static text "postalcodes" in the route. The functions app is not. It sees four levels in the path and picks the first one alphabetically which happens to be GetByCounty.

Other than adding an unnecessary path to get the number of levels to be unique, is there a way to make this work so the correct HTTP trigger is executed mimicking the behavior of standard Web APIs running in an app service?

  • Changing GetByPostalCode to "hickory/postalcodes/{country}/{postalcode} works
  • Changing GetByPostalCode to "postalcodes/history/{country}/{postalcode} works
  • Adding an additional path level "history/postalcodes/countries/{country}/{postalcode} works

But this breaks the API in terms of compatibility which isn't a good thing.

Azure Web Apps
Azure Web Apps
A feature of Azure App Service used to create and deploy scalable, mission-critical web apps.
4,305 questions
Azure Functions
Azure Functions
An Azure service that provides an event-driven serverless compute platform.
2,575 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
6,918 questions
No comments
{count} votes

Accepted answer
  1. answered 2022-01-21T10:23:11.09+00:00
    Pramod Valavala 14,541 Reputation points Microsoft Employee

    @Jon Krupa The default order is the order in which functions are registered, which is done by enumerating directories containing a function.json file making it lexicographically sorting all functions. A dirty fix seems to be adding prefixes to function names like below

       public class RouteSpecificity  
       {  
           [FunctionName("P00_" + nameof(GetDemoProduct))]  
           public IActionResult GetDemoProduct(  
               [HttpTrigger(AuthorizationLevel.Function, "get", Route = "test/demo/{prod}")] HttpRequest req,  
               ILogger log  
           ) =>  new OkObjectResult("Getting Demo Product");  
         
           [FunctionName("P01_" + nameof(GetProduct))]  
           public IActionResult GetProduct(  
               [HttpTrigger(AuthorizationLevel.Function, "get", Route = "test/{cat}/{prod}")] HttpRequest req,  
               ILogger log  
           ) =>  new OkObjectResult("Getting Product");  
       }  
    

    There is an open issue that tracks this and has a mention of a community solution to tackle this.


1 additional answer

Sort by: Most helpful
  1. answered 2022-01-21T10:02:40.377+00:00
    Ken Tucker 5,796 Reputation points

    Not that this is a great solution but you could always check if the country is postalcodes and return the results from GetByPostalCode instead of GetByCountry because they both return an HttpResponseData