Cómo pasar varios parámetros a un método get en ASP.NET Core

107

¿Cómo puedo pasar múltiples parámetros a los métodos Get en un controlador MVC 6? Por ejemplo, quiero poder tener algo como lo siguiente.

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

    }
}

Entonces puedo consultar como.

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

Respuestas:

91

También puedes usar esto:

// 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 refiérase a metalheart y metalhearty Mark Hughespara un enfoque posiblemente mejor.

antlas
fuente
21
Hasta que necesites que todos tengan el mismo apellido :)
Phillip Copley
14
Esa es una forma realmente mala de diseñar rutas API ... No es RESTful en absoluto.
Thomas Levesque
7
El enfoque anterior parece muy engorroso, no entiendo por qué tiene tantos votos a favor.
Bernoulli IT
1
@ThomasLevesque ¿Qué quisiste decir con no estar DESCANSADO?
Bruno Santos
2
@BrunoSantos no sigue los principios de REST. Se supone que los URI identifican recursos de forma única. Este no es el caso aquí (puede haber varias personas con el mismo nombre y apellido, y una dirección ciertamente no puede considerarse un identificador)
Thomas Levesque
60

¿Por qué no usar solo una acción del 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);
}

Otra opción es usar el enrutamiento de atributos, pero luego necesitaría tener un 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)
{
}
Corazón de metal
fuente
Sí, lo resuelvo ahora usando solo una acción que toma todos los atributos en los que quiero poder buscar una Persona. Como una búsqueda general. Sin embargo, preferiría que hubiera una forma de sobrecargar acciones en un controlador, pero ese podría no ser el caso.
mstrand
3
esto no funciona con .net core 2.0, ya que no se genera una plantilla de URL válida.
ZZZ
44

Para analizar los parámetros de búsqueda de la URL, debe anotar los parámetros del método del controlador con [FromQuery], por ejemplo:

[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
fuente
6
¿Por qué necesitarías esto? el enlace de parámetros de la cadena de consulta ocurre de forma predeterminada ...
metalheart
He intentado ambos pero la sobrecarga como intento hacer falla con o sin [FromQuery]
mstrand
2
@mstrand He actualizado - pruébalo, mira las [HttpGet]anotaciones adicionales , los diferentes nombres de métodos y la ruta específica en [Route]- las rutas deberían ser completamente explícitas ahora, lo que elimina algunos problemas posibles.
Mark Hughes
9

Creo que la forma más sencilla es simplemente usar AttributeRouting.

[Route("api/YOURCONTROLLER/{paramOne}/{paramTwo}")]
    public string Get(int paramOne, int paramTwo)
    {
        return "The [Route] with multiple params worked";
    }
Sunil Dhappadhule
fuente
¿Puedo usar el tipo de referencia preferido? Es decir,int paramOne, string paramTwo
k4s
Use [Route ("api / YOURCONTROLLER / {paramOne} / {paramTwo?}")] Si desea que su segundo parámetro sea opcional
Anytoe
8

Sugeriría usar un 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 asignará los campos a su objeto.

Esto hará que sea mucho más fácil pasar por sus parámetros y dará como resultado un código mucho más claro.

Sebastián
fuente
5

Para llamar a get con varios parámetros en web api core

  [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
fuente
3

Para agregar más detalles sobre la sobrecarga sobre la que preguntó en su comentario después de otra respuesta, aquí hay un resumen. Los comentarios en el ApiControllermuestran qué acción se llamará con 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 };
    }
}

Solo necesitaría una única ruta para esto, en caso de que se preguntara:

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

y podría probarlo todo con esta vista MVC, o algo similar. Sí, sé que se supone que no debes mezclar JavaScript con marcado y no estoy usando bootstrap como lo harías normalmente, pero esto es solo para fines de demostración.

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

ingrese la descripción de la imagen aquí

NB: eliminé FromURI. Aún así, puedo pasar el valor de la URL y obtener el resultado. Si alguien sabe benfifts usando fromuri, hágamelo saber

saktiprasad swain
fuente
Como se estipula en la documentación para la vinculación de parámetros [1] tipos simples, "(int, bool, double, etc.), más TimeSpan, DateTime, Guid, decimal y string" se leerán automáticamente desde el URI. El atributo [FromURI] es obligatorio cuando el parámetro no es uno de estos tipos para forzar la lectura de los del URI en lugar de su ubicación predeterminada, el cuerpo. En aras de la integridad, el atributo [FromBody] hace esencialmente lo contrario con los tipos complejos. [1] docs.microsoft.com/en-us/aspnet/web-api/overview/… )
Seb Andraos
1

Simplemente puede hacer lo siguiente:

    [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 necesita acceder a cada elemento por separado, simplemente consulte Request.Query.

SpiritBob
fuente
1

Los métodos deberían ser así:

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

Tenga en cuenta que el segundo método devuelve una matriz de objetos y el nombre del controlador está en plurar (Persons not Person).

Entonces, si desea obtener el recurso por id, será:

api/persons/1

si desea tomar objetos por algunos criterios de búsqueda como el nombre, etc., puede realizar una búsqueda como esta:

api/persons?firstName=Name&...

Y avanzando, si quieres tomar las órdenes de esa persona (por ejemplo), debería ser así:

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

Y método en el mismo controlador:

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

La forma más sencilla

Controlador:

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

Solicitud del cartero:

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

Punto de aprendizaje: el controlador aceptará la solicitud de patrón exacto.

Thushara Buddhika
fuente