如何将GWT与使用Errai的JAX-RS / RESTEasy服务器集成? [英] How do you integrate GWT with a JAX-RS/RESTEasy server using Errai?

查看:124
本文介绍了如何将GWT与使用Errai的JAX-RS / RESTEasy服务器集成?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想从GWT客户端应用程序调用使用RESTEasy和JAX-RS创建的REST服务。使用Errai为服务器和客户端使用单一代码库的最佳过程是什么?

解决方案

我们都喜欢REST。它的供应商,平台和语言中立;调试,实现和访问很简单;它为您的云端,浏览器,手机和桌面应用提供了坚实的后端。

Java开发人员可以使用支持 JAX-RS ,比如 RESTEasy ,可以在几分钟内启动并运行REST服务器。然后,使用JAX-RS客户端 ,这些JAX-RS REST只需几行代码即可从Java客户端应用程序中调用服务器。



尽管GWT与Java有很多共同之处,但从GWT调用REST服务可以是一个痛苦的经历。使用 RequestBuilder 类涉及指定正确的HTTP方法,对URL进行编码,然后解码响应或创建覆盖对象以表示由REST服务器发回的数据。这对调用一个或两个REST方法来说可能不是一个很大的开销,但是在将GWT与更复杂的REST服务集成时,它确实代表了很多工作。



这是其中 Errai 出现.Errai是一个JBoss项目,其中包括在GWT中实现JAX-RS标准。从理论上讲,这意味着您可以在Java和GWT项目之间共享您的JAX-RS接口,并提供一个定义REST服务器功能的源。

来自Errai的服务器只涉及几个简单的步骤。首先,您需要REST JAX-RS接口。这是一个JAX-RS注释的Java接口,它定义了REST服务器将提供的方法。此接口可以在您的Java和GWT项目之间共享。

  @Path(customers)
public interface CustomerService {
@GET
@Produces(application / json)
public List< Customer> listAllCustomers();
$ b $ @ bost
@Consumes(application / json)
@Produces(text / plain)

public long createCustomer(Customer customer );
}

然后将REST接口注入到您的GWT客户端类中。

  @Inject 
私人来电< CustomerService>客户服务;

定义了响应处理程序。

  RemoteCallback<长> callback = new RemoteCallback< Long>(){
public void callback(Long id){
Window.alert(Customer with ID:+ id);
}
};

最后调用REST方法。

  customerService.call(回调).listAllCustomers(); 

很简单吧?

您可能会从这个例子中相信Errai会为您当前的JAX-RS基础架构提供一个解决方案,但不幸的是,这个简单的例子并未涉及在尝试将您的GWT和Java REST代码库结合起来时,您可能会看到一些复杂的问题。以下是使用Errai和JAX-RS时需要注意的一些问题。



您需要实施CORS



通常,在实现GWT JAX-RS客户端时,您将针对外部REST服务器调试GWT应用程序。除非您实施 CORS ,否则这将不起作用,因为默认情况下浏览器托管GWT应用程序将不允许您的JavaScript代码联系未在同一个域中运行的服务器。事实上,您甚至可以在本地开发PC上运行REST服务器,但仍然遇到这些跨域问题,因为不同端口之间的调用也受到限制。



如果您正在使用RESTEasy,实现CORS可以用两种方法完成。第一个是使用 MessageBodyInterceptors 界面完成的。您提供了write()方法,并使用@Provider和@ServerInterceptor注释对类进行了注释。然后使用write()方法将Access-Control-Allow-Origin头添加到任何简单请求的响应中(简单请求不设置自定义标头,而请求主体只使用纯文本)。



第二种方法处理CORS预检请求(对于可能对用户数据造成副作用的HTTP请求方法 - 特别是对于GET以外的HTTP方法或POST使用某些MIME类型)。这些请求使用HTTP OPTIONS方法,并希望在回复中收到Access-Control-Allow-Origin,Access-Control-Allow-Methods和Access-Control-Allow-Headers标题。这是在下面的handleCORSRequest()方法中演示的。



注意



下面的REST接口允许任何和所有CORS请求,从安全角度来看这可能不合适。但是,假设在此级别防止或限制CORS将提供任何程度的安全性是不明智的,例如设置代理代表客户发出这些请求是相当简单的。

  @Path( / 1)
@Provider
@ServerInterceptor
public class RESTv1 implements RESTInterfaceV1,MessageBodyWriterInterceptor
{
@Override $ b $ public void write(final MessageBodyWriterContext context)抛出IOException,WebApplicationException
{context.getHeaders()。add(RESTInterfaceV1.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER,*);
context.proceed();

$ b @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();
}

}

有了这两种方法,您的REST服务器的任何调用都将提供适当的响应以允许跨源请求。



您需要接受并使用简单的POJO进行响应

简介说明了一个简单的REST界面,它以Long响应。 JAX-RS的Java和GWT实现都知道如何序列化和反序列化原始类和java.util集合等简单类。在现实世界的例子中,您的REST接口将会响应更复杂的对象。这是不同实现可能发生冲突的地方。



首先,JAX-RS和Errai使用不同的注释来自定义JSON和Java对象之间的对象编组。 Errai的注释类似@MapsTo和@Portable,而RESTEasy(或Jackson, JSON编组器)使用@JsonIgnore和@JsonSerialize之类的注释。这些注释是相互排斥的:GWT会抱怨杰克逊注释,杰克逊不能使用注释注释。



简单的解决方案是使用一组简单的POJO与您的休息界面。简单来说,我指的是没有参数构造函数的类,并且只有getter和setter方法直接与在JSON对象中传递的属性直接相关。简单的POJO可以由Errai和Jackson用他们的默认设置进行整理,从而消除了对不兼容的注释进行处理的必要。



Errai和Jackson也为源自来自不同地方的JSON字符串。 Jackson将使用getter和setter方法的名称,而Errai将使用实例变量的名称。所以请确保你的实例变量和getter / setter方法名称完全相同。这是正常的:

  public class Test 
{
private int count;
public int getCount(){return count;}
public void setCount(int count){this.count = count;}
}

这会导致问题:

  public class Test 
{
private int myCount;
public int getCount(){return myCount;}
public void setCount(int count){this.myCount = count;}
}

其次,为了实现一些业务功能,向这些REST数据对象添加其他方法是很有诱惑力的。但是,如果您这样做,在尝试使用GWT不支持的类之前不会过多,并且您可能会对 GWT不支持:日期格式化,克隆数组,将字符串转换为字节[] ...列表继续。因此,最好坚持使用REST数据对象中的基础知识,并使用类似于组合或基于组件的设计,完全在REST数据对象继承树之外实现任何业务逻辑。



注意

如果没有@Portable注释,您需要在调用ErraiApp.properties文件中的REST接口时,手动列出使用Errai的所有类



注意

您也不想离开地图。详情请参阅此错误



注意

不能在由JSON服务器返回的对象层次结构中使用嵌套参数化类型。请参阅此错误 Errai与byte []有关系。改为使用列表。有关更多详情,请参阅此论坛帖子



您需要使用Firefox进行调试



当使用GWT通过REST接口发送大量数据时,您将拥有用Firefox调试你的应用程序。根据我自己的经验,即使将一个小文件编码为字节[]并通过网络发送,也会导致Chrome中出现各种错误。由于JSON编码器试图处理损坏的数据,因此会引发各种不同的异常;在GWT应用程序的编译版本或在Firefox上进行调试时没有看到的异常。



不幸的是,Google尚未设法使Firefox GWT插件保持最新状态Mozilla的新版本发布周期,但您经常可以在GWT Google网上论坛中发现Alan Leung非官方发布的内容。 此链接有一个适用于Firefox 12的GWT插件版本,并且此链接有适用于Firefox 13的版本。



您需要使用Errai 2.1或更高版本 只有Errai 2.1或更高版本才能生成与Jackson兼容的JSON ,如果您试图将GWT与RESTEasy集成,这是必须的。杰克逊编组可以使用

  RestClient.setJacksonMarshallingActive(true); 

 < script type =text / javascript> 
erraiJaxRsJacksonMarshallingActive = true;
< / script>

您需要为高级功能创建单独的JAX-RS接口

如果您的JAX-RS REST接口返回高级对象,如 ATOM (或者更重要的一点,导入类像org.jboss.resteasy.plugins.providers.atom.Feed),您需要将您的REST接口跨越两个Java接口,因为Errai不知道这些对象,并且类可能不处于可轻松导入到GWT中的状态。

一个接口可以容纳普通的旧JSON和XML方法,而另一个接口可以容纳ATOM方法。这样您就可以避免在GWT应用程序中引用未知类的接口。
$ b

注意

Errai仅支持JSON mashalling在这一点上,尽管将来你可能能够定义自定义marshallers。


I'd like to call a REST service created using RESTEasy and JAX-RS from a GWT client application. What is the best process for using Errai to use a single code base for both server and client?

解决方案

We all love REST. It’s vendor, platform and language neutral; it’s simple to debug, implement and access; and it provides a solid back end to your cloud, browser, mobile and desktop apps.

Java developers can use libraries that support JAX-RS, like RESTEasy, to get a REST server up and running in just minutes. Then, using JAX-RS clients, these JAX-RS REST servers can be called from Java client applications with just a few lines of code.

But despite the fact that GWT shares much in common with Java, calling REST services from GWT can be a painful experience. Using the RequestBuilder class involves specifying the correct HTTP method, encoding your URL, and then either decode the response or create Overlay objects to represent the data being sent back by the REST server. This may not be a large overhead for calling one or two REST methods, but it does represent a lot of work when integrating GWT with a more complicated REST service.

This is where Errai comes in. Errai is a JBoss project that, among other things, implements the JAX-RS standard within GWT. In theory this means you can share your JAX-RS interface between your Java and GWT projects, providing a single source that defines the functionality of your REST server.

Calling the REST server from Errai involves only a few simple steps. First, you need the REST JAX-RS interface. This is a JAX-RS annotated Java interface that defines the methods that will be provided by your REST server. This interface can be shared between your Java and GWT projects.

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

The REST interface is then injected into your GWT client class.

@Inject
private Caller<CustomerService> customerService;

A response handler is defined.

RemoteCallback<Long> callback = new RemoteCallback<Long>() {
  public void callback(Long id) {
    Window.alert("Customer created with ID: " + id);
  }
};

And finally the REST method is called.

customerService.call(callback).listAllCustomers();

Pretty simple huh?

You may be led to believe from this example that Errai will provide a drop in solution to your current JAX-RS infrastructure, but unfortunately this simple example doesn’t touch on some of the complications that you are likely to see when trying to combine your GWT and Java REST code base. Here are some of the gotchas to be aware of when using Errai and JAX-RS.

You’ll need to implement CORS

Typically when implementing a GWT JAX-RS client, you will be debugging your GWT application against an external REST server. This won’t work unless you implement CORS, because by default the browser hosting the GWT application will not allow your JavaScript code to contact a server that is not running in the same domain. In fact you can even be running the REST server on your local development PC and still run into these cross domain issues, because calls between different ports are also restricted.

If you are using RESTEasy, implementing CORS can be done with two methods. The first is done using the MessageBodyInterceptors interface. You provide the write() method, and annotate your class with the @Provider and @ServerInterceptor annotations. The write() method is then used to add the "Access-Control-Allow-Origin" header to responses to any simple requests ("simple" requests don’t set custom headers, and the request body only uses plain text).

The second method handles the CORS preflight requests (for HTTP request methods that can cause side-effects on user data - in particular, for HTTP methods other than GET, or for POST usage with certain MIME types). These requests use the HTTP OPTIONS method, and expect to receive the "Access-Control-Allow-Origin", "Access-Control-Allow-Methods" and "Access-Control-Allow-Headers" headers in the reply. This is demonstrated in the handleCORSRequest() method below.

Note

The REST interface below allows any and all CORS requests, which may not be suitable from a security standpoint. However, it is unwise to assume that preventing or restricting CORS at this level will provide any degree of security, as setting up a proxy to make these requests on behalf of the client is quite 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();
    }

}

With these two methods in place, any call to your REST server will provide the appropriate responses to permit cross origin requests.

You’ll need to accept and respond with simple POJOs

The introduction illustrated a simple REST interface that responded with a Long. Both the Java and GWT implementations of JAX-RS know how to serialize and deserialize primitives and simple classes like the java.util collections.

In a real world example, your REST interface is going to respond with more complicated objects. This is where the different implementations can clash.

For a start, JAX-RS and Errai use different annotations to customize the marshalling of objects between JSON and Java objects. Errai has annotations like @MapsTo and @Portable, while RESTEasy (or Jackson, the JSON marshaller) uses annotations like @JsonIgnore and @JsonSerialize. These annotations are mutually exclusive: GWT will complain about the Jackson annotations, and Jackson can’t use the Errai annotations.

The simple solution is to have use set of simple POJOs with your rest interface. By simple I mean classes that have no-args constructors, and have only getter and setter methods that directly relate to properties that will be present in the JSON object as it travels over the wire. Simple POJOs can be marshalled by both Errai and Jackson with their default settings, removing the need to juggle incompatible annotations.

Errai and Jackson also source the names for the resulting properties in the JSON string from different places. Jackson will use the names of the getter and setter methods, while Errai will use the names of the instance variables. So make sure that your instance variables and getter/setter methods names are exactly the same. This is ok:

public class Test
{
    private int count;
    public int getCount() {return count;}
    public void setCount(int count) {this.count = count;}
}

This will cause problems:

public class Test
{
    private int myCount;
    public int getCount() {return myCount;}
    public void setCount(int count) {this.myCount = count;}
}

Secondly, it is tempting to add additional methods to these REST data objects to implement some business functionality. However, if you do this it won’t take long before you try and use a class that is not supported by GWT, and you might be surprised at what GWT doesn’t support: date formatting, cloning arrays, converting a String to a byte[]... The list goes on. So it’s best to stick to the basics in your REST data objects, and implement any business logic completely outside of the REST data object inheritance tree using something like composition or a component based design.

Note

Without the @Portable annotation, you will need to manually list any classes used Errai when calling the REST interface in the ErraiApp.properties file.

Note

You’ll also want to stay away from Maps. See this bug for details.

Note

You can't used nested parameterized types in the object hierarchy returned by your JSON server. See this bug and this forum post for details.

Note

Errai has issues with byte[]. Use a List instead. See this forum post for more details.

You’ll need to debug with Firefox

When it comes to sending large amounts of data over a REST interface using GWT, you will have to debug your application with Firefox. In my own experience, encoding even a small file into a byte[] and sending it over the network caused all manner of errors in Chrome. A variety of different exceptions will be thrown as the JSON encoder attempts to deal with corrupted data; exceptions that are not seen in the compiled version of the GWT application, or when debugging on Firefox.

Unfortunately Google has not managed to keep their Firefox GWT plugins up to date with Mozilla’s new release cycles, but you can quite often find unofficial released by Alan Leung in the GWT Google Groups forums. This link has a version of the GWT plugin for Firefox 12, and this link has a version for Firefox 13.

You’ll need to use Errai 2.1 or later

Only Errai 2.1 or later will produce JSON that is compatible with Jackson, which is a must if you are trying to integrate GWT with RESTEasy. Jackson marshalling can be enabled using

RestClient.setJacksonMarshallingActive(true);

or

<script type="text/javascript">
  erraiJaxRsJacksonMarshallingActive = true;
</script>

You'll need to create separate JAX-RS interfaces for advanced features

If your JAX-RS REST interface returns advanced objects, like ATOM (or more to the point, imports classes like org.jboss.resteasy.plugins.providers.atom.Feed), you'll need to split your REST interface across two Java interfaces, because Errai doesn't know about these objects, and the classes probably aren't in a state that can be easily imported into GWT.

One interface can hold your plain old JSON and XML methods, while the other can hold the ATOM methods. That way you can avoid having to reference the interface with the unknown classes in your GWT application.

Note

Errai only supports JSON mashalling at this point, although in future you may be able to define custom marshallers.

这篇关于如何将GWT与使用Errai的JAX-RS / RESTEasy服务器集成?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆