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:

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 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

Recuerda que parte de la URL que se usa al llamar a un controlador incluye la parte común gestionada por Spring que en nuestro caso es /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 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.

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 @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) {
        
    }

}

  • Línea 11: Se ha puesto entre { } la parte de idSeguroMedico para indicar que esa parte es variable.
  • Línea 12: Ahora el método Java tiene un parámetro de tipo 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}

La propia variable Java se puede llamar como queramos pero lo que hay dentro de @PathVariable debe ser exactamente el mismo texto que hay entre { } de la URL.

En AngularJS también había partes variable en las rutas y se usaban los dos puntos ”:” para indicar que era variable.

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 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.

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 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) {
        
    }    

}

  • Línea 12: El método 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.
  • Línea 17: El método 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.

Una cosa interesante es que si el cliente no pone el tipo en 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.

Esto último permite gracias al protocolo HTTP tener por ejemplo 2 métodos que sea iguales excepto en el formato de los datos que retornan.

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 HttpServletRequest para 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 jsonTransformer y Spring inyectará la implementación.
  • Línea 22: Declaramos la propiedad jsonTransformer y Spring inyectará la implementación.
  • Línea 27: En función del idSeguroMedico obtenemos el objeto de la clase SeguroMedico de 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 SeguroMedico en 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 HttpServletResponse para 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 BussinessException es que algun de los datos de entrada era erróneo y por lo tanto se retorna un 400 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 HttpServletResponse para 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 BussinessException es que algun de los datos de entrada era erróneo y por lo tanto se retorna un 400 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

Personalmente he tenido problemas en AngularJS retornando un 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.

No vemos ahora como retornar cuando se produce una excepción ya que lo veremos en un apartado específico para cada uno.

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.

Hay que fijarse que hemos puesto la línea justo antes de enviar los datos.

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);
            
        }

    } 

Por simplificar el DAO y el controlador no se ha hecho que se puedan filtrar los datos, por lo que siempre se muestran todos los seguros médicos.

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”.

Referencias

1) o XML ,etc,
2) , 3) Cabecera Accept
4) Cabecera Content-Type
unidades/10_servidor/06_controlador.txt · Última modificación: 2014/09/15 09:15 por admin
Ir hasta arriba
CC Attribution-Share Alike 3.0 Unported
chimeric.de = chi`s home Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0
Esta web utiliza cookies, puedes ver nuestra la política de cookies, aquí Si continuas navegando estás aceptándola
Política de cookies +