3

Greatings,

I have a problem with my asp.net mvc4 research project. I've tried to create apicontroller with multiple get actions and some of them with parameters, others - without.

my api

public class ContentController : ApiController
{
    [System.Web.Http.HttpGet]
    public HttpResponseMessage Categories1()
    {
        return Request.CreateResponse(HttpStatusCode.OK, new string[] { "Toys", "Games", "Animals", "Woob-Woob" }, Configuration.Formatters.JsonFormatter);
    }

    [System.Web.Http.HttpGet]
    public HttpResponseMessage Categories2()
    {
        return Request.CreateResponse(HttpStatusCode.OK, new string[] { "Toys", "Games", "Animals", "Woob-Woob" }, Configuration.Formatters.JsonFormatter);
    }

    [System.Web.Http.HttpGet]
    public HttpResponseMessage Categories3(int i)
    {
        return Request.CreateResponse(HttpStatusCode.OK, new string[] { "Toys", "Games", "Animals", "Woob-Woob" }, Configuration.Formatters.JsonFormatter);
    }

    [System.Web.Http.HttpGet]
    public HttpResponseMessage Categories4(int i)
    {
        return Request.CreateResponse(HttpStatusCode.OK, new string[] { "Toys", "Games", "Animals", "Woob-Woob" }, Configuration.Formatters.JsonFormatter);
    }
}

my apiConfig:

    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "ApiByActionWithId",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        config.Routes.MapHttpRoute(
            name: "ApiByAction",
            routeTemplate: "api/{controller}/{action}"
        );

        config.Routes.MapHttpRoute(
            name: "ApiByName",
            routeTemplate: "api/{controller}/{action}/{name}",
            defaults: null
        );

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

and the result of this

for /api/content/categories1 the result is ["Toys","Games","Animals","Woob-Woob"]

for /api/content/categories3 and /api/content/categories3/1 result - "No HTTP resource was found that matches the request URI 'http:// localhost:51200/api/content/categories3'."

I have no idea how it should be handled

1 Answer 1

1

I think it is failing because of the mismatch between the name of the parameter in the api actions and the name of the parameter in the route.

Change your first route:

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

to the following:

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

See if that works.

In general, more specific routes should be at the top of your Register method. With more generic routes being at the bottom as catch-alls.

Consider a controller like the following:

public class UserSettingsController : ApiController
{
    [HttpGet]
    public HttpResponseMessage GetAll();
    [HttpGet]
    public HttpResponseMessage Get(string key);
    [HttpGet]
    public HttpResponseMessage Set(string key, string value);
    [HttpGet]
    public HttpResponseMessage Restore(string key);
}

To setup the routes, it would like the routes below. Notice that they are ordered so that the GetAll method will be hit if there is no key on the api call. If the first 2 routes were switched, then the GetAll version would never be hit, because a call to /api/settings would go to the Get method and the key would just be set to null.

        config.Routes.MapHttpRoute(
              name: "user/settings",
              routeTemplate: "user/settings",
              defaults: new { controller = "UserSettings", action = "GetAll" },
              constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) }
        );

        config.Routes.MapHttpRoute(
              name: "user/settings/{key} (get)",
              routeTemplate: "user/settings/{key}",
              defaults: new { controller = "UserSettings", action = "Get" },
              constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) }
        );

        config.Routes.MapHttpRoute(
              name: "user/settings/{key} (set)",
              routeTemplate: "user/settings/{key}",
              defaults: new { controller = "UserSettings", action = "Set" },
              constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Put) }
        );

        config.Routes.MapHttpRoute(
              name: "user/settings/{key}/restore",
              routeTemplate: "user/settings/{key}/restore",
              defaults: new { controller = "UserSettings", action = "Restore" },
              constraints: new { httpMethod = new HttpMethodConstraint(HttpMethod.Post) }
        );
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for your help, rclement, it works. But I still don't know how to map route if I will have 20-30 methods in apicontroller with very different arguments and names.
When you are creating your mappings, you can remove that template parameters like {controller} or {action} and specify specific actions if you want. You can also specify different lists of parameters. I will update the answer with a few examples.

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.