web-dev-qa-db-fra.com

La chaîne de requête ne fonctionne pas lors de l'utilisation du routage d'attribut

J'utilise System.Web.Http.RouteAttribute Et System.Web.Http.RoutePrefixAttribute Pour activer des URL plus propres pour mon application Web API 2. Pour la plupart de mes demandes, je peux utiliser le routage (par exemple. Controller/param1/param2) Ou des chaînes de requête (par exemple. Controller?param1=bob&param2=mary).

Malheureusement, avec l'un de mes contrôleurs (et un seul), cela échoue. Voici mon contrôleur:

[RoutePrefix("1/Names")]
public class NamesController : ApiController
{

    [HttpGet]
    [Route("{name}/{sport}/{drink}")]
    public List<int> Get(string name, string sport, string drink)
    {
        // Code removed...
    }

    [HttpGet]
    [Route("{name}/{drink}")]
    public List<int> Get(string name, string drink)
    {
        // Code removed...
    }
}

Lorsque je demande à utiliser l’acheminement, les deux fonctionnent correctement. Cependant, si j'utilise une chaîne de requête, elle échoue, en m'indiquant que ce chemin n'existe pas.

J'ai essayé d'ajouter ce qui suit à ma fonction WebApiConfig.cs 'Register(HttpConfiguration config) de la classe (avant et après la route par défaut), mais rien n'a été fait:

config.Routes.MapHttpRoute(
name: "NameRoute",
routeTemplate: "{verId}/Names/{name}/{sport}/{drink}",
defaults: new { name = RouteParameter.Optional, sport = RouteParameter.Optional, drink = RouteParameter.Optional },
constraints: new { verId = @"\d+" });

Donc, pour plus de clarté, j'aimerais pouvoir faire les deux choses suivantes:

localhost:12345/1/Names/Ted/rugby/coke
localhost:12345/1/Names/Ted/coke

et,

localhost:12345/1/Names?name=Ted&sport=rugby&drink=coke
localhost:12345/1/Names?name=Ted&drink=coke

mais malheureusement, les versions de chaîne de requête ne fonctionnent pas! :(

Mise à jour

J'ai complètement supprimé la deuxième action et j'essaie maintenant d'utiliser une seule action avec des paramètres facultatifs. J'ai changé mon attribut d'itinéraire en [Route("{name}/{drink}/{sport?}")] comme Tony a suggéré de rendre le sport nullable, mais cela empêche maintenant localhost:12345/1/Names/Ted/coke D'être un itinéraire valide pour une raison quelconque. Les chaînes de requête se comportent de la même manière qu'auparavant.

Mise à jour 2 J'ai maintenant une action singulière dans mon contrôleur:

[RoutePrefix("1/Names")]
public class NamesController : ApiController
{

    [HttpGet]
    [Route("{name}/{drink}/{sport?}")]
    public List<int> Get(string name, string drink, string sport = "")
    {
        // Code removed...
    }
}

cependant, l'utilisation de chaînes de requête ne permet pas de trouver un chemin approprié, contrairement à la méthode de routage.

59
Chris Paton

Après avoir beaucoup travaillé au violon et à la recherche sur Google, j'ai trouvé un "correctif". Je ne sais pas si c'est idéal/meilleure pratique/tout à fait faux, mais cela résout mon problème.

Tout ce que j'ai fait, c'est ajouter [Route("")] en plus des attributs de route que j'utilisais déjà. Cela permet essentiellement au routage de l'API Web 2 d'autoriser les chaînes de requête, car il s'agit désormais d'un itinéraire valide.

Un exemple serait maintenant:

[HttpGet]
[Route("")]
[Route("{name}/{drink}/{sport?}")]
public List<int> Get(string name, string drink, string sport = "")
{
    // Code removed...
}

Cela rend les deux localhost:12345/1/Names/Ted/coke et localhost:12345/1/Names?name=Ted&drink=coke valide.

41
Chris Paton

J'étais confronté au même problème de "Comment inclure les paramètres de recherche en tant que chaîne de requête?", Alors que j'essayais de créer une API Web pour mon projet actuel. Après googling, voici ce qui fonctionne pour moi:

Action du contrôleur Api:

[HttpGet, Route("search/{categoryid=categoryid}/{ordercode=ordercode}")]

public Task<IHttpActionResult> GetProducts(string categoryId, string orderCode)
{

}

L'URL que j'ai essayé par le facteur:

http://localhost/PD/search?categoryid=all-products&ordercode=star-1932

http://localhost/PD is my hosted api
59
Mosharaf Hossain

Avec le routage d'attribut, vous devez spécifier des valeurs par défaut afin qu'elles soient facultatives.

[Route("{name}/{sport=Football}/{drink=Coke}")]

L'attribution d'une valeur lui permettra d'être facultative, vous n'aurez donc pas à l'inclure et la valeur à spécifier sera transmise.

Je n'ai pas testé la chaîne de requête pour cela, mais cela devrait fonctionner de la même manière.

Je viens de relire la question et je vois que vous avez 2 Obtenez les verbes avec le même chemin, je crois que cela causerait un conflit, car le routage ne saurait pas lequel utiliser, peut-être que l'utilisation des paramètres optionnels vous aidera. Vous pouvez également en spécifier un et valider la méthode pour savoir comment procéder.

[Route("{name}/{sport?}/{drink?}")]

Ensuite, vérifiez les variables dans la méthode pour voir si elles sont nulles et manipulez-les si nécessaire.

J'espère que ça aide, certains? lol

Si ce n'est peut-être pas le cas sur ce site, il contient plus de détails sur le routage d'attributs.

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

Clip de ce site:

Paramètres facultatifs et valeurs par défaut Vous pouvez spécifier qu'un paramètre est facultatif en ajoutant un point d'interrogation au paramètre, c'est-à-dire:

[Route("countries/{name?}")]
public Country GetCountry(string name = "USA") { }

Actuellement, une valeur par défaut doit être spécifiée dans le paramètre facultatif pour que la sélection d'actions réussisse, mais nous pouvons étudier la possibilité de lever cette restriction. (S'il vous plaît laissez-nous savoir si cela est important.)

Les valeurs par défaut peuvent être spécifiées de la même manière:

[Route("countries/{name=USA}")]
public Country GetCountry(string name) { }

Le paramètre optionnel '?' et les valeurs par défaut doivent apparaître après les contraintes en ligne dans la définition du paramètre.

17
Tony

Juste une note de côté de ma part aussi. Pour que les paramètres queryString fonctionnent, vous devez fournir un la valeur par défaut de vos paramètres de méthode le rend facultatif. Comme vous le feriez également lors de l'invocation normale d'une méthode C #.

[RoutePrefix("api/v1/profile")]
public class ProfileController : ApiController
{

   ...

   [HttpGet]
   [Route("{profileUid}")]
   public IHttpActionResult GetProfile(string profileUid, long? someOtherId) 
   {
      // ...
   }

   ...

}

Cela me permet d'appeler le noeud final comme ceci:

/api/v1/profile/someUid
/api/v1/profile/someUid?someOtherId=123
10
Juri

Voici un léger déviant de @ bhargav kishore mummadireddy's , mais une déviation importante. Sa réponse va par défaut les valeurs de chaîne de requête à une valeur réelle non vide. Cette réponse va les vider par défaut.

Il vous permet d'appeler le contrôleur via le routage de chemin ou à l'aide de la chaîne de requête. Pour l’essentiel, il définit la valeur par défaut de la chaîne de requête sur vide, ce qui signifie qu’elle sera toujours routée.

Cela était important pour moi, car je veux renvoyer 400 (requête incorrecte) si aucune chaîne de requête n'est spécifiée, plutôt que de demander à ASP.NET de renvoyer l'erreur "impossible de localiser cette méthode sur ce contrôleur".

[RoutePrefix("api/AppUsageReporting")]
public class AppUsageReportingController : ApiController
    {
        [HttpGet]
        // Specify default routing parameters if the parameters aren't specified
        [Route("UsageAggregationDaily/{userId=}/{startDate=}/{endDate=}")]
        public async Task<HttpResponseMessage> UsageAggregationDaily(string userId, DateTime? startDate, DateTime? endDate)
        {
            if (String.IsNullOrEmpty(userId))
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, $"{nameof(userId)} was not specified.");
            }

            if (!startDate.HasValue)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, $"{nameof(startDate)} was not specified.");
            }

            if (!endDate.HasValue)
            {
                return Request.CreateResponse(HttpStatusCode.BadRequest, $"{nameof(endDate)} was not specified.");
            }
        }
    }
4
contactmatt

Utiliser Route("search/{categoryid=categoryid}/{ordercode=ordercode}") vous permettra d’utiliser les paramètres Querystrings et inline route comme indiqué par mosharaf hossain. Ecrire cette réponse comme cela devrait être la meilleure réponse et la meilleure façon. Utiliser Route("") posera des problèmes si vous avez plusieurs Gets/Puts/Posts/Deletes.

Puisque vous avez [Route("{name}/{drink}/{sport?}")] comme attribut, ce code ne sera jamais touché.

config.Routes.MapHttpRoute(
name: "NameRoute",
routeTemplate: "{verId}/Names/{name}/{sport}/{drink}",
defaults: new { name = RouteParameter.Optional, sport = RouteParameter.Optional, drink = RouteParameter.Optional },
constraints: new { verId = @"\d+" });

Ainsi, seul l'attribut route [Route("{name}/{drink}/{sport?}")] sera honoré ici. Depuis votre demande localhost:12345/1/Names?name=Ted&sport=rugby&drink=coke, N'a pas de nom, sport ou boisson dans l'URL, il ne correspondra pas à cette route d'attribut. Nous ne considérons pas les paramètres de chaîne de requête lors de la mise en correspondance des itinéraires.

Pour résoudre ce problème, vous devez rendre les 3 facultatifs dans votre route d'attribut. Ensuite, cela correspond à la demande.

1
mugdhak

J'utilise l'attribut FromUri comme solution

[Route("UsageAggregationDaily")]
public async Task<HttpResponseMessage> UsageAggregationDaily([FromUri] string userId = null, [FromUri] DateTime? startDate = null, [FromUri] DateTime? endDate = null)
0
Vasil Galinovsky