Paramètres de chaîne de requête facultatifs dans l'API Web ASP.NET

212

J'ai besoin d'implémenter la méthode WebAPI suivante:

/api/books?author=XXX&title=XXX&isbn=XXX&somethingelse=XXX&date=XXX

Tous les paramètres de chaîne de requête peuvent être nuls. Autrement dit, l'appelant peut spécifier de 0 à tous les 5 paramètres.

Dans MVC4 beta, j'avais l'habitude de faire ce qui suit:

public class BooksController : ApiController
{
    // GET /api/books?author=tolk&title=lord&isbn=91&somethingelse=ABC&date=1970-01-01
    public string GetFindBooks(string author, string title, string isbn, string somethingelse, DateTime? date) 
    {
        // ...
    }
}

MVC4 RC ne se comporte plus comme ça. Si je spécifie moins de 5 paramètres, il répond par un 404dicton:

Aucune action n'a été trouvée sur les livres du contrôleur qui correspond à la demande.

Quelle est la signature de méthode correcte pour qu'elle se comporte comme avant, sans avoir à spécifier le paramètre facultatif dans le routage URL?

frapontillo
la source
mettre [httpget] en action.
user960567
2
Si je règle tous les paramètres, la méthode est appelée; de plus il commence Getdonc il est automatiquement lié à la HTTP GETméthode ...
frapontillo
Voici comment fonctionne le routage de l'API Web, asp.net/web-api/overview/web-api-routing-and-actions/…
user960567
4
Oui. Je sais comment ça marche. Je ne peux tout simplement pas le faire fonctionner dans CETTE circonstance particulière.
frapontillo
Comment cela s'est-il même compilé? string?n'est pas un type valide. Vous ne pouvez pas déclarer en stringtant que type nullable car il s'agit d'un type de référence.
EkoostikMartin

Réponses:

307

Ce problème a été corrigé dans la version régulière de MVC4. Vous pouvez maintenant:

public string GetFindBooks(string author="", string title="", string isbn="", string  somethingelse="", DateTime? date= null) 
{
    // ...
}

et tout fonctionnera hors de la boîte.

frapontillo
la source
Puis-je utiliser null ici par défaut? Par exemple: string author = null?
Boris Zinchenko
2
Oui, nullest considéré comme une expression constante , et donc une valeur par défaut valide .
JDawg
Je me demande pourquoi nous devons mentionner les valeurs par défaut, même pour les paramètres facultatifs, comme indiqué ici . Tout type en C # a toujours une valeur par défaut, de sorte que l'exécution du routage aurait pu prendre la valeur par défaut du type s'il ne l'avait pas reçue de l'URI. Quelle est la raison technique derrière cela?. Je suis sûr que cela a quelque chose à voir avec le classeur de modèles.
RBT
@RBT Pour que la route puisse être associée
James Westgate
J'utilisais des paramètres de date et si je les mettais à nullable ne fonctionnait pas. Je dois donc le définir nullable et le set null comme valeur par défaut, et utiliser la validation côté serveur en conséquence et renvoyer les messages d'erreur. Ça a marché.
Atta H.
85

Il est possible de passer plusieurs paramètres en un seul modèle comme l'a suggéré vijay. Cela fonctionne pour GET lorsque vous utilisez l'attribut de paramètre FromUri. Cela indique à WebAPI de remplir le modèle à partir des paramètres de requête.

Le résultat est une action de contrôleur plus propre avec un seul paramètre. Pour plus d'informations, voir: http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api

public class BooksController : ApiController
  {
    // GET /api/books?author=tolk&title=lord&isbn=91&somethingelse=ABC&date=1970-01-01
    public string GetFindBooks([FromUri]BookQuery query)
    {
      // ...
    }
  }

  public class BookQuery
  {
    public string Author { get; set; }
    public string Title { get; set; }
    public string ISBN { get; set; }
    public string SomethingElse { get; set; }
    public DateTime? Date { get; set; }
  }

Il prend même en charge plusieurs paramètres, tant que les propriétés ne sont pas en conflit.

// GET /api/books?author=tolk&title=lord&isbn=91&somethingelse=ABC&date=1970-01-01
public string GetFindBooks([FromUri]BookQuery query, [FromUri]Paging paging)
{
  // ...
}

public class Paging
{
  public string Sort { get; set; }
  public int Skip { get; set; }
  public int Take { get; set; }
}

Mise à jour :
Afin de garantir que les valeurs sont facultatives, assurez-vous d'utiliser des types de référence ou des valeurs Nullables (ex. Int?) Pour les propriétés des modèles.

Andrew C
la source
4
Oui, mais le décorateur [FromUri] seul ne semble pas prendre en charge les paramètres facultatifs.
John Meyer
6
@JohnMeyer Vous avez raison d'utiliser [FromUri] ne répond pas directement à la question d'origine. Il dit essentiellement de remplir ces modèles avec les valeurs de l'Uri. Les propriétés des modèles devraient être nullables ou un type de référence pour qu'elles puissent être facultatives. Ajout d'informations supplémentaires.
Andrew C
@AndrewC - Pourriez-vous expliquer quand / pourquoi vous devez utiliser des valeurs nulles pour vous assurer que les valeurs sont facultatives? Si vous ne rendez pas les valeurs nulles (par exemple, propriété int Skip) et qu'aucun paramètre de requête n'est spécifié pour cette propriété, la méthode API Controller correspondra toujours avec succès à la demande et la valeur de Skipsera juste la valeur par défaut pour ce type, ou 0 dans ce cas
Clark
2
@Clark - Sans utiliser un type nullable, vous ne saurez pas si l'utilisateur n'a pas fourni de valeur et a obtenu la valeur de type non initialisé (0 pour int) ou si l'utilisateur a spécifié 0. En utilisant nullable, vous êtes sûr que l'utilisateur l'a laissé non défini vous pouvez donc appliquer votre valeur par défaut en toute sécurité dans l'action du contrôleur. Si vous regardez Take dans l'exemple ci-dessus, que devrait faire l'action si elle a reçu un 0 pour Take? L'utilisateur voulait-il demander 0 enregistrements ou ne l'a-t-il pas spécifié et vous devez donc prendre tous les enregistrements. En règle générale, si vous souhaitez qu'un type de valeur (int, bool, etc.) soit facultatif, il doit être nullable.
Andrew C
70

Utiliser les valeurs par défaut initiales pour tous les paramètres comme ci-dessous

public string GetFindBooks(string author="", string title="", string isbn="", string  somethingelse="", DateTime? date= null) 
{
    // ...
}
Muhammad Amin
la source
1
C'est la bonne procédure mais pour une chose: DateTimen'est pas annulable. J'ai déjà essayé d'utiliser à la DateTime?place, mais MVC ne mappe pas la demande à la méthode donnée si je ne définis que certains des paramètres dans ma demande HTTP.
frapontillo
vous pouvez passer la date sous forme de chaîne et l'analyser dans la fonction de votre contrôleur à l'aide de la fonction DateTime.Parse ().
Muhammad Amin
1
@MuhammadAmin, DateTimen'est pas un type de données nullable . Votre code ne doit pas être compilé, car vous ne pourriez pas attribuer de nullvaleur à un paramètre de type DateTime. Peut-être devriez-vous le changer DateTime?ou utiliser une valeur différente pour une valeur par défaut comme DateTime.Now.
Ivaylo Slavov
1
@IvayloSlavov DateTime.Now n'est pas une constante de temps de compilation, elle ne peut donc pas être affectée comme paramètre par défaut.
GiriB
@GiriB, vous avez bien raison. Datetime.Nowne peut pas être utilisé dans l'initialisation des paramètres par défaut, je suis corrigé.
Ivaylo Slavov
1

si vous voulez passer plusieurs paramètres, vous pouvez créer un modèle au lieu de passer plusieurs paramètres.

dans le cas où vous ne voulez pas passer de paramètre, vous pouvez également l'ignorer et votre code sera net et propre.

vijay
la source
1
Cela n'est vrai que pour les paramètres POST dans le corps de la demande - les paramètres dans l'url peuvent toujours être référencés individuellement comme arguments.
Nathan
1

Les valeurs par défaut ne peuvent pas être fournies pour les paramètres qui ne sont pas déclarés ' optional'

 Function GetFindBooks(id As Integer, ByVal pid As Integer, Optional sort As String = "DESC", Optional limit As Integer = 99)

Dans votre WebApiConfig

 config.Routes.MapHttpRoute( _
          name:="books", _
          routeTemplate:="api/{controller}/{action}/{id}/{pid}/{sort}/{limit}", _
          defaults:=New With {.id = RouteParameter.Optional, .pid = RouteParameter.Optional, .sort = UrlParameter.Optional, .limit = UrlParameter.Optional} _
      )
Rizwan Mumtaz
la source
8
En fait, ils le peuvent. J'utilise C #, pas VB.NET.
frapontillo