Llevamos ya 5 temas sobre REST y aun no hemos visto como hacerlo funcionar aunque ya se ha explicado en que consiste. Por fin en este tema veremos como implementar un controlador que siga la arquitectura de REST.
Para llegar a este punto hemos tenido primero que aprender:
Para ir explicando todo lo relacionado con el controlador vamos a ir haciendo el ejemplo poco a poco con la clase SeguroMedicoController
.
La clase empezará estando vacía
package es.cursohibernate.seguros.presentacion.controller; public class SeguroMedicoController { }
y vamos a ir añadiendo código poco a poco.
Con todo ésto ya estamos preparado para crear un controlador REST con las funcionalidades que vimos en el tema 10.1 REST. En ese tema mostramos una tabla con las URLs que había que implementar. Como cada una de aquellas URL va a ser un método Java de nuestro controlador podemos modificar aquella tabla añadiendo el nombre de los métodos Java que vamos a crear y también vamos a poner las URL de SeguroMedico
Método Java | Descripción | URL | Método HTTP | JSON Enviado | JSON Retornado |
---|---|---|---|---|---|
read | Leer un seguro médico | /api/SeguroMedico/{idSeguroMedico} | GET | Ninguno | Seguro medico leido |
find | Buscar seguros médicos | /api/SeguroMedico/?columnaBusqueda1=valor1&columnaBusqueda2=valor2&…. | GET | Ninguno | Array de Seguros medicos |
insert | Añadir un seguro médico | /api/SeguroMedico | POST | Seguro medico a insertar | Seguro medico insertado |
update | Actualizar un seguro médico | /api/SeguroMedico/{idSeguroMedico} | PUT | Seguro medico a actualizar | Seguro medico actualizado |
delete | Borrar un seguro médico | /api/SeguroMedico/{idSeguroMedico} | DELETE | Ninguno | Ninguno |
Pero empecemos desde el principio y veamos en que consiste un controlador.
Un controlador es una clase Java a la que se le llama cierto método de ella cuando se pide al navegador cierta URL. Para que esto ocurra es necesario que la clase tenga 3 características.
Es necesario que le digamos a Spring que clases Java son controladores. Para ello debemos incluir la anotación org.springframework.stereotype.Controller
a nivel de clase.
package es.cursohibernate.seguros.presentacion.controller; import org.springframework.stereotype.Controller; @Controller public class SeguroMedicoController { }
@Controller
El paquete donde se encuentra la clase debe ser un paquete o subpaquete del definido en el fichero dispatcher-servlet.xml
, concretamente en el atributo base-package
definido en el tag <context:component-scan>
.
La línea
<context:component-scan base-package="es.cursohibernate.seguros.presentacion.controller" />sirve para decirle a Spring el paquete a partir del cual buscar clases que estén anotadas con cierta anotaciones, entre ellas la anotación
@Controller
y así sepa que controladores hay. En caso de que nuestra clase no estuviera dentro de ese paquete o alguno de sus subpaquetes nunca se encontraría la clase.
La última de las características que debe tener la clase es tener por lo menos un método anotado con la anotación @RequestMapping
y que dicha anotación tenga un atributo llamado value
con parte de la URI que especifica cuando será llamado ese método.
package es.cursohibernate.seguros.presentacion.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class SeguroMedicoController { @RequestMapping(value="/SeguroMedico") public void read() { } }
/SeguroMedico
El atributo value
solo es una parte de la URL, ¿cual es la URL que lleva hasta ese método?
La URL que en nuestro ejemplo llamará a este método es: http://localhost:8084/seguros/api/SeguroMedico
. Vamos a partir en trozos la URL para ver de donde viene cada parte.
http://localhost:8084
: Es el host y el puerto donde está instalado el Tomcat./seguros
: Es el contexto de la aplicación. Su nombre se define en el fichero context.xml
, en el atributo path
del tag <Context>
./api
: El la URI que siempre maneja Spring. La definimos en el fichero web.xml
, en el tag <url-pattern>
/SeguroMedico
: Es la parte que finalmente definimos en la anotación @RequestMapping
del método a llamar
Por lo tanto la URL , en nuestro ejemplo, de cualquier método de un controlador siempre empezará por http://localhost:8084/seguros/api
/api
Pasemos a ver ahora como podemos obtener los valores de entrada que vienen por la conexión HTTP.
Para saber el método HTTP usado podríamos pensar que hay un método Java que nos dice que método HTTP utilizado, aunque dicho método existe no lo vamos a usar. Spring permite de una forma declarativa hacer que llamen a nuestro método Java solo cuando el es cierto método HTTP.
La forma es añadir a la anotación @RequestMapping
otro atributo llamado method
con el método HTTP que debe tener la petición para que llamen a ese método.
Veamoslo mejor con un ejemplo:
package es.cursohibernate.seguros.presentacion.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class SeguroMedicoController { @RequestMapping(value="/SeguroMedico",method=RequestMethod.GET) public void read() { } }
read
no solo según la URL sino tambien si el método HTTP solo es GET
.
Como podemos ver en la anotación @RequestMapping
hay un parámetro llamado method
en el que indicamos que cuando se llame a esa URL con ese método HTTP llamemos a dicho el método Java.
Algunas URL incluyen una parte variable con el idSeguroMedico
, ¿como podemos desde Java acceder al valor de dicha parte variable de la URL? Para poder acceder debemos hacer 2 cosas:
{ }
la parte variable@PathVariable
con el nombre de la parte variable para que lo relacione con la URL.Veamos el ejemplo:
package es.cursohibernate.seguros.presentacion.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class SeguroMedicoController { @RequestMapping(value="/SeguroMedico/{idSeguroMedico}",method=RequestMethod.GET) public void read(@PathVariable("idSeguroMedico") int idSeguroMedico) { } }
{ }
la parte de idSeguroMedico
para indicar que esa parte es variable.int
que se llama idSeguroMedico
y lo relacionamos con la URL con la anotación @PathVariable(“idSeguroMedico”)
. Siendo el valor que hay en @PathVariable
exactamente el mismo que hay entre { }
de la URL.
Ahora tenemos en Java el parámetro idSeguroMedico
que obtendrá su valor a partir de la URL, concretamente de lo que hay entre llaves {idSeguroMedico}
@PathVariable
debe ser exactamente el mismo texto que hay entre { }
de la URL.
Por último nos queda acceder al texto que nos envían por HTTP con los métodos POST
y PUT
, es cual será el JSON de entrada. Acceder a él es tan sencillo como añadir otro parámetro al método Java de tipo String
con la anotación @RequestBody
.
En nuestro ejemplo vamos ahora a añadir un nuevo método Java llamado insertar
que se llame por POST
para poder acceder de esa forma al JSON de entrada.
package es.cursohibernate.seguros.presentacion.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class SeguroMedicoController { @RequestMapping(value="/SeguroMedico/{idSeguroMedico}",method=RequestMethod.GET) public void read(@PathVariable("idSeguroMedico") int idSeguroMedico) { } @RequestMapping(value="/SeguroMedico",method=RequestMethod.POST) public void insert(@RequestBody String jsonEntrada) { } }
POST
jsonEntrada
contendrá el String con el cuerpo de la petición ya que tiene la anotación @RequestBody
.
Ahora tenemos el parámetro jsonEntrada
un String con el JSON 1) que nos han enviado en el cuerpo de la petición HTTP.
Por ahora hemos limitado la llamada a los métodos Java en función de la URL y del método HTTP, pero también deberíamos limitar la llamada a dichos métodos en función del formato en el que nos envían los datos y en función del formato de los datos en lo que quieren que les enviemos.
ya comentamos en el_formato_de_los_datos Que al hacernos una petición HTTP se suelen enviar 2 cabeceras:
Content-Type
: El cliente indica el formato de los datos que el propio cliente está enviado en el cuerpo de la petición ,es decir el formato de los datos que el servidor obtendrá mediante el @RequestBody
.Accept
: El cliente indica el formato en el que desea que el servidor le retorne los datos que está solicitando.Sin embargo posteriormente el servidor puede hacer lo que quiera y enviarlos en el formato que quiera y para ello en la respuesta definirá la cabecera Content-Type
, aunque por ejemplo no sería adecuado que al servidor se le solicitara JSON y él retornara XML.Así que si nuestros servicios REST solo van a aceptar JSON y retornar JSON, ¿no deberíamos restringir que solo llamaran a los métodos Java si el cliente nos envia o nos solicita datos en formato JSON?
Para eso la anotación @RequestMapping
dispone de otros 2 atributos:
consumes
: El método solo será llamado si el cliente indica mediante Content-Type
un formato definido en consumes
. produces
: El método solo será llamado si el cliente indica mediante Accept
un formato definido en produces
. Ahora debemos modificar nuestros 2 métodos para indicar que los formatos que aceptan / devuelven son en formato JSON.
package es.cursohibernate.seguros.presentacion.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class SeguroMedicoController { @RequestMapping(value="/SeguroMedico/{idSeguroMedico}",method=RequestMethod.GET,produces = "application/json") public void read(@PathVariable("idSeguroMedico") int idSeguroMedico) { } @RequestMapping(value="/SeguroMedico",method=RequestMethod.POST,consumes = "application/json",produces = "application/json") public void insert(@RequestBody String jsonEntrada) { } }
read
solo será llamado si el cliente acepta 2) que el servidor le retorne los datos en formato “application/json”
. No hemos indicado el atributo consumes
ya que al método read
no se le envian datos en el cuerpo de la petición.insert
solo será llamado si el cliente acepta 3) que el servidor le retorna los datos en formato “application/json”
y también si el tipo de datos 4) que envía al servidor está en formato “application/json”
. en este caso si que se ponen ambos atributos que ya el método si que acepta y retorna datos en formato “application/json”
.
Ahora si enviamos una petición con un Content-Type
o un Accept
no válido , Spring no retornará un error 406 Not Acceptable
.
Content-Type
o en Accept
pero solo hay un método Java para esa URL y ese método HTTP si que llamará al método Java.
en el siguiente ejemplo según la cabecera Accept
se llamaría a uno u otro.
@RequestMapping(value="/SeguroMedico/{idSeguroMedico}",method=RequestMethod.GET,produces = "application/json") public void readJSON(@PathVariable("idSeguroMedico") int idSeguroMedico) { } @RequestMapping(value="/SeguroMedico/{idSeguroMedico}",method=RequestMethod.GET,produces = "application/xml") public void readXML(@PathVariable("idSeguroMedico") int idSeguroMedico) { }
Desgraciadamente si probamos el ejemplo con la petición /SeguroMedico/5
no dará el siguiente error:
Ambiguous handler methods mapped for HTTP path '/SeguroMedico/5'
Para soluciones el error deberemos añadir las siguientes 2 líneas en el fichero dispatcher-servlet.xml
:
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
Por último Spring también nos permite acceder a todos los datos que teníamos en los Servlets mediante la clase HttpServletRequest
, para ello solo tenemos que declarar un parámetro en cada método de la clase HttpServletRequest
.
package es.cursohibernate.seguros.presentacion.controller; import javax.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class SeguroMedicoController { @RequestMapping(value = "/SeguroMedico/{idSeguroMedico}", method = RequestMethod.GET, produces = "application/json") public void read(HttpServletRequest httpServletRequest, @PathVariable("idSeguroMedico") int idSeguroMedico) { } @RequestMapping(value = "/SeguroMedico", method = RequestMethod.POST, consumes = "application/json", produces = "application/json") public void insert(HttpServletRequest httpServletRequest, @RequestBody String jsonEntrada) { } }
HttpServletRequest
para poder tener acceso a toda la información de la petición.Antes de empezar a ver como retornar la información al cliente , vamos a modificar los 2 métodos para generar la nueva información que queremos retornar en función de los datos de entrada.
package es.cursohibernate.seguros.presentacion.controller; import es.cursohibernate.seguros.dominio.SeguroMedico; import es.cursohibernate.seguros.persistencia.BussinessException; import es.cursohibernate.seguros.persistencia.SeguroMedicoDAO; import es.cursohibernate.seguros.presentacion.json.JsonTransformer; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class SeguroMedicoController { @Autowired JsonTransformer jsonTransformer; @Autowired SeguroMedicoDAO seguroMedicoDAO; @RequestMapping(value = "/SeguroMedico/{idSeguroMedico}", method = RequestMethod.GET, produces = "application/json") public void read(HttpServletRequest httpServletRequest, @PathVariable("idSeguroMedico") int idSeguroMedico) { try { SeguroMedico seguroMedico=seguroMedicoDAO.get(idSeguroMedico); String jsonSalida=jsonTransformer.toJson(seguroMedico); } catch (BussinessException ex) { } catch (Exception ex) { } } @RequestMapping(value = "/SeguroMedico", method = RequestMethod.POST, consumes = "application/json", produces = "application/json") public void insert(HttpServletRequest httpServletRequest, @RequestBody String jsonEntrada) { try { SeguroMedico seguroMedico=(SeguroMedico)jsonTransformer.fromJson(jsonEntrada, SeguroMedico.class); seguroMedicoDAO.insert(seguroMedico); String jsonSalida=jsonTransformer.toJson(seguroMedico); } catch (BussinessException ex) { } catch (Exception ex) { } } }
jsonTransformer
y Spring inyectará la implementación.jsonTransformer
y Spring inyectará la implementación.idSeguroMedico
obtenemos el objeto de la clase SeguroMedico
de la base de datos mediante el DAO.BussinessException
.Exception
.SeguroMedico
.SeguroMedico
en la base de datos mediante el DAO.BussinessException
.Exception
.Gracias a todo el trabajo que hemos hecho previamente de preparar todas las clases que íbamos a necesitar, el trabajo del controlador es realmente muy sencillo.
Ahora pasemos a ver como retornar los datos.
Cada petición HTTP siempre retorna un valor indicado el éxito o fracaso de dicha petición. Si todo funciona correctamente se suele retornar un 200 OK, pero veamos la lista de estados que vamos a usar:
Número | Nombre | Significado |
---|---|---|
200 | OK | Todo ha funcionado correctamente y retornamos los datos |
204 | No Content | Todo ha funcionado correctamente pero NO retornamos datos ya que no es necesario. Se usa en el DELETE en el que no se retorna nada |
400 | Bad Request | Hay errores en los datos que ha enviado el usuario. Se retorna un objeto List<BusinessMessage> |
500 | Internal Server Error | Error del servidor. Se produce cuando hay un error en el código. Se retorna la excepción que se ha producido |
¿Como retornamos esos valores desde Java? Simplemente con el método httpServletResponse.setStatus(int sc);
httpServletResponse.setStatus(HttpServletResponse.SC_OK); httpServletResponse.setStatus(HttpServletResponse.SC_NO_CONTENT); httpServletResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
Esto hace que debemos incluir como parámetro de los métodos otro objeto de la clase HttpServletResponse
.
package es.cursohibernate.seguros.presentacion.controller; import es.cursohibernate.seguros.dominio.SeguroMedico; import es.cursohibernate.seguros.persistencia.BussinessException; import es.cursohibernate.seguros.persistencia.SeguroMedicoDAO; import es.cursohibernate.seguros.presentacion.json.JsonTransformer; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class SeguroMedicoController { @Autowired JsonTransformer jsonTransformer; @Autowired SeguroMedicoDAO seguroMedicoDAO; @RequestMapping(value = "/SeguroMedico/{idSeguroMedico}", method = RequestMethod.GET, produces = "application/json") public void read(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, @PathVariable("idSeguroMedico") int idSeguroMedico) { try { SeguroMedico seguroMedico = seguroMedicoDAO.get(idSeguroMedico); String jsonSalida = jsonTransformer.toJson(seguroMedico); httpServletResponse.setStatus(HttpServletResponse.SC_OK); } catch (BussinessException ex) { httpServletResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); } catch (Exception ex) { httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } @RequestMapping(value = "/SeguroMedico", method = RequestMethod.POST, consumes = "application/json", produces = "application/json") public void insert(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, @RequestBody String jsonEntrada) { try { SeguroMedico seguroMedico = (SeguroMedico) jsonTransformer.fromJson(jsonEntrada, SeguroMedico.class); seguroMedicoDAO.insert(seguroMedico); String jsonSalida = jsonTransformer.toJson(seguroMedico); httpServletResponse.setStatus(HttpServletResponse.SC_OK); } catch (BussinessException ex) { httpServletResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); } catch (Exception ex) { httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } }
HttpServletResponse
para poder tener acceso a todos los métodos relativos a la respuesta.200 OK
. BussinessException
es que algun de los datos de entrada era erróneo y por lo tanto se retorna un 400 Bad Request
.500 Internal Server Error
HttpServletResponse
para poder tener acceso a todos los métodos relativos a la respuesta.200 OK
. BussinessException
es que algun de los datos de entrada era erróneo y por lo tanto se retorna un 400 Bad Request
.500 Internal Server Error
200 OK
cuando retorno un null
. Ese caso se da en:
SeguroMedico seguroMedico = seguroMedicoDAO.get(idSeguroMedico);Si
seguroMedico
es null
en vez de retornar un 200 y transformar el null
a JSON es mas correcto en ese caso retornar un 204 No Content
.
Como ya habíamos visto en el tema anterior retornar los datos es tan sencillo como llamar el método httpServletResponse.getWriter().println(String);
Ahora el código quedará:
package es.cursohibernate.seguros.presentacion.controller; import es.cursohibernate.seguros.dominio.SeguroMedico; import es.cursohibernate.seguros.persistencia.BussinessException; import es.cursohibernate.seguros.persistencia.SeguroMedicoDAO; import es.cursohibernate.seguros.presentacion.json.JsonTransformer; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class SeguroMedicoController { @Autowired JsonTransformer jsonTransformer; @Autowired SeguroMedicoDAO seguroMedicoDAO; @RequestMapping(value = "/SeguroMedico/{idSeguroMedico}", method = RequestMethod.GET, produces = "application/json") public void read(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, @PathVariable("idSeguroMedico") int idSeguroMedico) { try { SeguroMedico seguroMedico = seguroMedicoDAO.get(idSeguroMedico); String jsonSalida = jsonTransformer.toJson(seguroMedico); httpServletResponse.setStatus(HttpServletResponse.SC_OK); httpServletResponse.getWriter().println(jsonSalida); } catch (BussinessException ex) { httpServletResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); } catch (Exception ex) { httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } @RequestMapping(value = "/SeguroMedico", method = RequestMethod.POST, consumes = "application/json", produces = "application/json") public void insert(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, @RequestBody String jsonEntrada) { try { SeguroMedico seguroMedico = (SeguroMedico) jsonTransformer.fromJson(jsonEntrada, SeguroMedico.class); seguroMedicoDAO.insert(seguroMedico); String jsonSalida = jsonTransformer.toJson(seguroMedico); httpServletResponse.setStatus(HttpServletResponse.SC_OK); httpServletResponse.getWriter().println(jsonSalida); } catch (BussinessException ex) { httpServletResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); } catch (Exception ex) { httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } }
httpServletResponse.getWriter().println(String)
para retornar el String en formato JSON con los datos.
Por último nos falta ver como retornar el Content-Type
de los datos que se devuelven. Para ello simplemente debemos llamar al método httpServletResponse.setContentType(String)
con el valor de la cabecera Content-Type
.
package es.cursohibernate.seguros.presentacion.controller; import es.cursohibernate.seguros.dominio.SeguroMedico; import es.cursohibernate.seguros.persistencia.BussinessException; import es.cursohibernate.seguros.persistencia.SeguroMedicoDAO; import es.cursohibernate.seguros.presentacion.json.JsonTransformer; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Controller public class SeguroMedicoController { @Autowired JsonTransformer jsonTransformer; @Autowired SeguroMedicoDAO seguroMedicoDAO; @RequestMapping(value = "/SeguroMedico/{idSeguroMedico}", method = RequestMethod.GET, produces = "application/json") public void read(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, @PathVariable("idSeguroMedico") int idSeguroMedico) { try { SeguroMedico seguroMedico = seguroMedicoDAO.get(idSeguroMedico); String jsonSalida = jsonTransformer.toJson(seguroMedico); httpServletResponse.setStatus(HttpServletResponse.SC_OK); httpServletResponse.setContentType("application/json; charset=UTF-8"); httpServletResponse.getWriter().println(jsonSalida); } catch (BussinessException ex) { httpServletResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); } catch (Exception ex) { httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } @RequestMapping(value = "/SeguroMedico", method = RequestMethod.POST, consumes = "application/json", produces = "application/json") public void insert(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, @RequestBody String jsonEntrada) { try { SeguroMedico seguroMedico = (SeguroMedico) jsonTransformer.fromJson(jsonEntrada, SeguroMedico.class); seguroMedicoDAO.insert(seguroMedico); String jsonSalida = jsonTransformer.toJson(seguroMedico); httpServletResponse.setStatus(HttpServletResponse.SC_OK); httpServletResponse.setContentType("application/json; charset=UTF-8"); httpServletResponse.getWriter().println(jsonSalida); } catch (BussinessException ex) { httpServletResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); } catch (Exception ex) { httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } }
httpServletResponse.setContentType(String)
indicamos que el formato de los datos que se retornan es JSON.
Veamos ahora como se retorna el resultado cuando se produce una BussinessException
. Como ya se explicó en el tema anterior , esta excepción contiene el método getBussinessMessages()
que retorna una lista de objetos BussinessMessage
. Es justamente esta lista lo que queremos retornar al usuario.
Como la página web que estas ahora mismo leyendo ya se está haciendo un poco larga , vamos a poner el código solo del tratamiento de la excepción BussinessException
.
} catch (BussinessException ex) { List<BussinessMessage> bussinessMessage=ex.getBussinessMessages(); String jsonSalida = jsonTransformer.toJson(bussinessMessage); httpServletResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); httpServletResponse.setContentType("application/json; charset=UTF-8"); try { httpServletResponse.getWriter().println(jsonSalida); } catch (IOException ex1) { Logger.getLogger(SeguroMedicoController.class.getName()).log(Level.SEVERE, null, ex1); } }
400 Bad Request
.httpServletResponse.getWriter().println(jsonSalida)
lanza una excepción así que la tratamos en la línea 10 generando una línea de log ya que no podemos retornar la información al usuario ya que ha fallado justamente eso.
Veamos ahora como se retorna el resultado cuando se produce una Exception
. En este caso no vamos a retornar un JSON sino simplemente un texto plano con la traza de la excepción.
La forma de retornar la excepción por la conexión HTTP es la siguiente:
ex.printStackTrace(httpServletResponse.getWriter());
Como la página web que estas ahora mismo leyendo ya se está haciendo un poco larga , vamos a poner el código solo del tratamiento de la excepción Exception
.
httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); httpServletResponse.setContentType("text/plain; charset=UTF-8"); try { ex.printStackTrace(httpServletResponse.getWriter()); } catch (IOException ex1) { Logger.getLogger(SeguroMedicoController.class.getName()).log(Level.SEVERE, null, ex1); }
500 Internal Server Error
.printStackTrace()
que permite retornar los datos de la excepción. Por desgracia como el método puede lanzar una excepción la tratamos en la línea 6 generando una línea de log ya que no podemos retornar la información al usuario ya que ha fallado justamente eso.Por fin hemos acabado de ver todo lo necesario para crearnos nuestros propios controladores, a continuación vamos a ver como son el resto de los métodos del controlador.
Como ya hemos explicado paso a paso un par de métodos, en lo que queda solo vamos a resaltar las líneas mas importantes.
@RequestMapping(value = "/SeguroMedico", method = RequestMethod.GET, produces = "application/json") public void find(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { try { List<SeguroMedico> segurosMedicos = seguroMedicoDAO.findAll(); String jsonSalida = jsonTransformer.toJson(segurosMedicos); httpServletResponse.setStatus(HttpServletResponse.SC_OK); httpServletResponse.setContentType("application/json; charset=UTF-8"); httpServletResponse.getWriter().println(jsonSalida); } catch (BussinessException ex) { List<BussinessMessage> bussinessMessage=ex.getBussinessMessages(); String jsonSalida = jsonTransformer.toJson(bussinessMessage); httpServletResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); httpServletResponse.setContentType("application/json; charset=UTF-8"); try { httpServletResponse.getWriter().println(jsonSalida); } catch (IOException ex1) { Logger.getLogger(SeguroMedicoController.class.getName()).log(Level.SEVERE, null, ex1); } } catch (Exception ex) { httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } }
@RequestMapping(value = "/SeguroMedico/{idSeguroMedico}", method = RequestMethod.PUT, consumes = "application/json", produces = "application/json") public void update(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, @RequestBody String jsonEntrada, @PathVariable("idSeguroMedico") int idSeguroMedico) { try { SeguroMedico seguroMedico = (SeguroMedico) jsonTransformer.fromJson(jsonEntrada, SeguroMedico.class); seguroMedicoDAO.update(idSeguroMedico,seguroMedico); String jsonSalida = jsonTransformer.toJson(seguroMedico); httpServletResponse.setStatus(HttpServletResponse.SC_OK); httpServletResponse.setContentType("application/json; charset=UTF-8"); httpServletResponse.getWriter().println(jsonSalida); } catch (BussinessException ex) { List<BussinessMessage> bussinessMessage=ex.getBussinessMessages(); String jsonSalida = jsonTransformer.toJson(bussinessMessage); httpServletResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); httpServletResponse.setContentType("application/json; charset=UTF-8"); try { httpServletResponse.getWriter().println(jsonSalida); } catch (IOException ex1) { Logger.getLogger(SeguroMedicoController.class.getName()).log(Level.SEVERE, null, ex1); } } catch (Exception ex) { httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); httpServletResponse.setContentType("text/plain; charset=UTF-8"); try { ex.printStackTrace(httpServletResponse.getWriter()); } catch (IOException ex1) { Logger.getLogger(SeguroMedicoController.class.getName()).log(Level.SEVERE, null, ex1); } } }
@RequestMapping(value = "/SeguroMedico/{idSeguroMedico}", method = RequestMethod.DELETE, produces = "application/json") public void delete(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, @PathVariable("idSeguroMedico") int idSeguroMedico) { try { seguroMedicoDAO.delete(idSeguroMedico); httpServletResponse.setStatus(HttpServletResponse.SC_NO_CONTENT); } catch (BussinessException ex) { List<BussinessMessage> bussinessMessage=ex.getBussinessMessages(); String jsonSalida = jsonTransformer.toJson(bussinessMessage); httpServletResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); httpServletResponse.setContentType("application/json; charset=UTF-8"); try { httpServletResponse.getWriter().println(jsonSalida); } catch (IOException ex1) { Logger.getLogger(SeguroMedicoController.class.getName()).log(Level.SEVERE, null, ex1); } } catch (Exception ex) { httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } }
204 No Content
Para probar nuestra API REST no es necesario que creemos una aplicación en JavaScript con AngularJS , antes de hacer todo eso podemos probarla desde el propio navegador. Firefox dispone de un gran plugin llamado REST Client
Este plugin nos permite cambiar todos los parámetros de una petición HTTP como:
Y ver todo lo que retorna el servidor:
Por ello es una manera ideal de depurar nuestro API REST y lo recomiendo mientras estamos desarrollando.
Aunque ya se ha comentado varias veces, la forma de hacer este controlador no es la mas adecuada si usas Spring. El motivo de ello es que Spring dispone de muchas utilidades que nos pueden ayudar a reducir tanto código repetido. Por ejemplo el tratamiento de errores debería estar solo una única vez y no repetido tantas veces. Un tutorial al respecto está en Exception Handling in Spring MVC
Otra posible forma de mejorar el código sin depender tanto de Spring sería usar el patrón Template al estilo de JdbcTemplate
Aun así el motivo de haberlo hecho de esta forma es para que aprendas todo lo que es necesario hacer y ahora ya estés en disposición de ver los problemas y busques información sobre como mejorarlo.
El ejemplo de esta unidad es exactamente lo que acabamos de contar pero en un nuevo proyecto llamado “seguros”.