Comment passer plusieurs paramètres à une méthode get dans ASP.NET Core

108

Comment puis-je transmettre plusieurs paramètres aux méthodes Get dans un contrôleur MVC 6. Par exemple, je veux pouvoir avoir quelque chose comme ce qui suit.

[Route("api/[controller]")]
public class PersonController : Controller
{
    public string Get(int id)
    {
    }

    public string Get(string firstName, string lastName)
    {

    }

    public string Get(string firstName, string lastName, string address)
    {

    }
}

Donc je peux interroger comme.

api/person?id=1
api/person?firstName=john&lastName=doe
api/person?firstName=john&lastName=doe&address=streetA
mstrand
la source

Réponses:

92

Vous pouvez également utiliser ceci:

// GET api/user/firstname/lastname/address
[HttpGet("{firstName}/{lastName}/{address}")]
public string GetQuery(string id, string firstName, string lastName, string address)
{
    return $"{firstName}:{lastName}:{address}";
}

Remarque : veuillez vous référer à metalheart et metalheartet Mark Hughespour une meilleure approche.

antlas
la source
22
Jusqu'à ce que vous ayez besoin d'avoir tout le monde avec le même nom :)
Phillip Copley
15
C'est une très mauvaise façon de concevoir des routes API ... Pas du tout RESTful.
Thomas Levesque
7
L'approche ci-dessus semble très lourde, ne comprenez pas pourquoi elle a tant de votes positifs.
Bernoulli IT
1
@ThomasLevesque Qu'entendez-vous par ne pas être RESTful?
Bruno Santos
2
@BrunoSantos il ne suit pas les principes de REST. Les URI sont censés identifier de manière unique les ressources. Ce n'est pas le cas ici (il peut y avoir plusieurs personnes avec le même prénom et le même nom, et une adresse ne peut certainement pas être considérée comme un identifiant)
Thomas Levesque
61

Pourquoi ne pas utiliser une seule action de contrôleur?

public string Get(int? id, string firstName, string lastName, string address)
{
   if (id.HasValue)
      GetById(id);
   else if (string.IsNullOrEmpty(address))
      GetByName(firstName, lastName);
   else
      GetByNameAddress(firstName, lastName, address);
}

Une autre option consiste à utiliser le routage d'attributs, mais vous devrez alors avoir un format d'URL différent:

//api/person/byId?id=1
[HttpGet("byId")] 
public string Get(int id)
{
}

//api/person/byName?firstName=a&lastName=b
[HttpGet("byName")]
public string Get(string firstName, string lastName, string address)
{
}
coeur de métal
la source
Oui, je le résous maintenant en utilisant une seule action en prenant tous les attributs sur lesquels je veux pouvoir rechercher une personne. Comme une recherche générale. Je préférerais cependant s'il existe un moyen d'avoir des actions surchargées dans un contrôleur, mais cela pourrait ne pas être le cas.
mstrand
3
cela ne fonctionne pas avec .net core 2.0, car aucun modèle d'URL valide n'est réellement généré.
ZZZ
44

Pour analyser les paramètres de recherche à partir de l'URL, vous devez annoter les paramètres de la méthode du contrôleur avec [FromQuery], par exemple:

[Route("api/person")]
public class PersonController : Controller
{
    [HttpGet]
    public string GetById([FromQuery]int id)
    {

    }

    [HttpGet]
    public string GetByName([FromQuery]string firstName, [FromQuery]string lastName)
    {

    }

    [HttpGet]
    public string GetByNameAndAddress([FromQuery]string firstName, [FromQuery]string lastName, [FromQuery]string address)
    {

    }
}
Mark Hughes
la source
6
pourquoi en auriez-vous besoin? la liaison de paramètres à partir de la chaîne de requête se produit par défaut ...
metalheart
1
J'ai essayé les deux, mais la surcharge comme j'essaye de le faire échoue avec ou sans [FromQuery]
mstrand
2
@mstrand J'ai mis à jour - essayez-le, voyez les [HttpGet]annotations supplémentaires , les différents noms de méthodes et l'itinéraire spécifique dans [Route]- les routes devraient être pleinement explicites maintenant, ce qui élimine quelques problèmes possibles.
Mark Hughes
9

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

[Route("api/YOURCONTROLLER/{paramOne}/{paramTwo}")]
    public string Get(int paramOne, int paramTwo)
    {
        return "The [Route] with multiple params worked";
    }
Sunil Dhappadhule
la source
Puis-je utiliser le type de référence préféré? Autrement dit,int paramOne, string paramTwo
k4s
Utilisez [Route ("api / YOURCONTROLLER / {paramOne} / {paramTwo?}")] Si vous voulez que votre deuxième paramètre soit facultatif
Anytoe
8

Je suggérerais d'utiliser un objet dto séparé comme argument:

[Route("api/[controller]")]
public class PersonController : Controller
{
    public string Get([FromQuery] GetPersonQueryObject request)
    {
        // Your code goes here
    }
}

public class GetPersonQueryObject 
{
    public int? Id { get; set; }
    public string Firstname { get; set; }
    public string Lastname { get; set; }
    public string Address { get; set; }
}

Dotnet mappera les champs à votre objet.

Cela rendra beaucoup plus facile le passage de vos paramètres et se traduira par un code beaucoup plus clair.

Sébastien
la source
5

Pour appeler get avec plusieurs paramètres dans le noyau de l'API Web

  [ApiController]
    [Route("[controller]")]
    public class testController : Controller
    {

      [HttpGet]
        [Route("testaction/{id:int}/{startdate}/{enddate}")]
        public IEnumerable<classname> test_action(int id, string startdate, string enddate)
        {

            return List_classobject;
        }

    }

In web browser
https://localhost:44338/test/testaction/3/2010-09-30/2012-05-01
Prash9002
la source
3

Pour ajouter plus de détails sur la surcharge que vous avez posée dans votre commentaire après une autre réponse, voici un résumé. Les commentaires dans le ApiControllermontrent quelle action sera appelée avec chaque GETrequête:

public class ValuesController : ApiController
{
    // EXPLANATION: See the view for the buttons which call these WebApi actions. For WebApi controllers, 
    //          there can only be one action for a given HTTP verb (GET, POST, etc) which has the same method signature, (even if the param names differ) so
    //          you can't have Get(string height) and Get(string width), but you can have Get(int height) and Get(string width).
    //          It isn't a particularly good idea to do that, but it is true. The key names in the query string must match the
    //          parameter names in the action, and the match is NOT case sensitive. This demo app allows you to test each of these
    //          rules, as follows:
    // 
    // When you send an HTTP GET request with no parameters (/api/values) then the Get() action will be called.
    // When you send an HTTP GET request with a height parameter (/api/values?height=5) then the Get(int height) action will be called.
    // When you send an HTTP GET request with a width parameter (/api/values?width=8) then the Get(string width) action will be called.
    // When you send an HTTP GET request with height and width parameters (/api/values?height=3&width=7) then the 
    //          Get(string height, string width) action will be called.
    // When you send an HTTP GET request with a depth parameter (/api/values?depth=2) then the Get() action will be called
    //          and the depth parameter will be obtained from Request.GetQueryNameValuePairs().
    // When you send an HTTP GET request with height and depth parameters (/api/values?height=4&depth=5) then the Get(int height) 
    //          action will be called, and the depth parameter would need to be obtained from Request.GetQueryNameValuePairs().
    // When you send an HTTP GET request with width and depth parameters (/api/values?width=3&depth=5) then the Get(string width) 
    //          action will be called, and the depth parameter would need to be obtained from Request.GetQueryNameValuePairs().
    // When you send an HTTP GET request with height, width and depth parameters (/api/values?height=7&width=2&depth=9) then the 
    //          Get(string height, string width) action will be called, and the depth parameter would need to be obtained from 
    //          Request.GetQueryNameValuePairs().
    // When you send an HTTP GET request with a width parameter, but with the first letter of the parameter capitalized (/api/values?Width=8) 
    //          then the Get(string width) action will be called because the case does NOT matter.
    // NOTE: If you were to uncomment the Get(string height) action below, then you would get an error about there already being  
    //          a member named Get with the same parameter types. The same goes for Get(int id).
    //
    // ANOTHER NOTE: Using the nullable operator (e.g. string? paramName) you can make optional parameters. It would work better to
    //          demonstrate this in another ApiController, since using nullable params and having a lot of signatures is a recipe
    //          for confusion.

    // GET api/values
    public IEnumerable<string> Get()
    {
        return Request.GetQueryNameValuePairs().Select(pair => "Get() => " + pair.Key + ": " + pair.Value);
        //return new string[] { "value1", "value2" };
    }

    //// GET api/values/5
    //public IEnumerable<string> Get(int id)
    //{
    //    return new string[] { "Get(height) => height: " + id };
    //}

    // GET api/values?height=5
    public IEnumerable<string> Get(int height) // int id)
    {
        return new string[] { "Get(height) => height: " + height };
    }

    // GET api/values?height=3
    public IEnumerable<string> Get(string height)
    {
        return new string[] { "Get(height) => height: " + height };
    }

    //// GET api/values?width=3
    //public IEnumerable<string> Get(string width)
    //{
    //    return new string[] { "Get(width) => width: " + width };
    //}

    // GET api/values?height=4&width=3
    public IEnumerable<string> Get(string height, string width)
    {
        return new string[] { "Get(height, width) => height: " + height + ", width: " + width };
    }
}

Vous n'auriez besoin que d'un seul itinéraire pour cela, au cas où vous vous poseriez la question:

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

et vous pouvez tout tester avec cette vue MVC, ou quelque chose de similaire. Oui, je sais que vous n'êtes pas censé mélanger JavaScript avec du balisage et je n'utilise pas le bootstrap comme vous le feriez normalement, mais c'est uniquement à des fins de démonstration.

<div class="jumbotron">
    <h1>Multiple parameters test</h1>
    <p class="lead">Click a link below, which will send an HTTP GET request with parameters to a WebAPI controller.</p>
</div>
<script language="javascript">
    function passNothing() {
        $.get("/api/values", function (data) { alert(data); });
    }

    function passHeight(height) {
        $.get("/api/values?height=" + height, function (data) { alert(data); });
    }

    function passWidth(width) {
        $.get("/api/values?width=" + width, function (data) { alert(data); });
    }

    function passHeightAndWidth(height, width) {
        $.get("/api/values?height=" + height + "&width=" + width, function (data) { alert(data); });
    }

    function passDepth(depth) {
        $.get("/api/values?depth=" + depth, function (data) { alert(data); });
    }

    function passHeightAndDepth(height, depth) {
        $.get("/api/values?height=" + height + "&depth=" + depth, function (data) { alert(data); });
    }

    function passWidthAndDepth(width, depth) {
        $.get("/api/values?width=" + width + "&depth=" + depth, function (data) { alert(data); });
    }

    function passHeightWidthAndDepth(height, width, depth) {
        $.get("/api/values?height=" + height + "&width=" + width + "&depth=" + depth, function (data) { alert(data); });
    }

    function passWidthWithPascalCase(width) {
        $.get("/api/values?Width=" + width, function (data) { alert(data); });
    }
</script>
<div class="row">
    <button class="btn" onclick="passNothing();">Pass Nothing</button>
    <button class="btn" onclick="passHeight(5);">Pass Height of 5</button>
    <button class="btn" onclick="passWidth(8);">Pass Width of 8</button>
    <button class="btn" onclick="passHeightAndWidth(3, 7);">Pass Height of 3 and Width of 7</button>
    <button class="btn" onclick="passDepth(2);">Pass Depth of 2</button>
    <button class="btn" onclick="passHeightAndDepth(4, 5);">Pass Height of 4 and Depth of 5</button>
    <button class="btn" onclick="passWidthAndDepth(3, 5);">Pass Width of 3 and Depth of 5</button>
    <button class="btn" onclick="passHeightWidthAndDepth(7, 2, 9);">Pass Height of 7, Width of 2 and Depth of 9</button>
    <button class="btn" onclick="passHeightWidthAndDepth(7, 2, 9);">Pass Height of 7, Width of 2 and Depth of 9</button>
    <button class="btn" onclick="passWidthWithPascalCase(8);">Pass Width of 8, but with Pascal case</button>
</div>
Kent Weigel
la source
1

entrez la description de l'image ici

NB-j'ai supprimé FromURI .Je peux toujours transmettre la valeur de l'URL et obtenir le résultat.Si quelqu'un connaît benfifts en utilisant fromuri, faites-le moi savoir

saktiprasad swain
la source
Comme stipulé dans la documentation pour la liaison de paramètres [1] les types simples, "(int, bool, double, etc.), plus TimeSpan, DateTime, Guid, decimal et string" seront automatiquement lus à partir de l'URI. L'attribut [FromURI] est requis lorsque le paramètre n'appartient à aucun de ces types pour forcer la lecture de ceux de l'URI plutôt que de leur emplacement par défaut, le corps. Par souci d'exhaustivité, l'attribut [FromBody] fait essentiellement le contraire avec les types complexes. [1] docs.microsoft.com/en-us/aspnet/web-api/overview / ... )
Seb Andraos
1

Vous pouvez simplement faire ce qui suit:

    [HttpGet]
    public async Task<IActionResult> GetAsync()
    {
        string queryString = Request.QueryString.ToString().ToLower();

        return Ok(await DoMagic.GetAuthorizationTokenAsync(new Uri($"https://someurl.com/token-endpoint{queryString}")));
    }

Si vous devez accéder à chaque élément séparément, reportez-vous simplement à Request.Query.

SpiritBob
la source
1

Les méthodes devraient être comme ceci:

[Route("api/[controller]")]
public class PersonsController : Controller
{
    [HttpGet("{id}")]
    public Person Get(int id)

    [HttpGet]
    public Person[] Get([FromQuery] string firstName, [FromQuery] string lastName, [FromQuery] string address)
}

Notez que la deuxième méthode renvoie un tableau d'objets et que le nom du contrôleur est au pluriel (personnes et non personne).

Donc, si vous voulez obtenir une ressource par identifiant, ce sera:

api/persons/1

si vous voulez prendre des objets selon certains critères de recherche comme le prénom, etc., vous pouvez effectuer une recherche comme ceci:

api/persons?firstName=Name&...

Et aller de l'avant si vous voulez prendre les commandes de cette personne (par exemple), cela devrait être comme ceci:

api/persons/1/orders?skip=0&take=20

Et méthode dans le même contrôleur:

    [HttpGet("{personId}/orders")]
    public Orders[] Get(int personId, int skip, int take, etc..)
Paulius K.
la source
0
    public HttpResponseMessage Get(int id,string numb)
    {

        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
0

Le moyen le plus simple,

Manette:

[HttpGet("empId={empId}&startDate={startDate}&endDate={endDate}")]
 public IEnumerable<Validate> Get(int empId, string startDate, string endDate){}

Demande de facteur:

{router}/empId=1&startDate=2020-20-20&endDate=2020-20-20

Point d'apprentissage: la demande de modèle exact sera acceptée par le contrôleur.

Thushara Buddhika
la source