Comment dois-je transmettre plusieurs paramètres à une API Web ASP.Net GET?

136

J'utilise l'API Web .Net MVC4 pour implémenter (espérons-le) une API RESTful. Je dois transmettre quelques paramètres au système et lui faire effectuer une action, puis renvoyer une liste d'objets en tant que résultats. Plus précisément, je passe deux dates et je renvoie les enregistrements qui se situent entre elles. Je garde également une trace des enregistrements renvoyés afin que les appels suivants ne soient pas retraités dans le système.

J'ai envisagé quelques approches:

  1. Sérialisation des paramètres en une seule chaîne JSON et sélection de celle-ci dans l'API. http://forums.asp.net/t/1807316.aspx/1

  2. Passez les paramètres dans la chaîne de requête.
    Quelle est la meilleure façon de transmettre plusieurs paramètres de requête à une API reposante?

  3. Définition des paramètres de la route: api / controller / date1 / date2

  4. En utilisant un POST qui me permet intrinsèquement de passer un objet avec des paramètres.

  5. Recherche ODATA puisque l'API Web (actuellement) le prend en charge. Je n'ai pas encore fait grand chose avec cela, donc je ne suis pas très familier avec cela.

Il semble que les bonnes pratiques REST indiquent que lorsque les données sont extraites, vous devez utiliser un GET. Cependant, GET devrait également être nullipotent (ne produit aucun effet secondaire), et je me demande si mon implémentation spécifique viole cela puisque je marque des enregistrements dans le système API, donc je produis des effets secondaires.

Cela m'a également conduit à la question de la prise en charge des paramètres variables. Si la liste des paramètres d'entrée change, il serait fastidieux de redéfinir votre itinéraire pour le choix 3 si cela se produit souvent. Et que peut-il se passer si les paramètres sont définis au moment de l'exécution ...

Dans tous les cas, pour mon implémentation spécifique, quel choix (le cas échéant) me semble le mieux?

sig606
la source

Réponses:

10

Que signifie ce marquage d'enregistrement? Si cela n'est utilisé qu'à des fins de journalisation, j'utiliserais GET et désactiverais toute la mise en cache, car vous souhaitez enregistrer chaque requête pour ces ressources. Si le marquage des enregistrements a un autre but, POST est la voie à suivre. L'utilisateur doit savoir que ses actions affectent le système et la méthode POST est un avertissement.

LukLed
la source
Par marquage, j'entends simplement suivre les enregistrements traités et renvoyés afin que les appels suivants ne les répètent pas. Dans mon cas, je fais juste un insert dans une autre table pour suivre ce qui est traité.
sig606
À l'heure actuelle, je l'ai implémenté en tant que POST, principalement pour la raison que vous avez dite - des actions se produisent et le consommateur en est conscient. De plus, cela semble facile et plus flexible avec resp pour transmettre différentes données.
sig606
@ sig606: POST est la voie à suivre pour moi, mais votre protocole ne semble pas sûr. Que faire si quelque chose se produit et que les enregistrements sont récupérés côté client, mais pas traités en raison d'un bogue? Vous ne les retournerez plus et le client se retrouve avec des données perdues.
LukLed
Pour le moment, mon API ne renvoie les enregistrements qu'après leur traitement. Le consommateur passe donc deux dates à l'API. Les enregistrements entre ces deux dates sont traités et marqués. Ensuite, les données sont renvoyées à l'appelant. Je suppose que si quelque chose se produit pendant le traitement ou après le traitement avant d'atteindre le client, j'ai un problème.
sig606
141

Je pense que le moyen le plus simple est simplement d'utiliser AttributeRouting.

C'est évident dans votre contrôleur, pourquoi voudriez-vous cela dans votre WebApiConfigfichier Global ?

Exemple:

    [Route("api/YOURCONTROLLER/{paramOne}/{paramTwo}")]
    public string Get(int paramOne, int paramTwo)
    {
        return "The [Route] with multiple params worked";
    }

Les {}noms doivent correspondre à vos paramètres.

Aussi simple que cela, vous avez maintenant un autre GETqui gère plusieurs paramètres dans cette instance.

Mark Pieszak - Trilon.io
la source
12
C'est bien. La plupart des gens recommandent de configurer l'itinéraire dans le WebApiConfigfichier, mais c'est en effet plus agréable.
rhyek
4
En effet, nous (la plupart des gens) recommandons d'avoir une zone de gestion centralisée pour votre configuration. Dans le cas des API Web (Microsoft ou autre), les modèles centralisés pour REST sont essentiels. Le routage des attributs est mignon, mais il rend les exceptions ponctuelles beaucoup trop tentantes.
David Betz
3
D'accord, je dois mettre à jour ma réponse en fait. Il existe une bien meilleure façon de faire plusieurs paramètres avec les GET. Posté ceci quand j'étais plus récent sur WebAPI, maintenant je n'utilise pas AttributeRouting (à moins que je ne veuille tout simplement pas créer un nouveau contrôleur), et transmets tous les paramètres dans QueryString, ils mappent automatiquement. Mise à jour quand j'en ai l'occasion pour que les gens n'utilisent pas cette méthode plus ancienne
Mark Pieszak - Trilon.io
Existe-t-il un moyen de définir un Routepour les paramètres nommés (par exemple les paramètres de requête)?
Shimmy Weitzhandler du
1
Si le nom de la méthode d'action est requis, celui-ci peut être modifié en conséquence. [Route ("api / YOURCONTROLLER / Get / {paramOne} / {paramTwo}")] public string Get (int paramOne, int paramTwo) {return "quelque chose"; }
Dash
49

Ajoutez simplement un nouvel itinéraire aux WebApiConfigentrées.

Par exemple, pour appeler:

public IEnumerable<SampleObject> Get(int pageNumber, int pageSize) { ..

ajouter:

config.Routes.MapHttpRoute(
    name: "GetPagedData",
    routeTemplate: "api/{controller}/{pageNumber}/{pageSize}"
);

Ajoutez ensuite les paramètres à l'appel HTTP:

GET //<service address>/Api/Data/2/10 
Graham Wright
la source
10
Cela semble être la seule réponse qui répertorie toutes les parties. Je souhaite que quelqu'un décrive mieux comment utiliser l' api/controller?start=date1&end=date2URI de style.
Hot Licks
La réponse de @Hot Licks Andrew Veriga fonctionne bien avec les arguments de chaîne de requête. Essentiellement, vous liez les noms de chaîne de requête aux propriétés de classe et les transmettez à votre méthode. Votre méthode prendra un seul argument de classe marqué avec l'attribut [FromUri] et aura vos arguments de chaîne de requête comme propriétés.
David Peterson
Super truc. Merci!
Hugo Nava Kopp
salut @HotLicks et GrahamWright pensez-vous pouvoir répondre à cette question? Merci, stackoverflow.com/questions/57565318/…
45

Je devais juste implémenter une API RESTfull où je devais passer des paramètres. Je l'ai fait en passant les paramètres dans la chaîne de requête dans le même style que celui décrit par le premier exemple de Mark "api / controller? Start = date1 & end = date2"

Dans le contrôleur, j'ai utilisé une astuce de l' URL fractionnée en C #?

// uri: /api/courses
public IEnumerable<Course> Get()
{
    NameValueCollection nvc = HttpUtility.ParseQueryString(Request.RequestUri.Query);
    var system = nvc["System"];
    // BL comes here
    return _courses;
}

Dans mon cas, j'appelais le WebApi via Ajax ressemblant à:

$.ajax({
        url: '/api/DbMetaData',
        type: 'GET',
        data: { system : 'My System',
                searchString: '123' },
        dataType: 'json',
        success: function (data) {
                  $.each(data, function (index, v) {
                  alert(index + ': ' + v.name);
                  });
         },
         statusCode: {
                  404: function () {
                       alert('Failed');
                       }
        }
   });

J'espère que ça aide...

Nigel Findlater
la source
2
Je suppose que vous n'utilisez pas WebApi parce que ParameterBinding mappera automatiquement votre chaîne de requête aux paramètres de votre méthode api ...
emp
1
Oui, une meilleure façon serait d'utiliser et d'attribuer comme [Route ("api / DbMetaData / {system} / {searchString}")], puis d'y ajouter des paramètres au Get (système de chaînes, chaîne searchString), puis d'appeler avec " ... api / DbMetaData / mysystem / mysearchstring "
Nigel Findlater
J'ai utilisé son exemple dans mon C # MVC WebApi et cela a bien fonctionné. +1 par exemple
Si8
38

J'ai trouvé une excellente solution sur http://habrahabr.ru/post/164945/

public class ResourceQuery
{
   public string Param1 { get; set; }
   public int OptionalParam2 { get; set; }
}

public class SampleResourceController : ApiController
{
    public SampleResourceModel Get([FromUri] ResourceQuery query)
    {
        // action
    }
}
Andrew Veriga
la source
5
L'indice ici est le [FromUri]
tranceporter
2
Bien que l'article soit en russe, @tranceporter a raison. Le "FromUri" ressemble à un excellent moyen d'obtenir les paramètres de l'url. Un autre article qui pourrait être utile: asp.net/web-api/overview/formats-and-model-binding/…
Greg
C'est ce que je fais depuis un bon moment maintenant et ça a très bien fonctionné! Je recommande également cette solution.
David Peterson du
Si vous appelez une autre méthode d'assistance (pas la Get), pouvez-vous toujours utiliser [FromUri]? Je n'arrive pas à faire fonctionner ça.
jocull du
8

L'utilisation de GET ou POST est clairement expliquée par @LukLed . En ce qui concerne les façons dont vous pouvez passer les paramètres, je suggérerais d'utiliser la deuxième approche (je ne sais pas grand-chose non plus sur ODATA ).

1) Sérialisation des paramètres en une seule chaîne JSON et sélection de celle-ci dans l'API. http://forums.asp.net/t/1807316.aspx/1

Ce n'est pas convivial et convivial pour le référencement

2.Passez les paramètres dans la chaîne de requête. Quelle est la meilleure façon de transmettre plusieurs paramètres de requête à une API reposante?

Il s'agit de l'approche généralement préférable.

3.Définir les paramètres de la route: api / controller / date1 / date2

Ce n'est certainement pas une bonne approche. Cela donne l'impression que quelqu'un date2est une ressource secondaire date1et ce n'est pas le cas. Les deux date1et date2sont des paramètres de requête et sont au même niveau.

Dans le cas simple, je suggérerais un URI comme celui-ci,

api/controller?start=date1&end=date2

Mais j'aime personnellement le modèle d'URI ci-dessous, mais dans ce cas, nous devons écrire du code personnalisé pour mapper les paramètres.

api/controller/date1,date2
VJAI
la source
En fait, ce sont mes explications d'origine. Je pense que LukLed a brillé mes balises et mon lien URL.
sig606
En ce qui concerne le référencement, dans ce cas, cela ne s'appliquerait pas. Ce code sera "de serveur à serveur", donc je m'en fiche si le monde extérieur le découvre. En fait, je dois m'assurer que les mesures de sécurité appropriées sont prises pour éviter un accès aléatoire. J'ai dû faire la sérialisation JSON pour une autre partie du système (semble être un bogue essayant de POST de grandes listes d'obj donc j'ai dû sérialiser en chaîne), donc ce ne serait pas trop compliqué dans ce cas .
sig606
1
J'espère que vous avez déjà des réponses alors pourquoi vous posez la question?
VJAI
2
Désolé pour cette réponse tardive, Mark. J'avais essayé quelques solutions, mais je ne savais pas quelle était la meilleure et j'essayais de m'en tenir aux approches standard de l'industrie, alors j'ai posté ici à SO.
sig606
1
@Mark Quelque chose comme le suivant: stackoverflow.com/questions/9681658/... ?
RredCat
3
 [Route("api/controller/{one}/{two}")]
    public string Get(int One, int Two)
    {
        return "both params of the root link({one},{two}) and Get function parameters (one, two)  should be same ";
    }

Les deux paramètres du lien racine ({un}, {deux}) et les paramètres de la fonction Get (un, deux) doivent être identiques

ashwath hegde
la source
2

Je sais que c'est vraiment vieux, mais je voulais la même chose récemment et voici ce que j'ai trouvé ...

    public HttpResponseMessage Get([FromUri] string var, [FromUri] string test) {
        var retStr = new HttpResponseMessage(HttpStatusCode.OK);
        if (var.ToLower() == "getnew" && test.ToLower() == "test") {
            retStr.Content = new StringContent("Found Test", System.Text.Encoding.UTF8, "text/plain");
        } else {
            retStr.Content = new StringContent("Couldn't Find that test", System.Text.Encoding.UTF8, "text/plain");
        }

        return retStr;
    }

Alors maintenant dans votre adresse / URI / ...

http (s): // myURL / api / myController /? var = getnew & test = test

Résultat: "Test trouvé"


http (s): // myURL / api / myController /? var = getnew & test = quelque chose

Résultat: "Impossible de trouver ce test"

Rick Riggs
la source
Personnellement, j'aime ce style en C #, car je peux changer la signature de la méthode originale et surcharger exactement ce que j'essaie d'accomplir, sans modifier les configurations de routage. J'espère que cela aidera les autres qui sont habitués à cette approche (peut-être désuète) de faire une demande GET.
Rick Riggs
1
J'ai dû créer une API d'événement qui est utilisée par une application de calendrier tierce, qui utilise cette approche. Je suis content d'avoir trouvé cette réponse!
clayRay
0
    public HttpResponseMessage Get(int id,string numb)
    {
        //this will differ according to your entity name
        using (MarketEntities entities = new MarketEntities())
        {
          var ent=  entities.Api_For_Test.FirstOrDefault(e => e.ID == id && e.IDNO.ToString()== numb);
            if (ent != null)
            {
                return Request.CreateResponse(HttpStatusCode.OK, ent);
            }
            else
            {
                return Request.CreateErrorResponse(HttpStatusCode.NotFound, "Applicant with ID " + id.ToString() + " not found in the system");
            }
        }
    }
Jesse Mwangi
la source