0

I'm trying to add a new Web API in a legacy web with VB.NET and .NET Framework 4.8. It has multiple Web API running correctly.

For example - CalendarController.vb:

<RoutePrefix("api/{Controller}")>
Public Class CalendarController
    Inherits ApiController

    <HttpGet>
    <Route("{id:int}")>
    Public Function GetCalendarEvent(id As Integer) As IHttpActionResult
        ...
        Return Ok(...)
    End Function
End Class

StockController.vb:

<RoutePrefix("api/{Controller}")>
Public Class StockController
    Inherits ApiController

    <HttpGet>
    <Route("{friendlyURL}")>
    Public Function GetProductStock(friendlyURL As String) As IHttpActionResult
        ...
        Return Ok(...)
    End Function
End Class

Global.asax.vb:

Sub Application_Start(sender As Object, e As EventArgs)
    With RouteTable.Routes
        .MapHttpRoute(name:="Calendar.GetNextCalendarEvents", routeTemplate:="api/{Controller}/Next/{maxEvents}")
        .MapHttpRoute(name:="Calendar.GetMonthEntries", routeTemplate:="api/{Controller}/{month}/{year}")
        .MapHttpRoute(name:="Calendar.GetCalendarEvent", routeTemplate:="api/{Controller}/{id}")
        .MapHttpRoute(name:="Map.GetMarkerById", routeTemplate:="api/{Controller}/{markerId:int}")
        .MapHttpRoute(name:="Map.GetAllMarkers", routeTemplate:="api/{Controller}")
        .MapHttpRoute(name:="Stock.GetProductStock", routeTemplate:="api/{Controller}/{friendlyURL}")
        '.MapHttpRoute(name:="Stock.GetProductStock", routeTemplate:="api/{Controller}/{friendlyURL}", defaults:=Nothing, constraints:=New With {.friendlyURL = "^[a-zA-Z]+[a-zA-Z0-9,_ -]*$"})
    End With
End Sub

Testing both APIs:

const response = await fetch(`/api/Calendar/3050`);
const event = await response.json();
console.log(event);
//Returns 200 with an object correctly
{ id: 3050, title: '...', ... }

const response = await fetch(`/api/Stock/my-product`);
const product = await response.json();
console.log(product);

//Cannot access the API
GET https://localhost:44330/api/Stock/my-product 404 (Not Found)

{ Message: "No HTTP resource was found that matches the request  'https://localhost:44330/api/Stock/my-product'.", 
  MessageDetail: "No action was found on the controller 'Stock' that matches the request." }

const response = await fetch(`/api/Stock?friendlyURL=my-product`);
//Returns 200 Http Code and correct stock.

I cannot find out the differences.

EDIT: It runs when parameter is passed as query string

Thank you!

5
  • What exactly is the question here? What are you trying to achieve? What is not working? Commented Jul 1 at 16:58
  • Calendar API returns a result correctly. Stock API is not accessible (404), but everything seems to be configured correctly. GetProductStock doesn't execute, so there's no error in it. The only difference is the type of parameter: id is an integer, friendlyURL is a string. Commented Jul 1 at 18:28
  • Okay. I am not seeing anything super strange. Only thing that hit me is that the request might fall to previously defined api/{Controller}/{Action}/{id?} and it tries to look for "my-product" action on StockController. Can you edit the question and add there every route table registration? Commented Jul 1 at 20:59
  • Edited. Those are all the routes Commented Jul 3 at 6:35
  • When it's used as a query string, it runs. So must be an error in routes Commented Jul 3 at 12:08

1 Answer 1

1

I believe the request isn't getting to correct route registration. In the code below is a comment abiv ethe line I suspect is trying to route the request.

Sub Application_Start(sender As Object, e As EventArgs)
    With RouteTable.Routes
        .MapHttpRoute(name:="Calendar.GetNextCalendarEvents", routeTemplate:="api/{Controller}/Next/{maxEvents}")
        .MapHttpRoute(name:="Calendar.GetMonthEntries", routeTemplate:="api/{Controller}/{month}/{year}")
        ' I'd guess this registration is trying to route the request
        .MapHttpRoute(name:="Calendar.GetCalendarEvent", routeTemplate:="api/{Controller}/{id}")
        .MapHttpRoute(name:="Map.GetMarkerById", routeTemplate:="api/{Controller}/{markerId:int}")
        .MapHttpRoute(name:="Map.GetAllMarkers", routeTemplate:="api/{Controller}")
        .MapHttpRoute(name:="Stock.GetProductStock", routeTemplate:="api/{Controller}/{friendlyURL}")
        '.MapHttpRoute(name:="Stock.GetProductStock", routeTemplate:="api/{Controller}/{friendlyURL}", defaults:=Nothing, constraints:=New With {.friendlyURL = "^[a-zA-Z]+[a-zA-Z0-9,_ -]*$"})
    End With
End Sub

The way to make it work is to order route registrations from the most specific to most general. I would personally leverage of default values and explicit route values instead of using {Controller} replacement for specific route templates.

Sub Application_Start(sender As Object, e As EventArgs)
    With RouteTable.Routes
        .MapHttpRoute(name:="Calendar.GetNextCalendarEvents", routeTemplate:="api/Calendar/Next/{maxEvents}", defaults:=New With {.controller = "Calendar", .action = "GetNextCalendarEvents")
        .MapHttpRoute(name:="Calendar.GetMonthEntries", routeTemplate:="api/Calendar/{month}/{year}", defaults:=New With {.controller = "Calendar", .action = "GetMonthEntries"))
        .MapHttpRoute(name:="Map.GetAllMarkers", routeTemplate:="api/Map", defaults:=New With {.controller = "Map", .action = "GetAllMarkers"))
        .MapHttpRoute(name:="Map.GetMarkerById", routeTemplate:="api/Map/{markerId:int}", defaults:=New With {.controller = "Map", .action = "GetMarkerById"))
        .MapHttpRoute(name:="Stock.GetProductStock", routeTemplate:="api/Stock/{friendlyURL}", defaults:=New With {.controller = "Stock", .action = "GetProductStock"))
        ' Here I see eventual problem when a controller will have two actions with parameter id. It will throw an exception as it wouldn't know which one to choose
        .MapHttpRoute(name:="Default", routeTemplate:="api/{Controller}/{id}")
    End With
End Sub

I think this will do the trick. I would consider reviewing ASP.NET MVC Routing Overview (VB) and maybe adding default route from the overview.

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

2 Comments

Thank you, stackoverflow.com/users/8442437/sramekpete. It's probably a route conflict. I used the same method I used to add the other routes. It's not the solution, but I've changed the order and Stock API runs. I'm gonna edit the question to add the solution.
Glad you could figure out the problem!

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.