Representar acciones (verbos) en REST URI

16

Tengo que realizar una operación de impresión para mis documentos de cliente. Necesito que se realicen también las otras operaciones estándar, como agregar, actualizar, eliminar. entonces, tengo lo siguiente:

  • Para crear un nuevo cliente:
    URI = / customer / {id}, type = POST, Methodname = CreateCustomer ()
  • Para actualizar:
    URI: / customer / {id}, type = PUT, method = UpdateCstomer ()
  • Para Eliminar cliente:
    URI = / customer / {id}, type = DELETE, Methodname = DeleteCustomer ()
  • Para Ver:
    URI: / customer / {id}, escriba = GET, method = GetCustomer ()

Ahora, si necesito imprimir un documento para ese cliente, necesito una función de impresión. Mi URI puede verse así: / customer / {id}, type = POST, method = PrintCustomer (). Pero he usado ese tipo de URI y POST para CreateCustomer. Quería que el URI se viera así: / customer / Print / {id}, type = POST, method = PrintCustomer ().

Pero no puedo tener el verbo "Imprimir" en mi URI. ¿Cuál es la mejor manera de hacer esto? Pensé en / customer / document / {id} como el URI ... pero tendré el mismo problema. Tendría las operaciones CRUD en el "documento". Entonces, nuevamente me quedo sin lo que usaría para "imprimir". Por favor avise.

Nitya Maha
fuente
2
La impresión suele ser una operación del lado del cliente, así que tengo curiosidad: ¿cómo es su configuración de modo que requiera que envíe un comando a un servidor REST?
Shauna
2
@Shauna No necesariamente, el URI puede ser una solicitud al servidor de una versión para imprimir del recurso (es decir, una vista diferente).
Evan Plaice
1
@EvanPlaice: lo suficientemente justo, aunque eso todavía deja el acto de imprimir al cliente (que, incluso después de buscar una versión amigable para imprimir del lado del servidor, luego decidiría en qué dispositivo imprimir y enviar el comando de impresión en sí, incluso si eso el comando va a un servidor de impresión). Una solicitud para obtener una versión impresa de un recurso sería lógicamente ... bueno ... GET.
Shauna
@Shauna Activar un trabajo de impresión solo desde una solicitud HTTP sería imposible debido a la seguridad del navegador. Una solicitud de una versión para imprimir es solo una solicitud GET, pero aún necesita una forma de especificar que el navegador debe representar la versión imprimible. Puede especificar una URL diferente, pero eso violaría los principios de REST porque en realidad no está solicitando un recurso diferente, solo una transformación diferente del mismo recurso. De ahí la razón para especificar la transformación a través de un parámetro de consulta y / o tipo de contenido.
Evan Plaice
No tengo suficiente representante para publicar como respuesta, pero me parece interesante que tyk.io/rest-never-crud argumenta que POST /customers/123/printes algo válido.
jlh

Respuestas:

9

POSTno significa "crear", significa "proceso". Puede crear un nuevo recurso publicando una solicitud adecuada en un recurso existente (es decir, publicar en /customerspara crear un nuevo cliente). Pero también puede usar POSTpara completar todas las otras acciones que no corresponden a un paradigma CRUD ordenado.

En el caso de la impresión, debe considerar el acto de imprimir como un recurso en sí mismo. Le está pidiendo al sistema que cree un "trabajo de impresión" para usted. Esto significa que puede tener un prints/recurso que actúa como contenedor para todas las impresiones solicitadas. Cuando desea imprimir algo, es POSTun documento de este recurso que contiene toda la información sobre la impresión que desea crear, identificando los recursos que desea imprimir con enlaces a ellos.

Como documento JSON, podría verse así:

{
   contents: ["http://site/customers/12345"],
   paper-size: "A4",
   duplex: "true"
}

Obviamente, debe personalizar esto para que sea relevante para lo que desea hacer. La clave es que está identificando otros recursos para imprimir especificando su URL.

En respuesta a la solicitud, simplemente puede devolver a 200 OKo a 204 No-Contenty tratarlo como un proceso de disparar y olvidar. Sin embargo, si desea mejorarlo, puede regresar 201 Createdy especificar la URL del trabajo de impresión recién creado, por ejemplo /prints/12345.

Luego, un usuario podría realizar una acción GETen el recurso para ver el estado de su trabajo de impresión (pendiente, en progreso, etc.), o podría solicitar la cancelación del trabajo emitiendo un DELETE.

Una vez que reformule el problema en términos de recursos, el diseño RESTful debería surgir de forma natural y darle la oportunidad de expandirse y mejorar en formas que quizás no haya considerado de inmediato.

Paul Turner
fuente
2
POST generalmente significa crear / insertar, mientras que poner generalmente significa guardar / actualizar actualización. Así es como se define en REST, incluso si no es como se usa generalmente en HTML.
Evan Plaice
2
@EvanPlaice, los nombres de especificaciones HTTP PUT como el verbo crear / actualizar (utiliza un modelo crear + actualizar en lugar del más familiar crear + recuperar + actualizar) y POST es el verbo "procesamiento de datos", así como el verbo "agregar" . Roy Fielding en su blog describió POST como el verbo a usar cuando no desea estandarizar la operación. POST adquiere la semántica de "crear" cuando considera que agrega un nuevo elemento a una colección de elementos. En este caso, Tragedian golpeó el clavo en la cabeza usando POST para procesar o agregar un trabajo de impresión.
Rob
@RobY OK, eso tiene sentido. Como ejemplo, PUT podría usarse para representar un SPROC diseñado para ingresar datos en una base de datos. Mientras que un POST podría completar los pasos intermedios y las mutaciones requeridas para recopilar / preparar esos datos. El diseño de la operación POST podría cambiar o reemplazarse a medida que evoluciona el diseño, pero las operaciones PUT representan el modelo que (idealmente) no debería cambiar. Actualizaría mi respuesta, pero esta ya explica muy bien la diferencia.
Evan Plaice el
4

Ya hice esto antes. Para imprimir un documento, solo devuelvo una versión en pdf de un recurso. El cliente solo necesita enviar una solicitud GET para el recurso con Aceptar encabezado aplicación / pdf.

Esto también evita crear nuevos URI para recursos temporales como trabajos de impresión. El uso del encabezado HTTP también forma parte de REST y mantiene limpio el URI.

imel96
fuente
3

Simplemente agregue un parámetro al GET del URI actual

Es bastante típico usar un URI para múltiples acciones.

Si está hablando del mismo recurso pero una acción diferente, lo definiría como un parámetro.

/ customer / {id}? print = true

Luego, cuando define su método GET, detecta la presencia del parámetro de impresión y lo maneja de manera diferente.

REST se define de la siguiente manera:

  • POST: crea un registro, activo o recurso
  • PUT - Actualización, un registro, activo o recurso
  • BORRAR - Eliminar un registro, activo o recurso

GET, por otro lado, está destinado a ser utilizado de múltiples maneras porque generalmente hay muchas formas diferentes de recuperar un recurso. Por eso también las solicitudes GET se representan como una cadena de consulta. Si estaba trabajando con un recurso de base de datos, literalmente estaría recuperando una vista a través de una consulta, pero REST se abstrae intencionalmente a un nivel superior porque está diseñado para manejar con muchos tipos diferentes de recursos.

La especificación REST es bastante progresista, a pesar de que las API solo recientemente han comenzado a usarla en gran medida.

Si está interesado en aprender más sobre los protocolos REST, le recomiendo que lea "Los que odian odiarán a HATEOAS ".


Actualizar:

@Shauna señaló un agujero interesante en mi razonamiento. No existe una forma correcta y muchas formas se consideran aceptables. Lo pensé un poco más y dado que su uso previsto es transformar los datos en una representación diferente, tiene sentido definir la transformación como un nuevo tipo MIME.

Por ejemplo, podría representar el URI como:

/customer/{id}+print

Donde podría establecer la respuesta Content-Type en text / html + print. De esa manera, también tendría la opción de definir más transformaciones en el futuro.

Por ejemplo:

// for application/json
/customer/{id}+json

// for application/atom+xml
/customer/{id}+atom

De cualquier manera, todas las formas son aceptables. La implementación que decida depende más de las preferencias personales y de las capacidades de su servidor.

Aparte: Permítanme aclarar, ya que parece haber cierta confusión. El parámetro de consulta 'print' y / o el tipo de contenido se utilizan para especificar cómo se transforma el recurso. No cómo activar un trabajo de impresión física. Por razones de seguridad, el acceso a nivel de hardware siempre se deja al usuario / cliente / navegador.

Evan Plaice
fuente
Para agregar: como alternativa al uso de la cadena de consulta ( ?print=true), también puede usar parámetros de URI (es decir, - /customer/{id}/printable). El que use dependerá en gran medida del estándar que su sistema (CMS, framework, código en general) esté configurado para manejar. Ambos se consideran válidos y aceptables .
Shauna
@Shauna Técnicamente, el mejor enfoque sería emplear un tipo MIME específico para imprimir con el URI '/ customer / {id} + print' y una respuesta Tipo MIME de texto / html + print. La ventaja de este enfoque es que puede crear transformaciones para muchos tipos MIME (ex text / html, text / x-markdown, application / json, etc.) para el mismo URI. La desventaja de la solución que presenta es que necesitaría crear un URI adicional (y definir otra ruta) para cada tipo MIME diferente. De alguna manera derrota el propósito de usar REST.
Evan Plaice
(cont.) Yo diría que los hacks URI son un antipatrón introducido principalmente por la comunidad ROR, pero eso no significa que no sean útiles. Con la llegada de mejores servidores HTTPd de bajo nivel, se está volviendo más fácil implementar REST de una manera que aprovecha al máximo su potencial. Las cosas han avanzado mucho desde los días en que Apache y enrutar todo a través de index.html era la única opción.
Evan Plaice
2
GET no debe hacer cambios de estado ni tener efectos secundarios. Tenga en cuenta que GET es idempotente, lo que significa que el middleware puede volver a intentar la solicitud si no se verifica. En este caso, cada reintento daría como resultado una copia nueva y recién impresa del documento. ;)
Rob
@RobY Estaba asumiendo que la acción 'imprimir' no iba a manejar el proceso de imprimir físicamente el documento, ya que eso sería mejor para el navegador y el controlador de impresión. Por el contrario, la salida de medios / impresión devolvería una representación 'amigable para imprimir' del documento. Por lo tanto, se mantiene la idempotencia. Buen punto, sin embargo, enviar trabajos de impresión a través de Internet de manera apátrida sería un mal momento.
Evan Plaice el