¿Cómo debo pasar múltiples parámetros a un ASP.Net Web API GET?

136

Estoy usando la API web .Net MVC4 para (con suerte) implementar una API RESTful. Necesito pasar algunos parámetros al sistema y hacer que realice alguna acción, luego devolver una lista de objetos como resultado. Específicamente, paso dos fechas y devuelvo registros que se encuentran entre ellas. También estoy haciendo un seguimiento de los registros devueltos para que las llamadas posteriores no se vuelvan a procesar en el sistema.

He considerado algunos enfoques:

  1. Serializar los parámetros en una sola cadena JSON y separarlos en la API. http://forums.asp.net/t/1807316.aspx/1

  2. Pase los parámetros en la cadena de consulta.
    ¿Cuál es la mejor manera de pasar múltiples parámetros de consulta a una API relajante?

  3. Definición de los parámetros en la ruta: api / controller / date1 / date2

  4. Usando un POST que inherentemente me permite pasar un objeto con params.

  5. Investigando ODATA ya que la API web (actualmente) lo admite. Todavía no he hecho mucho con esto, así que no estoy muy familiarizado con él.

Parece que las prácticas REST adecuadas indican cuándo se extraen los datos, debe usar un GET. Sin embargo, GET también debería ser nulipotente (no produce efectos secundarios), y me pregunto si mi implementación específica viola eso ya que marco registros en el sistema API, por lo tanto, estoy produciendo efectos secundarios.

También me llevó a la cuestión de admitir parámetros variables. Si la lista de parámetros de entrada cambia, sería tedioso tener que redefinir su ruta para la Opción 3 si eso sucede mucho. Y qué podría pasar si los parámetros se definen en tiempo de ejecución ...

En cualquier caso, para mi implementación específica, ¿qué opción (si hay alguna) parece mejor?

sig606
fuente

Respuestas:

10

¿Qué significa esta marca de registro? Si esto se usa solo para fines de registro, usaría GET y deshabilitaría todo el almacenamiento en caché, ya que desea registrar cada consulta para estos recursos. Si el marcado de registros tiene otro propósito, POST es el camino a seguir. El usuario debe saber que sus acciones afectan el sistema y el método POST es una advertencia.

LukLed
fuente
Al marcar me refiero a simplemente rastrear qué registros se procesan y devuelven para que las llamadas posteriores no los repitan. En mi caso, solo estoy insertando en otra tabla para rastrear los procesados.
sig606
En este momento lo tengo implementado como POST principalmente por la razón que usted dijo: las acciones suceden y el consumidor las conoce. Además, parece fácil y más flexible con respecto a pasar datos diferentes.
sig606
@ sig606: POST es el camino a seguir para mí, pero su protocolo no parece ser seguro. ¿Qué sucede si algo sucede y los registros se recuperan del lado del cliente, pero no se procesan debido a un error? No los devolverá más y el cliente se quedará con datos perdidos.
LukLed
En este momento, mi API solo devuelve registros después de que se procesaron. Entonces el consumidor pasa la API dos fechas. Los registros entre esas dos fechas se procesan y marcan. Luego, los datos se devuelven a la persona que llama. Supongo que si algo sucede durante el procesamiento o después del procesamiento antes de llegar al cliente, tengo un problema.
sig606
141

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

Es obvio dentro de su controlador, ¿por qué querría esto en su WebApiConfigarchivo Global ?

Ejemplo:

    [Route("api/YOURCONTROLLER/{paramOne}/{paramTwo}")]
    public string Get(int paramOne, int paramTwo)
    {
        return "The [Route] with multiple params worked";
    }

Los {}nombres deben coincidir con sus parámetros.

Tan simple como eso, ahora tiene un separado GETque maneja múltiples parámetros en esta instancia.

Mark Pieszak - Trilon.io
fuente
12
Esto es genial. La mayoría de las personas recomiendan configurar la ruta en el WebApiConfigarchivo, pero esto es realmente mejor.
rhyek
44
De hecho, nosotros (la mayoría de las personas) recomendamos tener un área de administración centralizada para su configuración. En el caso de las API web (Microsoft o no), los patrones centralizados para REST son clave. El enrutamiento de atributos es lindo, pero hace que las excepciones únicas sean demasiado tentadoras.
David Betz
3
De acuerdo, necesito actualizar mi respuesta en realidad. Hay una forma mucho mejor de hacer múltiples parámetros con GET. Publiqué esto cuando era más nuevo en WebAPI, ahora no uso AttributeRouting (a menos que simplemente no quiera crear un nuevo Controlador), y pase todos los Parámetros en QueryString, se asignan automáticamente. Actualización cuando tengo la oportunidad de que la gente no use este método anterior
Mark Pieszak - Trilon.io
¿Hay alguna manera de establecer un Routepara parámetros con nombre (por ejemplo, parámetros de consulta)?
Shimmy Weitzhandler
1
Si se requiere el nombre del método de acción, esto se puede modificar para acomodarlo. [Ruta ("api / YOURCONTROLLER / Get / {paramOne} / {paramTwo}")] cadena pública Get (int paramOne, int paramTwo) {return "something"; }
Dash
49

Simplemente agregue una nueva ruta a las WebApiConfigentradas.

Por ejemplo, para llamar:

public IEnumerable<SampleObject> Get(int pageNumber, int pageSize) { ..

añadir:

config.Routes.MapHttpRoute(
    name: "GetPagedData",
    routeTemplate: "api/{controller}/{pageNumber}/{pageSize}"
);

Luego agregue los parámetros a la llamada HTTP:

GET //<service address>/Api/Data/2/10 
Graham Wright
fuente
10
Esta parece ser la única respuesta que enumera todas las partes. Deseo que alguien describa mejor cómo usar el api/controller?start=date1&end=date2estilo URI.
Hot Licks
La respuesta de @Hot Licks Andrew Veriga funciona bien con argumentos de cadena de consulta. Básicamente, vincula los nombres de las cadenas de consulta a las propiedades de la clase y los pasa a su método. Su método tomará un argumento de clase única marcado con el atributo [FromUri] y tendrá sus argumentos de cadena de consulta como sus propiedades.
David Peterson
Buena cosa. ¡Gracias!
Hugo Nava Kopp
hola @HotLicks y GrahamWright, ¿crees que puedes responder a esta pregunta? Gracias, stackoverflow.com/questions/57565318/…
45

Solo tuve que implementar una API RESTfull donde necesito pasar parámetros. Lo hice pasando los parámetros en la cadena de consulta en el mismo estilo que se describe en el primer ejemplo de Mark "api / controller? Start = date1 & end = date2"

¿En el controlador usé un consejo de URL dividida en C #?

// uri: /api/courses
public IEnumerable<Course> Get()
{
    NameValueCollection nvc = HttpUtility.ParseQueryString(Request.RequestUri.Query);
    var system = nvc["System"];
    // BL comes here
    return _courses;
}

En mi caso, estaba llamando a WebApi a través de Ajax con el siguiente aspecto:

$.ajax({
        url: '/api/DbMetaData',
        type: 'GET',
        data: { system : 'My System',
                searchString: '123' },
        dataType: 'json',
        success: function (data) {
                  $.each(data, function (index, v) {
                  alert(index + ': ' + v.name);
                  });
         },
         statusCode: {
                  404: function () {
                       alert('Failed');
                       }
        }
   });

Espero que esto ayude...

Nigel Findlater
fuente
2
Supongo que no está utilizando WebApi porque ParameterBinding asignará su cadena de consulta a los parámetros de su método de API automáticamente ...
emp
1
Sí, una mejor manera sería usar y atribuir como [Ruta ("api / DbMetaData / {system} / {searchString}")] y luego agregar los parámetros a Get (sistema de cadenas, string searchString) y luego llamar con " ... api / DbMetaData / mysystem / mysearchstring "
Nigel Findlater
Usé su ejemplo en mi C # MVC WebApi y funcionó bien. 1 por ejemplo
Si8
38

Encontré una excelente solución en http://habrahabr.ru/post/164945/

public class ResourceQuery
{
   public string Param1 { get; set; }
   public int OptionalParam2 { get; set; }
}

public class SampleResourceController : ApiController
{
    public SampleResourceModel Get([FromUri] ResourceQuery query)
    {
        // action
    }
}
Andrew Veriga
fuente
55
La pista aquí es el [FromUri]
tranceporter
2
Aunque el artículo está en ruso, @tranceporter tiene razón. El "FromUri" parece una excelente manera de obtener los parámetros de la url. Otro artículo que podría ser útil: asp.net/web-api/overview/formats-and-model-binding/…
Greg
¡Esto es lo que he estado haciendo durante bastante tiempo y funcionó muy bien! También recomendaría esta solución.
David Peterson el
Si llama a otro método auxiliar (no al Get), ¿puede seguir utilizando [FromUri]? Parece que no puedo hacer que eso funcione.
Jocull
8

El uso de GET o POST se explica claramente por @LukLed . Con respecto a las formas en que puede pasar los parámetros, sugeriría ir con el segundo enfoque (tampoco sé mucho sobre ODATA ).

1. Serializar los parámetros en una sola cadena JSON y separarlos en la API. http://forums.asp.net/t/1807316.aspx/1

Esto no es fácil de usar ni SEO.

2. Pase los parámetros en la cadena de consulta. ¿Cuál es la mejor manera de pasar múltiples parámetros de consulta a una API relajante?

Este es el enfoque preferible habitual.

3.Definición de los parámetros en la ruta: api / controller / date1 / date2

Este definitivamente no es un buen enfoque. Esto hace sentir que alguien date2es un sub recurso de date1y ese no es el caso. Tanto el date1y date2son parámetros de consulta y viene en el mismo nivel.

En un caso simple, sugeriría un URI como este,

api/controller?start=date1&end=date2

Pero personalmente me gusta el siguiente patrón de URI, pero en este caso tenemos que escribir un código personalizado para mapear los parámetros.

api/controller/date1,date2
VJAI
fuente
En realidad, esas fueron mis explicaciones de origen. Creo que LukLed brilló mis etiquetas y enlace URL.
sig606
En cuanto a SEO, en este caso no se aplicaría. Este código será "servidor a servidor", por lo que no me importaría si el mundo exterior lo descubriera alguna vez. De hecho, tengo que asegurarme de que se tomen las medidas de seguridad adecuadas para evitar el acceso aleatorio. Tuve que hacer la serialización JSON para otra parte del sistema (parece ser un error al intentar PUBLICAR grandes listas de obj, así que tuve que serializar en cadena), por lo que no sería demasiado difícil en este caso .
sig606
1
Espero que ya tengas respuestas, ¿por qué haces una pregunta?
VJAI
2
Perdón por esta respuesta tardía, Mark. Había intentado algunas soluciones pero no estaba seguro de cuál era el mejor y estaba tratando de seguir los enfoques estándar de la industria, así que publiqué aquí en SO.
sig606
1
@ Mark Algo parecido a lo siguiente: stackoverflow.com/questions/9681658/… ?
RredCat
3
 [Route("api/controller/{one}/{two}")]
    public string Get(int One, int Two)
    {
        return "both params of the root link({one},{two}) and Get function parameters (one, two)  should be same ";
    }

Ambos parámetros del enlace raíz ({uno}, {dos}) y los parámetros de la función Obtener (uno, dos) deben ser iguales

Ashwath Hegde
fuente
2

Sé que esto es muy viejo, pero quería lo mismo recientemente y esto es lo que encontré ...

    public HttpResponseMessage Get([FromUri] string var, [FromUri] string test) {
        var retStr = new HttpResponseMessage(HttpStatusCode.OK);
        if (var.ToLower() == "getnew" && test.ToLower() == "test") {
            retStr.Content = new StringContent("Found Test", System.Text.Encoding.UTF8, "text/plain");
        } else {
            retStr.Content = new StringContent("Couldn't Find that test", System.Text.Encoding.UTF8, "text/plain");
        }

        return retStr;
    }

Así que ahora en su dirección / URI / ...

http (s): // myURL / api / myController /? var = getnew & test = test

Resultado: "Prueba encontrada"


http (s): // myURL / api / myController /? var = getnew & test = anything

Resultado: "No se pudo encontrar esa prueba"

Rick Riggs
fuente
Personalmente, me gusta este estilo en C #, porque puedo cambiar la firma del método original y sobrecargar exactamente lo que estoy tratando de lograr, sin modificar las configuraciones de enrutamiento. Espero que ayude a otros que están acostumbrados a este enfoque (quizás anticuado) de hacer una solicitud GET.
Rick Riggs el
1
Tuve que crear una API de eventos que es consumida por una aplicación de calendario de terceros, que utiliza este enfoque. ¡Me alegro de haber encontrado esta respuesta!
clayRay
0
    public HttpResponseMessage Get(int id,string numb)
    {
        //this will differ according to your entity name
        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