web-dev-qa-db-fra.com

Noms de méthodes personnalisées dans l'API Web ASP.NET

Je convertis de l'API Web WCF à la nouvelle API Web ASP.NET MVC 4. J'ai un UsersController et je veux une méthode nommée Authenticate. Je vois des exemples de procédures à suivre pour GetAll, GetOne, Post et Delete, mais que se passe-t-il si je souhaite ajouter des méthodes supplémentaires à ces services? Par exemple, mon service Utilisateurs doit avoir une méthode appelée Authentifier où ils transmettent un nom d'utilisateur et un mot de passe, mais cela ne fonctionne pas.

public class UsersController : BaseApiController
{
    public string GetAll()
    {
        return "getall!";
    }

    public string Get(int id)
    {
        return "get 1! " + id;
    }

    public User GetAuthenticate(string userName, string password, string applicationName)
    {
        LogWriter.Write(String.Format("Received authenticate request for username {0} and password {1} and application {2}",
            userName, password, applicationName));

        //check if valid leapfrog login.
        var decodedUsername = userName.Replace("%40", "@");
        var encodedPassword = password.Length > 0 ? Utility.HashString(password) : String.Empty;
        var leapFrogUsers = LeapFrogUserData.FindAll(decodedUsername, encodedPassword);

        if (leapFrogUsers.Count > 0)
        {
            return new User
            {
                Id = (uint)leapFrogUsers[0].Id,
                Guid = leapFrogUsers[0].Guid
            };
        }
        else
            throw new HttpResponseException("Invalid login credentials");
    }
}

Je peux naviguer vers myapi/api/users/et il appellera GetAll et je pourrai accéder à myapi/api/users/1 et il s'appellera Get, mais si j'appelle myapi/api/users/authenticate? Username = {0} & password = {1} alors il appellera Get (NOT Authenticate) et erreur:

Le dictionnaire de paramètres contient une entrée NULL pour le paramètre 'id' de type 'System.Int32' non nullable pour la méthode 'System.String Get (Int32)' dans 'Navtrak.Services.WCF.NavtrakAPI.Controllers.UsersController'. Un paramètre facultatif doit être un type de référence, un type nullable ou déclaré en tant que paramètre facultatif.

Comment puis-je appeler des noms de méthode personnalisés tels que Authentifier?

107
Justin

Par défaut, la configuration de la route suit les conventions RESTFul, ce qui signifie qu’elle acceptera uniquement les noms d’action Get, Post, Put et Delete (regardez la route dans global.asax =>. Par défaut, elle ne vous permet pas de spécifier un nom d’action => il utilise le verbe HTTP pour expédier). Ainsi, lorsque vous envoyez une demande GET à /api/users/authenticate, vous appelez essentiellement l'action Get(int id) et transmettez id=authenticate qui se bloque de manière évidente, car votre action Get attend un entier.

Si vous voulez avoir des noms d'action différents des noms standard, vous pouvez modifier la définition de votre route dans global.asax:

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

Vous pouvez maintenant accéder à /api/values/getauthenticate pour authentifier l'utilisateur.

127
Darin Dimitrov

C’est la meilleure méthode que j’ai proposée jusqu’à présent pour incorporer des méthodes GET supplémentaires tout en prenant en charge les méthodes normales REST. 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 à frapper 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 Que si vos actions GET supplémentaires ne commencent pas par 'Get', vous voudrez peut-être ajouter un attribut HttpGet à la méthode.

86
sky-dev

Je suis dans le monde MVC4.

Pour ce que ça vaut, j'ai un SiteAPIController, et j'avais besoin d'une méthode personnalisée, qui pourrait s'appeler:

http://localhost:9000/api/SitesAPI/Disposition/0

Avec des valeurs différentes pour le dernier paramètre, obtenir un enregistrement avec différentes dispositions.

Ce qui a finalement fonctionné pour moi a été:

La méthode dans SitesAPIController:

// GET api/SitesAPI/Disposition/1
[ActionName("Disposition")]
[HttpGet]
public Site Disposition(int disposition)
{
    Site site = db.Sites.Where(s => s.Disposition == disposition).First();
    return site;
}

Et cela dans le WebApiConfig.cs

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

// this i added
config.Routes.MapHttpRoute(
    name: "Action",
    routeTemplate: "api/{controller}/{action}/{disposition}"
 );

Aussi longtemps que je nommais la {disposition} comme {id}, je rencontrais:

{
"Message": "No HTTP resource was found that matches the request URI 'http://localhost:9000/api/SitesAPI/Disposition/0'.",
"MessageDetail": "No action was found on the controller 'SitesAPI' that matches the request."
}

Lorsque je l'ai renommé en {disposition}, il a commencé à fonctionner. Donc, apparemment, le nom du paramètre correspond à la valeur dans l'espace réservé.

N'hésitez pas à éditer cette réponse pour la rendre plus précise/explicative.

19
Kinjal Dixit

Par défaut, Web Api s'attend à ce que l'URL sous la forme api/{controller}/{id} remplace le routage par défaut. vous pouvez définir le routage de deux manières différentes.

Première option:

Ajouter l'enregistrement de la route ci-dessous dans WebApiConfig.cs

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

Décorez votre méthode d'action avec HttpGet et les paramètres ci-dessous

[HttpGet]
public HttpResponseMessage ReadMyData(string param1,
                        string param2, string param3)

 {

// your code here

}

pour appeler ci-dessus l'URL de la méthode sera comme ci-dessous

http: // localhost: [yourport]/api/MyData/ReadMyData? param1 = valeur1 & param2 = valeur2 & param3 = valeur

Deuxième option Ajoutez le préfixe de route à la classe de contrôleur et Décorez votre méthode d'action avec HttpGet comme ci-dessous. Dans ce cas, il n'est pas nécessaire de changer les fichiers WebApiConfig.cs. Il peut avoir un routage par défaut.

[RoutePrefix("api/{controller}/{action}")]
public class MyDataController : ApiController
{

[HttpGet]
public HttpResponseMessage ReadMyData(string param1,
                        string param2, string param3)

{

// your code here

}

}

pour appeler ci-dessus l'URL de la méthode sera comme ci-dessous

http: // localhost: [yourport]/api/MyData/ReadMyData? param1 = valeur1 & param2 = valeur2 & param3 = valeur

14
Nagaraju Mengani

Si vous utilisez ASP.NET 5 avec ASP.NET MVC 6 , la plupart de ces réponses ne fonctionneront tout simplement pas car vous laisserez normalement MVC créer la collection de routes appropriée pour vous (en utilisant les conventions RESTful par défaut), ce qui signifie que vous ne trouverez aucun appel Routes.MapRoute() à modifier. à volonté.

La méthode ConfigureServices() invoquée par le fichier Startup.cs enregistre MVC avec le framework Dependency Injection intégré à ASP.NET 5: ainsi, lorsque vous appelez ApplicationBuilder.UseMvc() plus tard dans cette classe, le framework MVC ajoutera automatiquement ces routes par défaut à votre application. Nous pouvons jeter un coup d'œil à ce qui se passe derrière le capot en regardant la mise en œuvre de la méthode UseMvc() dans le code source du framework:

public static IApplicationBuilder UseMvc(
    [NotNull] this IApplicationBuilder app,
    [NotNull] Action<IRouteBuilder> configureRoutes)
{
    // Verify if AddMvc was done before calling UseMvc
    // We use the MvcMarkerService to make sure if all the services were added.
    MvcServicesHelper.ThrowIfMvcNotRegistered(app.ApplicationServices);

    var routes = new RouteBuilder
    {
        DefaultHandler = new MvcRouteHandler(),
        ServiceProvider = app.ApplicationServices
    };

    configureRoutes(routes);

    // Adding the attribute route comes after running the user-code because
    // we want to respect any changes to the DefaultHandler.
    routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(
        routes.DefaultHandler,
        app.ApplicationServices));

    return app.UseRouter(routes.Build());
}

La bonne chose à propos de cela est que le cadre gère maintenant tout le travail, en effectuant une itération de toutes les actions du contrôleur et en configurant leurs itinéraires par défaut, vous évitant ainsi un travail redondant.

Le problème, c’est qu’il existe peu ou pas de documentation sur la manière d’ajouter vos propres itinéraires. Heureusement, vous pouvez facilement le faire en utilisant soit un basé sur une convention et/ou un basé sur un attribut approche (aka Routage d'attributs ).

Basé sur une convention

Dans votre classe Startup.cs, remplacez ceci:

app.UseMvc();

avec ça:

app.UseMvc(routes =>
            {
                // Route Sample A
                routes.MapRoute(
                    name: "RouteSampleA",
                    template: "MyOwnGet",
                    defaults: new { controller = "Items", action = "Get" }
                );
                // Route Sample B
                routes.MapRoute(
                    name: "RouteSampleB",
                    template: "MyOwnPost",
                    defaults: new { controller = "Items", action = "Post" }
                );
            });

basé sur les attributs

Un atout majeur de MVC6 est que vous pouvez également définir des itinéraires par contrôleur en décorant la classe Controller et/ou les Action avec les paramètres de modèle RouteAttribute et/ou HttpGet_/HttpPost tels que:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;

namespace MyNamespace.Controllers
{
    [Route("api/[controller]")]
    public class ItemsController : Controller
    {
        // GET: api/items
        [HttpGet()]
        public IEnumerable<string> Get()
        {
            return GetLatestItems();
        }

        // GET: api/items/5
        [HttpGet("{num}")]
        public IEnumerable<string> Get(int num)
        {
            return GetLatestItems(5);
        }       

        // GET: api/items/GetLatestItems
        [HttpGet("GetLatestItems")]
        public IEnumerable<string> GetLatestItems()
        {
            return GetLatestItems(5);
        }

        // GET api/items/GetLatestItems/5
        [HttpGet("GetLatestItems/{num}")]
        public IEnumerable<string> GetLatestItems(int num)
        {
            return new string[] { "test", "test2" };
        }

        // POST: /api/items/PostSomething
        [HttpPost("PostSomething")]
        public IActionResult Post([FromBody]string someData)
        {
            return Content("OK, got it!");
        }
    }
}

Ce contrôleur traitera les requêtes suivantes:

 [GET] api/items
 [GET] api/items/5
 [GET] api/items/GetLatestItems
 [GET] api/items/GetLatestItems/5
 [POST] api/items/PostSomething

Notez également que si vous utilisez les deux approches togheter, les routes basées sur les attributs (lorsqu'elles sont définies) remplacent les routes basées sur les conventions, et les deux sur les routes par défaut définies par UseMvc().

Pour plus d'informations, vous pouvez également lisez le post suivant sur mon blog.

10
Darkseal

Voir cet article pour une discussion plus longue sur les actions nommées. Il montre également que vous pouvez utiliser l'attribut [HttpGet] au lieu de préfixer le nom de l'action avec "get".

http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api

9
Andrew