Como passar vários parâmetros para um método get no ASP.NET Core

108

Como posso passar vários parâmetros para métodos Get em um controlador MVC 6. Por exemplo, eu quero ser capaz de ter algo como o seguinte.

[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)
    {

    }
}

Então posso consultar como.

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

Respostas:

92

Você também pode usar isto:

// 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}";
}

Nota : Por favor, consulte metalheart de e metalhearte Mark Hughesde uma abordagem possivelmente melhor.

antlas
fonte
21
Até que você precise obter todos com o mesmo sobrenome :)
Phillip Copley
14
Essa é uma maneira realmente ruim de projetar rotas de API ... Nem um pouco RESTful.
Thomas Levesque de
7
A abordagem acima parece muito complicada, não entendo por que ela tem tantos votos positivos.
Bernoulli IT
1
@ThomasLevesque O que você quis dizer com não ser RESTful?
Bruno Santos
2
@BrunoSantos não segue os princípios do REST. Os URIs devem identificar recursos de maneira exclusiva. Este não é o caso aqui (pode haver várias pessoas com o mesmo nome e sobrenome, e um endereço certamente não pode ser considerado um identificador)
Thomas Levesque
61

Por que não usar apenas uma ação do controlador?

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);
}

Outra opção é usar o roteamento de atributo, mas você precisará ter um formato de URL diferente:

//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)
{
}
coração de metal
fonte
Sim, eu resolvo agora usando apenas uma ação, levando em conta todos os atributos que desejo poder pesquisar uma pessoa. Como uma pesquisa geral. Eu preferiria embora se houvesse uma maneira de ter ações sobrecarregadas em um controlador, mas pode não ser o caso.
mstrand
3
isso não funciona com .net core 2.0, uma vez que nenhum modelo de url válido é realmente gerado.
ZZZ
44

Para analisar os parâmetros de pesquisa do URL, você precisa anotar os parâmetros do método do controlador com [FromQuery], por exemplo:

[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
fonte
6
por que você precisa disso? a vinculação do parâmetro da string de consulta acontece por padrão ...
metalheart
1
Eu tentei os dois, mas a sobrecarga enquanto tento fazer falha com ou sem [FromQuery]
mstrand
2
@mstrand Eu atualizei - experimente, veja as [HttpGet]anotações extras , os diferentes nomes de métodos e a rota específica em [Route]- as rotas devem ser totalmente explícitas agora, o que elimina alguns problemas possíveis.
Mark Hughes
9

Acho que a maneira mais fácil é simplesmente usar AttributeRouting.

[Route("api/YOURCONTROLLER/{paramOne}/{paramTwo}")]
    public string Get(int paramOne, int paramTwo)
    {
        return "The [Route] with multiple params worked";
    }
Sunil Dhappadhule
fonte
Posso usar o tipo de referência preferido? Ou seja,int paramOne, string paramTwo
k4s
Use [Route ("api / YOURCONTROLLER / {paramOne} / {paramTwo?}")] Se quiser que o segundo parâmetro seja opcional
Anytoe
8

Eu sugeriria usar um objeto dto separado como argumento:

[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 irá mapear os campos para o seu objeto.

Isso tornará muito mais fácil passar seus parâmetros e resultará em um código muito mais claro.

Sebastian
fonte
5

Para chamar get com vários parâmetros no núcleo da API da 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
fonte
3

Para adicionar mais detalhes sobre a sobrecarga que você perguntou em seu comentário após outra resposta, aqui está um resumo. Os comentários no ApiControllermostram qual ação será chamada com cada GETconsulta:

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 };
    }
}

Você só precisaria de uma única rota para isso, caso esteja se perguntando:

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

e você pode testar tudo com esta visão MVC, ou algo semelhante. Sim, eu sei que você não deve misturar JavaScript com marcação e não estou usando bootstrap como você faria normalmente, mas isso é apenas para fins de demonstração.

<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
fonte
1

insira a descrição da imagem aqui

NB-I removido FromURI. Ainda posso passar o valor da URL e obter o resultado. Se alguém souber de benfifts usando fromuri, me avise

saktiprasad swain
fonte
Conforme estipulado na documentação para vinculação de parâmetros [1], tipos simples, "(int, bool, double e assim por diante), mais TimeSpan, DateTime, Guid, decimal e string" serão lidos automaticamente do URI. O atributo [FromURI] é necessário quando o parâmetro não é um desses tipos para forçar a leitura daqueles do URI em vez de seu local padrão, o corpo. Para fins de integridade, o atributo [FromBody] faz essencialmente o oposto com tipos complexos. [1] docs.microsoft.com/en-us/aspnet/web-api/overview/… )
Seb Andraos,
1

Você pode simplesmente fazer o seguinte:

    [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}")));
    }

Se você precisar acessar cada elemento separadamente, basta consultar Request.Query.

SpiritBob
fonte
1

Os métodos devem ser assim:

[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)
}

Observe que o segundo método retorna uma matriz de objetos e o nome do controlador está em plurar (Pessoas, não Pessoa).

Então, se você quiser obter o recurso por id, ele será:

api/persons/1

se você quiser pegar objetos por algum critério de pesquisa, como nome e etc, você pode fazer a pesquisa assim:

api/persons?firstName=Name&...

E avançando, se você quiser receber os pedidos dessa pessoa (por exemplo), deve ser assim:

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

E método no mesmo controlador:

    [HttpGet("{personId}/orders")]
    public Orders[] Get(int personId, int skip, int take, etc..)
Paulius K.
fonte
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
fonte
0

Maneira mais simples,

Controlador:

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

Pedido do carteiro:

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

Ponto de aprendizagem: Solicitar padrão exato será aceito pelo Controlador.

Assimhara Buddhika
fonte