Question

I am setting up an ASP.NET MVC 4 Web API to accept requests from a 3rd party server, and I simply can't figure out how to set the route mappings. Assume the 3rd party server expects to get responses for:

  1. http://[my_server]/authorize?user=[user name]&session=[session token]&item=[item]
  2. http://[my_server]/release?user=[user name]&session=[session token]

Alternatively, the requests can use a dedicated path, i.e.:

  1. http://[my_server]/***api***/authorize?user=[user name]&session=[session token]&item=[item]
  2. http://[my_server]/***api***/release?user=[user name]&session=[session token]

I would like to be able to support both alternatives.

Additional requests, following the more traditional /controller/id form, should be implemented too, but I'd like to focus on the above (I'm not even sure that Web API is the way to go here).

I have written the following controller:

public class MyController : ApiController
{
    [HttpGet]
    [ActionName("authorize")]
    public string Authorize(string user, string session, string item)
    {
        ...
        // return "OK" or "DENY";
    }

    [HttpGet]
    [ActionName("release")]
    public string Release(string user, string session)
    {
        ...
        return "OK";
    }
}

and tried everything I could find in SO and elsewhere in WebAppConfig.Register, but I keep getting a 404 error when I try the request in the browser:

http://localhost:22332/api/authorize?user=ury&session=token&item=an_item

My question is, what do I have to do - specifically in WebAppConfig.Register and in the controller - in order to serve the above requests (assuming my test URL is correct...)?

Was it helpful?

Solution 2

After a few hours working on this and with a lot of help from the Route Debugger - Thanks Phil Haack! - I've found both the problem and the solution.

The problem: Route mapping matching is ordered, and RouteTable.Routes, from which the app's route mapping is initialized, contains quite a few of them. The request pattern I was looking for also matched some of these mappings ("authorize" was matched as a controller, for example).

The solution: add "my" route mappings before the default mappings.

Yeah, right...

Since most operations are not supported on HttpRouteCollection, the resulting code is a bit ugly, but it works:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        routesCopy = new RouteBase[RouteTable.Routes.Count];

        RouteTable.Routes.CopyTo(routesCopy, 0);

        config.Routes.Clear();
        config.Routes.MapHttpRoute(name: "AuthorizeWebApiRoute", routeTemplate: "authorize", defaults: new { controller = "My", action = "authorize" });
        config.Routes.MapHttpRoute(name: "ReleaseWebApiRoute", routeTemplate: "release", defaults: new { controller = "My", action = "release" });

        foreach (var route in routesCopy)
        {
            RouteTable.Routes.Add(route);
        }
    }
}

As long as "my" route mappings don't match the default mappings (I made them specific enough not to), I'm all good - I think...

OTHER TIPS

You're getting tripped up by Web API conventions for matching a controller name in a URL to a controller class.

If the name of your controller is "MyController", then the URL to request is:

http://localhost:22332/api/my/authorize?user=ury&session=token&item=an_item

To support a request w/o the "api" bit in the URL, simply add a second route definition in the Register method of WebApiConfig.cs. Since you're also using "actions" in your URL's, you'd need these two routes:

    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{action}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );

    config.Routes.MapHttpRoute(
        name: "RootWebApiRoute",
        routeTemplate: "{controller}/{action}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );

If you want to omit the "api" prefix as well as the controller parameter, I think you need a different route definition. Something like:

    public static void Register(HttpConfiguration config) {
        config.Routes.MapHttpRoute(
            name: "ActionOnlyRoute",
            routeTemplate: "{action}",
            defaults: new { controller = "My" }
        );
    }

Here, we'll only look for an "action" in the URL and route everything to your "MyController" class.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top