Tabla de Contenidos
10.6 Controlador
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:
- A manejar datos en formato JSON. En el tema 10.2 JSON
- A configurar una aplicación para que funciones Spring. En el tema 10.3 Spring
- A configurar su sistema de inyección de dependencias. En el tema 10.4 Inyección de dependencias
- A programar todo el acceso a la base de datos. En el tema 10.5 Base de datos
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 |
Controlador
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.
- Anotacion @Controller
- Paquete de la clase
- value de la anotación @RequestMapping
Anotacion @Controller
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 {
}
- Línea 5: Añadimos la anotación
@Controller
Paquete de la clase
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.
value de la anotación @RequestMapping
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() {
}
}
- Línea 9: Indicamos que este método debe ser llamado si parte de la URI pedida es
/SeguroMedico
URL
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 ficherocontext.xml, en el atributopathdel tag<Context>./api: El la URI que siempre maneja Spring. La definimos en el ficheroweb.xml, en el tag<url-pattern>/SeguroMedico: Es la parte que finalmente definimos en la anotación@RequestMappingdel 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
Datos de entrada
Pasemos a ver ahora como podemos obtener los valores de entrada que vienen por la conexión HTTP.
Método 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() {
}
}
- Línea 10: Ahora solo se llamará al método Java
readno solo según la URL sino tambien si el método HTTP solo esGET.
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.
Parámetros de la URL
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:
- Indicar en la URL que esa parte es variable, poniendo entre
{ }la parte variable - Añadiendo un parámetro al método de Java y usando una anotación
@PathVariablecon 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) {
}
}
- Línea 11: Se ha puesto entre
{ }la parte deidSeguroMedicopara indicar que esa parte es variable. - Línea 12: Ahora el método Java tiene un parámetro de tipo
intque se llamaidSeguroMedicoy lo relacionamos con la URL con la anotación@PathVariable(“idSeguroMedico”). Siendo el valor que hay en@PathVariableexactamente 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.
Accediendo al JSON de entrada
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) {
}
}
- Línea 17 y 18: Creamos el nuevo método que será llamado con la URI “/SeguroMedico”, con el método HTTP
POST - Línea 18: El parámetro
jsonEntradacontendrá 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.
El formato de los datos
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 cabeceraContent-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 medianteContent-Typeun formato definido enconsumes.produces: El método solo será llamado si el cliente indica medianteAcceptun formato definido enproduces.
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) {
}
}
- Línea 12: El método
readsolo será llamado si el cliente acepta 2) que el servidor le retorne los datos en formato“application/json”. No hemos indicado el atributoconsumesya que al métodoreadno se le envian datos en el cuerpo de la petición. - Línea 17: El método
insertsolo 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"/>
HttpServletRequest
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) {
}
}
- Líneas 14 y 17: Declaramos un parámetro de la clase
HttpServletRequestpara poder tener acceso a toda la información de la petición.
Datos de salida
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) {
}
}
}
- Línea 19: Declaramos la propiedad
jsonTransformery Spring inyectará la implementación. - Línea 22: Declaramos la propiedad
jsonTransformery Spring inyectará la implementación. - Línea 27: En función del
idSeguroMedicoobtenemos el objeto de la claseSeguroMedicode la base de datos mediante el DAO. - Línea 28: Transformamos el objeto en un String en formato JSON
- Línea 30: Este código se ejecutará si por un motivo que no es un error de programación no se han podido obtener los datos y debemos notificar un mensaje al usuario, es cuando se produce una excepción de tipo
BussinessException. - Línea 32: Este código se ejecutará si ha habido un error en el programa, es cuando se produce una excepción de tipo
Exception.
- Línea 40: Transformamos el JSON de entrada en un objeto de la clase
SeguroMedico. - Línea 41: Insertamos el nuevo objeto de la clase
SeguroMedicoen la base de datos mediante el DAO. - Línea 42: El objeto recién insertado lo transformamos otra vez en un String en formato JSON para retornarlo.
- Línea 44: Este código se ejecutará si por un motivo que no es un error de programación no se han podido obtener los datos y debemos notificar un mensaje al usuario, es cuando se produce una excepción de tipo
BussinessException. - Línea 46: Este código se ejecutará si ha habido un error en el programa, es cuando se produce una excepción de tipo
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.
Estado HTTP
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);
}
}
}
- Línea 26: Declaramos un parámetro de la clase
HttpServletResponsepara poder tener acceso a todos los métodos relativos a la respuesta. - Línea 31: Como hemos obtenido de forma exitosa los datos retornamos un
200 OK. - Línea 34: Si se produce una
BussinessExceptiones que algun de los datos de entrada era erróneo y por lo tanto se retorna un400 Bad Request. - Línea 37: Si se produce cualquier otro tipo de excepción es que ha sido un error del programa y retornamos un
500 Internal Server Error
- Línea 44: Declaramos un parámetro de la clase
HttpServletResponsepara poder tener acceso a todos los métodos relativos a la respuesta. - Línea 50: Como hemos obtenido de forma exitosa los datos retornamos un
200 OK. - Línea 53: Si se produce una
BussinessExceptiones que algun de los datos de entrada era erróneo y por lo tanto se retorna un400 Bad Request. - Línea 56: Si se produce cualquier otro tipo de excepción es que ha sido un error del programa y retornamos un
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.
Los datos de respuesta
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);
}
}
}
- Linea 32 y 52: Llamamos al método '' httpServletResponse.getWriter().println(String)'' para retornar el String en formato JSON con los datos.
Content-Type
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);
}
}
}
- Líneas 32 y 53 : usando el método
httpServletResponse.setContentType(String)indicamos que el formato de los datos que se retornan es JSON.
BussinessException
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);
}
}
- Linea 2: Obtenemos la lista de los mensajes que es lo que realmente queremos retornar.
- Línea 3: Tranformamos la lista de mensaje en un String en formato JSON
- Línea 5: Establecemos que el estado es
400 Bad Request. - Línea 6: Establecemos que es formato de los datos es JSON
- Línea 8: Devolvemos los datos. Por desgracia el método
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.
Exception
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);
}
- Línea 1: Establecemos que el estado es
500 Internal Server Error. - Línea 2: Establecemos que el formato de los datos es texto plano
- Línea 4: Aqui es donde se llama al método
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.
El resto de métodos del controlador
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.
find
@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);
}
}
update
@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);
}
}
}
delete
@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);
}
}
- Línea 6: Destacar que no se retorna nada y por lo tanto el estado HTTP es
204 No Content
REST Client
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:
- Metodo
- URL
- Cuerpo
- Cabeceras
Y ver todo lo que retorna el servidor:
- Cabeceras
- Estado
- Cuerpo de la respuesta.
Por ello es una manera ideal de depurar nuestro API REST y lo recomiendo mientras estamos desarrollando.
Comentarios finales
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.
Ejemplo
El ejemplo de esta unidad es exactamente lo que acabamos de contar pero en un nuevo proyecto llamado “seguros”.
