Me gustaría llamar a un servicio REST creado utilizando RESTEasy y JAX-RS desde una aplicación cliente GWT. ¿Cuál es el mejor proceso para usar Errai para usar una única base de código para el servidor y el cliente?¿Cómo se integra GWT con un servidor JAX-RS/RESTEasy usando Errai?
Respuesta
Todos amamos DESCANSO. Es proveedor, plataforma y lenguaje neutral; es simple depurar, implementar y acceder; y proporciona un sólido backend a su nube, navegador, dispositivo móvil y aplicaciones de escritorio.
Los desarrolladores de Java pueden usar las bibliotecas que admiten JAX-RS, como RESTEasy, para poner en funcionamiento un servidor REST en cuestión de minutos. Luego, using JAX-RS clients, estos servidores JAX-RS REST se pueden llamar desde aplicaciones cliente Java con solo unas pocas líneas de código.
Pero a pesar de que GWT comparte mucho en común con Java, llamar a los servicios REST de GWT puede ser una experiencia dolorosa. El uso de la clase RequestBuilder implica especificar el método HTTP correcto, codificar su URL, y luego decodificar la respuesta o crear objetos Overlay para representar los datos que envía el servidor REST. Esto puede no ser una gran sobrecarga para llamar a uno o dos métodos REST, pero representa mucho trabajo al integrar GWT con un servicio REST más complicado.
Aquí es donde entra Errai. Errai es un proyecto de JBoss que, entre otras cosas, implements the JAX-RS standard within GWT. En teoría, esto significa que puede compartir su interfaz JAX-RS entre sus proyectos Java y GWT, proporcionando una fuente única que define la funcionalidad de su servidor REST.
Llamar al servidor REST desde Errai solo requiere unos pocos pasos simples. Primero, necesitas la interfaz REST JAX-RS. Esta es una interfaz Java anotada JAX-RS que define los métodos que proporcionará su servidor REST. Esta interfaz se puede compartir entre sus proyectos de Java y GWT.
@Path("customers")
public interface CustomerService {
@GET
@Produces("application/json")
public List<Customer> listAllCustomers();
@POST
@Consumes("application/json")
@Produces("text/plain")
public long createCustomer(Customer customer);
}
La interfaz REST se inyecta en su clase de cliente GWT.
@Inject
private Caller<CustomerService> customerService;
Se define un controlador de respuesta.
RemoteCallback<Long> callback = new RemoteCallback<Long>() {
public void callback(Long id) {
Window.alert("Customer created with ID: " + id);
}
};
Y finalmente se llama al método REST.
customerService.call(callback).listAllCustomers();
Pretty simple huh?
Le puede hacer creer, a partir de este ejemplo, que Errai proporcionará una solución a su infraestructura JAX-RS actual, pero desafortunadamente este ejemplo simple no menciona algunas de las complicaciones que probablemente verá cuando tratando de combinar su base de código GWT y Java REST. A continuación, se detallan algunos de los problemas que hay que tener en cuenta al utilizar Errai y JAX-RS.
Tendrá que aplicar CORS
Normalmente cuando se implementa un cliente GWT JAX-RS, se le depuración de la aplicación GWT en un servidor externo RESTO. Esto no funcionará a menos que implemente CORS, porque de manera predeterminada el navegador que aloja la aplicación GWT no permitirá que su código JavaScript se ponga en contacto con un servidor que no se está ejecutando en el mismo dominio. De hecho, incluso puede ejecutar el servidor REST en su PC de desarrollo local y aún así encontrarse con estos problemas de dominio cruzado, ya que las llamadas entre diferentes puertos también están restringidas.
Si está utilizando RESTEasy, implementar CORS se puede hacer con dos métodos. El primero se hace usando la interfaz MessageBodyInterceptors. Proporciona el método write() y anota su clase con las anotaciones @Provider y @ServerInterceptor. El método write() se usa para agregar el encabezado "Access-Control-Allow-Origin" a las respuestas a cualquier solicitud simple (las solicitudes "simples" no establecen encabezados personalizados, y el cuerpo de la solicitud solo usa texto sin formato).
El segundo método maneja las solicitudes de verificación previa de CORS (para los métodos de solicitud HTTP que pueden causar efectos secundarios en los datos del usuario; en particular, para los métodos HTTP distintos de GET o para el uso POST con ciertos tipos MIME). Estas solicitudes utilizan el método HTTP OPTIONS y esperan recibir los encabezados "Access-Control-Allow-Origin", "Access-Control-Allow-Methods" y "Access-Control-Allow-Headers" en la respuesta. Esto se demuestra en el método handleCORSRequest() a continuación.
Nota
La interfaz REST a continuación permite a cualquier y todas las peticiones CORS, que pueden no ser adecuados desde un punto de vista de la seguridad. Sin embargo, no es prudente suponer que prevenir o restringir CORS en este nivel proporcionará algún grado de seguridad, ya que setting up a proxy para realizar estas solicitudes en nombre del cliente es bastante trivial.
@Path("/1")
@Provider
@ServerInterceptor
public class RESTv1 implements RESTInterfaceV1, MessageBodyWriterInterceptor
{
@Override
public void write(final MessageBodyWriterContext context) throws IOException, WebApplicationException
{ context.getHeaders().add(RESTInterfaceV1.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
context.proceed();
}
@OPTIONS
@Path("/{path:.*}")
public Response handleCORSRequest(@HeaderParam(RESTInterfaceV1.ACCESS_CONTROL_REQUEST_METHOD) final String requestMethod, @HeaderParam(RESTInterfaceV1.ACCESS_CONTROL_REQUEST_HEADERS) final String requestHeaders)
{
final ResponseBuilder retValue = Response.ok();
if (requestHeaders != null)
retValue.header(RESTInterfaceV1.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders);
if (requestMethod != null)
retValue.header(RESTInterfaceV1.ACCESS_CONTROL_ALLOW_METHODS, requestMethod);
retValue.header(RESTInterfaceV1.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
return retValue.build();
}
}
Con estos dos métodos en su lugar, cualquier llamada a su servidor REST proporcionará las respuestas apropiadas para permitir solicitudes de origen cruzado.
Tendrá que aceptar y responder con POJOs simples
La introducción ilustra una interfaz REST simple que respondió con una larga. Tanto la implementación de Java como la de GWT de JAX-RS saben cómo serializar y deserializar primitivas y clases simples como las colecciones de java.util.
En un ejemplo del mundo real, su interfaz REST responderá con objetos más complicados. Aquí es donde las diferentes implementaciones pueden chocar.
Para empezar, JAX-RS y Errai utilizan anotaciones diferentes para personalizar la clasificación de objetos entre objetos JSON y Java. Errai tiene anotaciones como @MapsTo y @Portable, mientras que RESTEasy (o Jackson, the JSON marshaller) usa anotaciones como @JsonIgnore y @JsonSerialize. Estas anotaciones son mutuamente excluyentes: GWT se quejará de las anotaciones de Jackson, y Jackson no puede usar las anotaciones de Errai.
La solución simple es tener un conjunto de POJOs simples con su interfaz de reposo. Por simple me refiero a las clases que tienen constructores no-args, y solo tienen métodos getter y setter que se relacionan directamente con las propiedades que estarán presentes en el objeto JSON a medida que viaja por el cable. Los puntos simples POJO pueden ser organizados por Errai y Jackson con sus configuraciones predeterminadas, eliminando la necesidad de hacer malabares con las anotaciones incompatibles.
Errai y Jackson también obtienen los nombres de las propiedades resultantes en la cadena JSON de diferentes lugares. Jackson usará los nombres de los métodos getter y setter, mientras que Errai usará los nombres de las variables de instancia. Así que asegúrese de que las variables de instancia y los nombres de los métodos getter/setter sean exactamente iguales.Esto está bien:
public class Test
{
private int count;
public int getCount() {return count;}
public void setCount(int count) {this.count = count;}
}
Esto causará problemas:
public class Test
{
private int myCount;
public int getCount() {return myCount;}
public void setCount(int count) {this.myCount = count;}
}
En segundo lugar, es tentador para añadir métodos adicionales para estos objetos de datos REST para implementar algunas funciones de negocio. Sin embargo, si hace esto, no pasará mucho tiempo antes de que intente utilizar una clase que no sea compatible con GWT, y le sorprenderá saber qué GWT doesn’t support: formatear fechas, clonar matrices, convertir una cadena en un byte []. .. La lista continua. Por lo tanto, es mejor seguir los principios básicos de los objetos de datos REST e implementar cualquier lógica comercial completamente fuera del árbol de herencia de objetos de datos REST usando algo como la composición o un diseño basado en componentes.
Nota
Sin la anotación @Portable, tendrá que manually list any classes used Errai when calling the REST interface in the ErraiApp.properties file.
Nota
También querrá permanecer lejos de Mapas. Ver this bug para más detalles.
Nota
No se puede hacer servir tipos parametrizados anidados en la jerarquía de objetos devueltos por el servidor JSON. Ver this bug y this forum post para más detalles.
Nota
Errai tiene problemas con el byte []. Use una lista en su lugar. Ver this forum post para más detalles.
Tendrá que depurar con Firefox
Cuando se trata de enviar grandes cantidades de datos a través de una interfaz REST utilizando GWT, que tendrá que depurar la aplicación con Firefox. En mi propia experiencia, codificar incluso un archivo pequeño en un byte [] y enviarlo a través de la red provocó todo tipo de errores en Chrome. Se lanzarán una variedad de excepciones diferentes cuando el codificador JSON intente manejar datos corruptos; excepciones que no se ven en la versión compilada de la aplicación GWT, o cuando se depura en Firefox.
Desafortunadamente, Google no ha logrado mantener sus plugins de Firefox GWT actualizados con los nuevos ciclos de publicación de Mozilla, pero a menudo puede encontrarse no oficial publicado por Alan Leung en los foros de Grupos de Google GWT. This link tiene una versión del plug-in para Firefox GWT 12, y this link tiene una versión para Firefox 13.
Tendrá que utilizar Errai 2.1 o posterior
Only Errai 2.1 or later will produce JSON that is compatible with Jackson, que es una necesidad si usted está tratando de integrar GWT con RESTEasy.Jackson de clasificación se puede habilitar el uso de
RestClient.setJacksonMarshallingActive(true);
o
<script type="text/javascript">
erraiJaxRsJacksonMarshallingActive = true;
</script>
Tendrá que crear interfaces de JAX-RS separadas para las funciones avanzadas
Si sus declaraciones de interfaz RS-JAX adelantada de reposo objetos, como ATOM (o más al punto, importa clases como org.jboss.resteasy.plugins.providers.atom.Feed), tendrá que dividir su interfaz REST en dos interfaces Java, porque Errai no lo hace Conozca estos objetos, y las clases probablemente no estén en un estado que pueda importarse fácilmente en GWT.
Una interfaz puede contener sus antiguos métodos JSON y XML, mientras que la otra puede contener los métodos ATOM. De esta forma, puede evitar tener que hacer referencia a la interfaz con las clases desconocidas en su aplicación GWT.
Nota
Errai only supports JSON mashalling at this point, aunque en el futuro es posible que pueda definir señaleros personalizados.
Con el fin de implementar CORS (para poder obtener solicitudes entre sitios, por supuesto), como estoy usando RESTEasy, seguí las clases sugeridas por la respuesta aceptada y necesité cambiarlas un poco para que funcionen. Esto es lo que he usado:
//@Path("/1")
@Path("/") // I wanted to use for all of the resources
@Provider
@ServerInterceptor
public class RESTv1 implements RESTInterfaceV1, MessageBodyWriterInterceptor
{
/* Enables the call from any origin. */
/* To allow only a specific domain, say example.com, use "example.com" instead of "*" */
@Override
public void write(final MessageBodyWriterContext context) throws IOException, WebApplicationException
{ context.getHeaders().add(RESTInterfaceV1.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
context.proceed();
}
/* This is a RESTful method like any other.
The browser sends an OPTION request to check if the domain accepts CORS.
It sends via header (Access-Control-Request-Method) the method it wants to use, say 'post',
and will only use it if it gets a header (Access-Control-Allow-Methods) back with the intended
method in its value.
The method below then checks for any Access-Control-Request-Method header sent and simply
replies its value in a Access-Control-Allow-Methods, thus allowing any method to be used.
The same applies to Access-Control-Request-Headers and Access-Control-Allow-Headers.
*/
@OPTIONS
@Path("/{path:.*}")
public Response handleCORSRequest(
@HeaderParam(RESTInterfaceV1.ACCESS_CONTROL_REQUEST_METHOD) final String requestMethod,
@HeaderParam(RESTInterfaceV1.ACCESS_CONTROL_REQUEST_HEADERS) final String requestHeaders)
{
final ResponseBuilder retValue = Response.ok();
if (requestHeaders != null)
retValue.header(RESTInterfaceV1.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders);
if (requestMethod != null)
retValue.header(RESTInterfaceV1.ACCESS_CONTROL_ALLOW_METHODS, requestMethod);
retValue.header(RESTInterfaceV1.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
return retValue.build();
}
}
Tenga cuidado, ya que permitirá a las peticiones de cualquier origen (ACCESS_CONTROL_ALLOW_ORIGIN_HEADER
se establece en "*"
en ambos métodos).
Los valores de esas constantes son los siguientes:
public interface RESTInterfaceV1 {
// names of the headers
public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
public static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
public static final String ACCESS_CONTROL_REQUEST_METHOD = "Access-Control-Request-Method";
}
Thatss él!
--A
- 1. ¿Cómo se integra Delphi con Active Directory?
- 2. ¿Cómo se integra InAppSettingsKit?
- 3. ¿Cómo se integra con Spring ScalaTest
- 4. ¿Cómo se integra hiedra con MSbuild
- 5. ¿Cómo se integra un enfoque TDD con VisualStudio?
- 6. ¿Cómo se integra EventMachine & Rails?
- 7. Mini Profiler se integra con SqlConnection
- 8. Usando Guava con GWT
- 9. Perforce: ¿Cómo se integra en varias ramas?
- 10. Eclipse integra con qt
- 11. Usando guayaba empaquetada con GWT
- 12. ¿Cómo crear un componente compuesto gwt con niños usando uibinder?
- 13. GWT y WebSocket/Datos push del servidor al cliente GWT
- 14. ¿Existe alguien que haya usado JBoss Errai en sus proyectos?
- 15. ¿Qué marco Javascript se integra bien con Grails?
- 16. ¿NHibernate se integra bien con Microsoft Sync Framework?
- 17. ¿Mejor reproductor de video que se integra fácilmente con ASP.NET?
- 18. Cómo configurar Ext GWT con GWT Designer?
- 19. ¿Cómo se lee el archivo local (Cliente) con GWT?
- 20. ¿Cómo se integra Active Directory en una aplicación .NET usando DDD?
- 21. Cómo crear un servidor WebSocket usando SuperWebSocket
- 22. ¿Cómo crear un enlace cliqueable con GWT?
- 23. GWT Clonar un widget usando DOM.clone
- 24. Integra CodeIgniter con phpBB3 User System
- 25. GWT/StockWatcher: no se puede conectar el servidor
- 26. ¿Cómo usar Django con GWT?
- 27. GWT: separada JS + CSS + imágenes del servidor
- 28. ¿Cómo usar GWT al descargar archivos con un servlet?
- 29. GWT I18N en el lado del servidor
- 30. GWT SE aplicación amigable
¡Esa es una respuesta realmente completa! – jonasr