198 votes

Contrôleur unique avec plusieurs méthodes GET dans ASP.NET Web API

Dans l'API Web, j'avais une classe de structure similaire :

public class SomeController : ApiController
{
    [WebGet(UriTemplate = "{itemSource}/Items")]
    public SomeValue GetItems(CustomParam parameter) { ... }

    [WebGet(UriTemplate = "{itemSource}/Items/{parent}")]
    public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... }
}

Comme nous pouvions mapper les méthodes individuelles, il était très simple d'obtenir la bonne requête au bon endroit. Pour une classe similaire qui n'avait qu'une seule GET mais dispose également d'une méthode Object j'ai utilisé avec succès le paramètre IActionValueBinder . Cependant, dans le cas décrit ci-dessus, j'obtiens l'erreur suivante :

Multiple actions were found that match the request: 

SomeValue GetItems(CustomParam parameter) on type SomeType

SomeValue GetChildItems(CustomParam parameter, SomeObject parent) on type SomeType

J'essaie d'aborder ce problème en surchargeant la fonction ExecuteAsync méthode de ApiController mais sans succès jusqu'à présent. Un conseil sur ce problème ?

Edit : J'ai oublié de mentionner que j'essaie maintenant de déplacer ce code sur ASP.NET Web API qui a une approche différente du routage. La question est la suivante : comment faire fonctionner le code sur ASP.NET Web API ?

1 votes

Avez-vous toujours le {parent} comme RouteParameter.Optional ?

0 votes

Oui, je l'ai fait. Peut-être que j'utilise IActionValueBinder de la mauvaise façon, car pour des types tels que int id (comme dans la démo), cela fonctionne bien.

0 votes

Désolé, j'aurais dû être plus clair. J'aurais pensé que le fait qu'il soit facultatif signifiait qu'il correspondait à la route des éléments ainsi qu'à celle des sous-éléments, ce qui expliquerait le message d'erreur que vous voyez.

270voto

sky-dev Points 1990

C'est le meilleur moyen que j'ai trouvé pour prendre en charge les méthodes GET supplémentaires et les méthodes REST normales. Ajoutez les routes suivantes à votre WebApiConfig :

routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id = RouteParameter.Optional }, new { id = @"\d+" });
routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");
routes.MapHttpRoute("DefaultApiGet", "Api/{controller}", new { action = "Get" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
routes.MapHttpRoute("DefaultApiPost", "Api/{controller}", new {action = "Post"}, new {httpMethod = new HttpMethodConstraint(HttpMethod.Post)});

J'ai vérifié cette solution avec la classe de test ci-dessous. J'ai été capable de frapper avec succès chaque méthode dans mon contrôleur ci-dessous :

public class TestController : ApiController
{
    public string Get()
    {
        return string.Empty;
    }

    public string Get(int id)
    {
        return string.Empty;
    }

    public string GetAll()
    {
        return string.Empty;
    }

    public void Post([FromBody]string value)
    {
    }

    public void Put(int id, [FromBody]string value)
    {
    }

    public void Delete(int id)
    {
    }
}

J'ai vérifié qu'il prend en charge les demandes suivantes :

GET /Test
GET /Test/1
GET /Test/GetAll
POST /Test
PUT /Test/1
DELETE /Test/1

Note Si vos actions GET supplémentaires ne commencent pas par "Get", vous pouvez ajouter un attribut HttpGet à la méthode.

4 votes

Cette réponse est excellente et m'a beaucoup aidé pour une autre question connexe. Merci !

4 votes

J'ai essayé ceci -- cela ne semble pas fonctionner. Les routes sont toutes mappées de façon aléatoire à la méthode GetBlah(long id) :(

1 votes

@BrainSlugs83 : Cela dépend de l'ordre. Et vous voudrez ajouter (aux méthodes "withId"), un constraints: new{id=@"\d+"}

66voto

uggeh Points 242

Partez de là :

config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}",
            new { id = RouteParameter.Optional });

A ceci :

config.Routes.MapHttpRoute("API Default", "api/{controller}/{action}/{id}",
            new { id = RouteParameter.Optional });

Ainsi, vous pouvez maintenant spécifier à quelle action (méthode) vous voulez envoyer votre requête HTTP.

l'affectation à "http://localhost:8383/api/Command/PostCreateUser" invoque :

public bool PostCreateUser(CreateUserCommand command)
{
    //* ... *//
    return true;
}

et le poster sur "http://localhost:8383/api/Command/PostMakeBooking" invoque :

public bool PostMakeBooking(MakeBookingCommand command)
{
    //* ... *//
    return true;
}

J'ai essayé cela dans une application de service API WEB auto-hébergée et cela fonctionne comme un charme :)

8 votes

Merci pour cette réponse utile. J'aimerais ajouter que si vous commencez vos noms de méthodes par Get, Post, etc., vos requêtes seront dirigées vers ces méthodes en fonction du verbe HTTP utilisé. Mais vous pouvez aussi donner n'importe quel nom à vos méthodes, puis les décorer avec l'attribut [HttpGet] , [HttpPost] etc. pour associer le verbe à la méthode.

0 votes

Veuillez consulter mon pregunta

0 votes

@DikaArtaKarunia pas de problème, content que ma réponse soit toujours applicable 6 ans plus tard :D

41voto

Kalel Wade Points 1006

Je trouve que les attributs sont plus propres à utiliser que de les ajouter manuellement via le code. Voici un exemple simple.

[RoutePrefix("api/example")]
public class ExampleController : ApiController
{
    [HttpGet]
    [Route("get1/{param1}")] //   /api/example/get1/1?param2=4
    public IHttpActionResult Get(int param1, int param2)
    {
        Object example = null;
        return Ok(example);
    }

}

Vous avez également besoin de ceci dans votre webapiconfig

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

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

Quelques bons liens http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api Celui-ci explique mieux le routage. http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api

3 votes

Je devais aussi ajouter config.MapHttpAttributeRoutes(); à mon WebApiConfig.cs y GlobalConfiguration.Configuration.EnsureInitialized(); à la fin de mon WebApiApplication.Application_Start() pour que les attributs de l'itinéraire fonctionnent.

0 votes

@Ergwun Ce commentaire m'a beaucoup aidé. Juste pour le compléter, config.MapHttpAttributeRoutes(); doit apparaître avant le mappage de l'itinéraire (par exemple avant config.Routes.MappHttpRoute(... .

12voto

Alexander Zeitler Points 2141

Vous devez définir d'autres routes dans global.asax.cs comme ceci :

routes.MapHttpRoute(
    name: "Api with action",
    routeTemplate: "api/{controller}/{action}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

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

5 votes

Oui, c'est vrai, mais ce serait bien de voir un exemple de ces routes. Cela rendrait cette réponse plus précieuse pour la communauté. (et vous obtiendriez un +1 de ma part :)

0 votes

Vous pouvez lire un exemple ici - stackoverflow.com/questions/11407267/

2 votes

Une solution réelle aurait été plus agréable.

3voto

Pavan Josyula Points 420

Je ne suis pas sûr que vous ayez trouvé la réponse, mais j'ai fait ceci et ça marche.

public IEnumerable<string> Get()
{
    return new string[] { "value1", "value2" };
}

// GET /api/values/5
public string Get(int id)
{
    return "value";
}

// GET /api/values/5
[HttpGet]
public string GetByFamily()
{
    return "Family value";
}

Maintenant dans global.asx

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

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

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

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X