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

Dans Web API, 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 cartographier des méthodes individuelles, il était très simple d’obtenir la bonne demande au bon endroit. Pour une classe similaire qui ne disposait que d’une seule méthode GET mais qui avait également un paramètre Object , j’ai utilisé IActionValueBinder avec succès. 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 substituant la méthode ExecuteAsync d’ ApiController mais sans ApiController jusqu’à présent. Des conseils sur cette question?

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

C’est le meilleur moyen que j’ai trouvé pour prendre en charge des méthodes GET supplémentaires et prendre également en charge les méthodes REST normales. Ajoutez les itinéraires suivants à 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 réussi à bash chaque méthode dans mon contrôleur ci-dessous:

 public class TestController : ApiController { public ssortingng Get() { return ssortingng.Empty; } public ssortingng Get(int id) { return ssortingng.Empty; } public ssortingng GetAll() { return ssortingng.Empty; } public void Post([FromBody]ssortingng value) { } public void Put(int id, [FromBody]ssortingng value) { } public void Delete(int id) { } } 

J’ai vérifié qu’il prend en charge les requêtes suivantes:

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

Remarque Si vos actions GET supplémentaires ne commencent pas par “Get”, vous pouvez append un atsortingbut HttpGet à la méthode.

Allez de ceci:

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

Pour ça:

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

Par conséquent, vous pouvez maintenant spécifier à quelle action (méthode) vous souhaitez envoyer votre requête HTTP.

invocation à “http: // localhost: 8383 / api / Command / PostCreateUser” appelle:

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

et en publiant dans “http: // localhost: 8383 / api / Command / PostMakeBooking” invoque:

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

J’ai essayé ceci dans une application de service d’API WEB auto-hébergée et cela fonctionne comme un charme 🙂

Je trouve les atsortingbuts plus propres à utiliser que de les append manuellement via du 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 en avez également besoin 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

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 } ); 

Je ne sais pas si vous avez trouvé la réponse, mais je l’ai fait et ça marche

 public IEnumerable Get() { return new ssortingng[] { "value1", "value2" }; } // GET /api/values/5 public ssortingng Get(int id) { return "value"; } // GET /api/values/5 [HttpGet] public ssortingng 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 } ); 

Avez-vous essayé de passer à WebInvokeAtsortingbute et de définir la méthode sur “GET”?

Je crois que j’ai eu un problème similaire et que je suis passé à dire explicitement quelle méthode (GET / PUT / POST / DELETE) est attendue sur la plupart de mes méthodes, sinon toutes.

 public class SomeController : ApiController { [WebInvoke(UriTemplate = "{itemSource}/Items"), Method="GET"] public SomeValue GetItems(CustomParam parameter) { ... } [WebInvoke(UriTemplate = "{itemSource}/Items/{parent}", Method = "GET")] public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... } } 

Le WebGet devrait le gérer, mais j’ai vu qu’il y avait des problèmes avec plusieurs Get get less multiple Get du même type de retour.

[Modifier: rien de tout cela n’est valable avec le coucher de soleil de WCF WebAPI et la migration vers ASP.Net WebAPI sur la stack MVC]

Avec le nouveau Web Api 2, il est devenu plus facile d’avoir plusieurs méthodes d’obtention.

Si le paramètre transmis aux méthodes GET est suffisamment différent pour que le système de routage d’atsortingbuts puisse distinguer leurs types, comme c’est le cas avec int s et Guid s, vous pouvez spécifier le type attendu dans l’atsortingbut [Route...]

Par exemple –

 [RoutePrefix("api/values")] public class ValuesController : ApiController { // GET api/values/7 [Route("{id:int}")] public ssortingng Get(int id) { return $"You entered an int - {id}"; } // GET api/values/AAC1FB7B-978B-4C39-A90D-271A031BFE5D [Route("{id:Guid}")] public ssortingng Get(Guid id) { return $"You entered a GUID - {id}"; } } 

Pour plus de détails sur cette approche, voir ici http://nodogmablog.bryanhogan.net/2017/02/web-api-2-controller-with-multiple-get-methods-part-2/

Une autre option consiste à donner aux méthodes GET des itinéraires différents.

  [RoutePrefix("api/values")] public class ValuesController : ApiController { public ssortingng Get() { return "simple get"; } [Route("geta")] public ssortingng GetA() { return "A"; } [Route("getb")] public ssortingng GetB() { return "B"; } } 

Voir ici pour plus de détails – http://nodogmablog.bryanhogan.net/2016/10/web-api-2-controller-with-multiple-get-methods/

J’essayais d’utiliser le routage d’atsortingbut Web Api 2 pour autoriser plusieurs méthodes Get, et j’avais incorporé les suggestions utiles des réponses précédentes, mais dans le Controller, j’avais seulement décoré la méthode “spéciale” (exemple):

 [Route( "special/{id}" )] public IHttpActionResult GetSomethingSpecial( ssortingng id ) { 

… sans placer également un [RoutePrefix] en haut du Controller:

 [RoutePrefix("api/values")] public class ValuesController : ApiController 

Je recevais des erreurs en indiquant qu’aucune route ne correspondait à l’URI soumise. Une fois que j’ai eu à la fois le [Route] décorant la méthode et [RoutePrefix] décorant le contrôleur dans son ensemble, cela a fonctionné.

Dans ASP.NET Core 2.0, vous pouvez append un atsortingbut Route au contrôleur:

 [Route("api/[controller]/[action]")] public class SomeController : Controller { public SomeValue GetItems(CustomParam parameter) { ... } public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... } } 

Aucun des exemples ci-dessus n’a fonctionné pour mes besoins personnels. Ce qui suit est ce que j’ai fini par faire.

  public class ContainsConstraint : IHttpRouteConstraint { public ssortingng[] array { get; set; } public bool match { get; set; } ///  /// Check if param contains any of values listed in array. ///  /// The param to test. /// The items to compare against. /// Whether we are matching or NOT matching. public ContainsConstraint(ssortingng[] array, bool match) { this.array = array; this.match = match; } public bool Match(System.Net.Http.HttpRequestMessage request, IHttpRoute route, ssortingng parameterName, IDictionary values, HttpRouteDirection routeDirection) { if (values == null) // shouldn't ever hit this. return true; if (!values.ContainsKey(parameterName)) // make sure the parameter is there. return true; if (ssortingng.IsNullOrEmpty(values[parameterName].ToSsortingng())) // if the param key is empty in this case "action" add the method so it doesn't hit other methods like "GetStatus" values[parameterName] = request.Method.ToSsortingng(); bool contains = array.Contains(values[parameterName]); // this is an extension but all we are doing here is check if ssortingng array contains value you can create exten like this or use LINQ or whatever u like. if (contains == match) // checking if we want it to match or we don't want it to match return true; return false; } 

Pour utiliser ce qui précède dans votre itinéraire, utilisez:

 config.Routes.MapHttpRoute("Default", "{controller}/{action}/{id}", new { action = RouteParameter.Optional, id = RouteParameter.Optional}, new { action = new ContainsConstraint( new ssortingng[] { "GET", "PUT", "DELETE", "POST" }, true) }); 

Ce qui se passe est la sorte de fake de contrainte dans la méthode afin que cette route ne corresponde qu’aux méthodes GET, POST, PUT et DELETE par défaut. Le “vrai” dit que nous voulons vérifier la correspondance des éléments dans le tableau. Si vous avez dit faux, excluez ceux du str. Vous pouvez alors utiliser des routes au-dessus de cette méthode par défaut comme:

 config.Routes.MapHttpRoute("GetStatus", "{controller}/status/{status}", new { action = "GetStatus" }); 

Dans ce qui précède, il cherche essentiellement l’URL suivante => http://www.domain.com/Account/Status/Active ou quelque chose comme ça.

Au-delà de ce qui précède, je ne suis pas sûr que je devienne trop fou. À la fin de la journée, cela devrait être par ressource. Mais je vois un besoin de cartographier les URL conviviales pour diverses raisons. Je suis certain que le Web Api évoluera avec une sorte de provision. Si le temps je vais construire une solution plus permanente et poster.

Je ne pouvais pas faire fonctionner une des solutions de routage ci-dessus – une partie de la syntaxe semble avoir changé et je suis toujours nouveau sur MVC – en un clin d’œil, j’ai assemblé ce hack vraiment horrible (et simple) pour l’instant – notez que cela remplace la méthode “public MyObject GetMyObjects (long id)” – nous changeons le type “id” en une chaîne et changeons le type de retour en object.

 // GET api/MyObjects/5 // GET api/MyObjects/function public object GetMyObjects(ssortingng id) { id = (id ?? "").Trim(); // Check to see if "id" is equal to a "command" we support // and return alternate data. if (ssortingng.Equals(id, "count", SsortingngComparison.OrdinalIgnoreCase)) { return db.MyObjects.LongCount(); } // We now return you back to your regularly scheduled // web service handler (more or less) var myObject = db.MyObjects.Find(long.Parse(id)); if (myObject == null) { throw new HttpResponseException ( Request.CreateResponse(HttpStatusCode.NotFound) ); } return myObject; } 

Si vous avez plusieurs actions dans le même fichier, transmettez le même argument, par exemple Id à toutes les actions. C’est parce que l’action seulement peut identifier Id, donc au lieu de donner n’importe quel nom à l’argument, déclarez seulement Id comme ceci.


 [httpget] [ActionName("firstAction")] firstAction(ssortingng Id) {..... ..... } [httpget] [ActionName("secondAction")] secondAction(Int Id) {..... ..... } //Now go to webroute.config file under App-start folder and add following routes.MapHttpRoute( name: "firstAction", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } ); routes.MapHttpRoute( name: "secondAction", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } ); 

Alternative simple

Utilisez simplement une chaîne de requête.

Le routage

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

Manette

 public class TestController : ApiController { public IEnumerable Get() { } public SomeViewModel GetById(int objectId) { } } 

Demandes

 GET /Test GET /Test?objectId=1 

Remarque

Gardez à l’esprit que le paramètre de la chaîne de requête ne doit pas être “id” ou quel que soit le paramètre dans l’itinéraire configuré.

Modifiez le WebApiConfig et ajoutez à la fin un autre Routes.MapHttpRoute comme ceci:

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

Créez ensuite un contrôleur comme celui-ci:

 public class ServiceController : ApiController { [HttpGet] public ssortingng Get(int id) { return "object of id id"; } [HttpGet] public IQueryable DropDowEmpresa() { return db.Empresa.Where(x => x.Activo == true).Select(y => new DropDownModel { Id = y.Id, Value = y.Nombre, }); } [HttpGet] public IQueryable DropDowTipoContacto() { return db.TipoContacto.Select(y => new DropDownModel { Id = y.Id, Value = y.Nombre, }); } [HttpGet] public ssortingng FindProductsByName() { return "FindProductsByName"; } } 

C’est comme ça que je l’ai résolu. J’espère que ça va aider quelqu’un.