Utillizar los métodos PUT
, POST
y DELETE
en lugar del método GET
para alterar el estado de un recurso.
PUT /usuario/711?activate
PUT /usuario/711/activate
GET /users/711?activate
GET /usuario/711/activate
/carros
/carros/711
/getAllCars
/createNewCar
/deleteAllCars
/cars
/users
/products
/settings
/car
/user
/product
/setting
Utilizar Subrecursos para relaciones
Si un recurso está relacionado con otro, utilizar notación para sub-recursos:
GET /cars/711/drivers/ Regresa una lista de drivers para el carro 711
GET /cars/711/drivers/4 regresa un driver #4 para el carro 711
El estándar HTTP provee al rededor de 70 códigos para estados que describen los valores que regresan.
200 – OK – Eyerything is working
201 – OK – New resource has been created
204 – OK – The resource was successfully deleted
304 – Not Modified – The client can use cached data
400 – Bad Request – The request was invalid or cannot be served. The exact error should be explained in the error payload. E.g. „The JSON is not valid"
401 – Unauthorized – The request requires an user authentication
403 – Forbidden – The server understood the request, but is refusing it or the access is not allowed.
404 – Not found – There is no resource behind the URI.
422 – Unprocessable Entity – Should be used if the server cannot process the enitity, e.g. if an image cannot be formatted or mandatory fields are missing in the payload.
500 – Internal Server Error – API developers should avoid this error. If an error occurs in the global catch blog, the stracktrace should be logged and not returned as response.
Se tiene que considerar el payload que se tiene que pagar para manejar de manera adecuada los mensajes anteriores en una respuesta json. Por ejemplo, el cliente del API
deberá de saber como manejar una respuesta como la siguiete:
{
"errors": [
{
"userMessage": "Disculpa, el recurso solicitado no existe",
"internalMessage": "El usuario no existe en la base de datos",
"code": 34,
"more info": "http://conacyt.gob.mx/api/v1/errors/12345"
}
]
}
Los métodos PUT
, POST
o PATCH
pueden llevar a cabo modificaciones a los campos de un recurso, por lo tanto deberán de indicar, como parte de la respuesta, sí la operación fue de actualización (updated) o creación (created).
En el caso de una petición POST
que resulte en una creación, se deberá de utilizar el HTTP 201 status code
e incluir un Location Header
que contenga una URL con el nuevo recurso.
Utilizar los Headers del protocolo HTTP para los formatos de serialización
Tanto el cliente como el servidor necesitan saber que tipo de formato se está utilizando durante la comunicación. El formato tiene que ser especificado en el header del protocolo HTTP: {: .note}
Content-Type
define el formato de la solucitud.
Accept
define una lista de formatos de respuesta aceptables.
Proveer operaciones de filtrado (filtering), ordenamiento (sorting), selección de campos (field selection) y Paginación (Paging) de colecciones {: .note}
Utilizar sólo un parámetro de busqueda por cada campo que se desea filtrar:
GET /cars?color=red Regresa una lista de todos los carros Rojos
GET /cars?seats<=2 Regresa una lista de los carros que tienen un máximo de 2 asientos
Similar a filtering
, en Sorting
se utiliza un parámetro genérico llamdo sort que puede ser utilizado para describir reglas de ordenamiento; generalmente el ordenamiento es de tipo ascendente o descendente sobre varios campos separados por comas:
# Recupera los carros ordenamos de manera ascendente en el campo id
GET /cars?sort=id,asc
La consulta anterior regresará una lista de carros ordenados de manera ascendente en el campo id y de manera ascendente en el campo modelo.
# Recuepera todos los tickets en orden descendiente en el campo id.
GET /tickets?sort=id,desc
Dar la posibilidad de elegir los campos que se desean regresar. Esto ayuda a reducir el tráfico en la red.
Ejemplo:
# Para la entidad Car
GET /cars?fields=manufacturer,model,id,color
# Para la entidad Tickets
GET /tickets?fields=id,subject,customer_name,updated_at&state=open&sort=-updated_at
Utilizar la propiedad page
y size
para limitar el número de resultados que se desean recuperar.
Ejemplo:
GET /cars?page=0&size=5
Los links para las páginas siguientes, o previas, deberán de ser provista en el json que se regresa. Para la navegación a través del API deberá de ser a través de las siguientes propiedades:
last : true o false
totalElements : Número de elementos en la base de datos
totalPages : Número de páginas que pueden mostrar para un determinado tamaño
first : true si el resultado es la primer página de la colección
numberOfElements : Número de elementos que se tiene en la página actual
sort : true Sí los valores están ordenados
size : Tamaño de elementos que se muestran por ágina
number : Número de la página
Un ejemplo de la respuesta se muestra a continuación:
Solicitud:
curl -u dads:dads http://localhost:8080/rest-spring-jpa/dataStores?page=3\&size=2
Respuesta:
{
"content": [
{
"id": 1,
"dataStoreType": {
"id": 1,
"name": "jdbc",
"description": "Data Store for JDBC connector"
},
"url": "jdbc:mysql://localhost/membership?autoReconnect=true",
"driverClass": "com.mysql.jdbc.Driver",
"username": "developer",
"password": "temporal123",
"tableTypes": "TABLE,VIEW"
}
],
"last": true,
"totalElements": 1,
"totalPages": 1,
"first": true,
"numberOfElements": 1,
"sort": null,
"size": 1,
"number": 0
}
Algunos proxies sólo permiten métodos POST
y GET
. Para soportar una API
de tipo RESTful se deberá de enviar la información utilizando la directiva X-HTTP-Method-Override
del método POST y en el Header del protocolo HTTP. {: .note}
RESOURCE:
http://api.conacyt.mx/v1/carros
POST (create)
crea un nuevo carro
GET (read)
consulta la lista de carros disponibles
PUT (update)
actualiza uno o varios carros
DELETE (delete)
borra todos los carros disponibles
A continuación, se muestran más ejemplos de peticiones HTTP utilizando los lineamientos vistos hasta el momento:
HTTP Método | Resource Endpoint | input | Success Response | Error Response |
GET | /polls | Body:Empty | Status: 200 Body:Poll List | Status: 500 |
POST | /polls | Body:New Poll data | Status:201 Body: Newly created poll id | Status: 500 |
PUT | /polls | N/A | N/A | Status: 400 |
DELETE | /polls | N/A | N/A | Status: 400 |
GET | /polls/{pollId} | Body:Empty | Status: 200 Body: Poll data | Status: 400 o 500 |
POST | /polls/{pollId} | N/A | N/A | Status: 400 |
PUT | /polls/{pollId} | Body:Poll data with Updates | Status:200 Body:Empty | Status: 404 o 500 |
DELETE | /polls/{pollId} | Body:Empty | Status:200 | Status: 404 o 500 |
Entidad : DataStore
Consultar todos los DataStore:
/**
* GET /data-stores : get all the dataStores.
*
* @param pageable
* the pagination information
* @return the ResponseEntity with status 200 (OK) and the list of
* dataStores in body
*/
@GetMapping("/data-stores")
@Timed
public ResponseEntity<List<DataStore>> getAllDataStores(@ApiParam Pageable pageable) {
log.debug("REST request to get a page of DataStores");
Page<DataStore> page = dataStoreService.findAll(pageable);
HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/data-stores");
return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
}
Para consultar un elemento por su identificador
/**
* GET /data-stores/:id : get the "id" dataStore.
*
* @param id
* the id of the dataStore to retrieve
* @return the ResponseEntity with status 200 (OK) and with body the
* dataStore, or with status 404 (Not Found)
*/
@GetMapping("/data-stores/{id}")
@Timed
public ResponseEntity<DataStore> getDataStore(@PathVariable String id) {
log.debug("REST request to get DataStore : {}", id);
DataStore dataStore = dataStoreService.findOne(id);
return ResponseUtil.wrapOrNotFound(Optional.ofNullable(dataStore));
}
Para crear una nueva entidad de tipo DataStore
/**
* POST /data-stores : Create a new dataStore.
*
* @param dataStore
* the dataStore to create
* @return the ResponseEntity with status 201 (Created) and with body the
* new dataStore, or with status 400 (Bad Request) if the dataStore
* has already an ID
* @throws URISyntaxException
* if the Location URI syntax is incorrect
*/
@PostMapping("/data-stores/test")
@Timed
public ResponseEntity<DataStore> testConnection(@Valid @RequestBody DataStore dataStore) throws URISyntaxException {
log.info("REST connection DataStore : {}", dataStore);
if (dataStoreService.testConnection(dataStore)) {
return ResponseEntity.ok().headers(HeaderUtil.createSuccessDataStoreStatus(ENTITY_NAME, "ok"))
.body(dataStore);
} else {
return ResponseEntity.badRequest()
.headers(HeaderUtil.createFailureDataStoreStatus(ENTITY_NAME, "failure"))
.body(dataStore);
}
}
Para actualizar un elemento de tipo DataStore
/**
* PUT /data-stores : Updates an existing dataStore.
*
* @param dataStore
* the dataStore to update
* @return the ResponseEntity with status 200 (OK) and with body the updated
* dataStore, or with status 400 (Bad Request) if the dataStore is
* not valid, or with status 500 (Internal Server Error) if the
* dataStore couldnt be updated
* @throws URISyntaxException
* if the Location URI syntax is incorrect
*/
@PutMapping("/data-stores")
@Timed
public ResponseEntity<DataStore> updateDataStore(@Valid @RequestBody DataStore dataStore)
throws URISyntaxException {
log.debug("REST request to update DataStore : {}", dataStore);
if (dataStore.getId() == null) {
return createDataStore(dataStore);
}
DataStore result = dataStoreService.save(dataStore);
return ResponseEntity.ok()
.headers(HeaderUtil.createEntityUpdateAlert(ENTITY_NAME, dataStore.getId().toString())).body(result);
}
Para borrar un recurso:
/**
* DELETE /data-stores/:id : delete the "id" dataStore.
*
* @param id
* the id of the dataStore to delete
* @return the ResponseEntity with status 200 (OK)
*/
@DeleteMapping("/data-stores/{id}")
@Timed
public ResponseEntity<Void> deleteDataStore(@PathVariable String id) {
log.debug("REST request to delete DataStore : {}", id);
dataStoreService.delete(id);
return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(ENTITY_NAME, id)).build();
}