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

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

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 Functions
Azure Functions
An Azure service that provides an event-driven serverless compute platform.
4,248 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.
10,234 questions
Azure App Service
Azure App Service
Azure App Service is a service used to create and deploy scalable, mission-critical web apps.
6,865 questions
0 comments No comments
{count} votes

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

    @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. Ken Tucker 5,846 Reputation points
    2022-01-21T10:02:40.377+00:00

    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