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

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

问题描述

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

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?

推荐答案

我们都喜欢 REST.它是供应商、平台和语言中立的;调试、实施和访问都很简单;它为您的云、浏览器、移动和桌面应用程序提供了可靠的后端.

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 开发人员可以使用支持 JAX-RS 的库,例如 RESTEasy,只需几分钟即可启动并运行 REST 服务器.然后,使用 JAX-RS 客户端,这些 JAX-RS REST只需几行代码即可从 Java 客户端应用程序调用服务器.

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.

尽管 GWT 与 Java 有很多共同点,但从 GWT 调用 REST 服务可能是一种痛苦的体验.使用 RequestBuilder 类涉及指定正确的 HTTP 方法、编码您的 URL,然后解码响应或创建 Overlay 对象来表示 REST 服务器发回的数据.这对于调用一两个 REST 方法来说可能不是很大的开销,但在将 GWT 与更复杂的 REST 服务集成时,它确实代表了大量工作.

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.

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

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.

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

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

然后将 REST 接口注入您的 GWT 客户端类.

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

@Inject
private Caller<CustomerService> customerService;

定义了响应处理程序.

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

最后调用 REST 方法.

And finally the REST method is called.

customerService.call(callback).listAllCustomers();

很简单吧?

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

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.

您需要实施 CORS

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

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.

如果您使用的是 RESTEasy,则可以通过两种方法实现 CORS.第一个是使用 MessageBodyInterceptors 接口完成的.您提供 write() 方法,并使用 @Provider 和 @ServerInterceptor 注释来注释您的类.然后使用 write() 方法将Access-Control-Allow-Origin"标头添加到任何简单请求的响应中(简单"请求不设置自定义标头,请求正文仅使用纯文本).

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

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

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.

注意

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

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

}

使用这两种方法后,对 REST 服务器的任何调用都将提供适当的响应以允许跨源请求.

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

你需要接受并用简单的 POJO 做出回应

介绍说明了一个简单的 REST 接口,该接口以 Long 响应.JAX-RS 的 Java 和 GWT 实现都知道如何序列化和反序列化原语和简单的类,如 java.util 集合.

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.

在现实世界的示例中,您的 REST 接口将使用更复杂的对象进行响应.这就是不同实现可能发生冲突的地方.

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

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

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.

简单的解决方案是在您的休息界面中使用一组简单的 POJO.简单地说,我的意思是具有无参数构造函数的类,并且只有 getter 和 setter 方法,这些方法与 JSON 对象通过网络传输时将出现在 JSON 对象中的属性直接相关.Errai 和 Jackson 可以使用默认设置对简单的 POJO 进行编组,从而无需处理不兼容的注释.

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 和 Jackson 还从不同位置获取 JSON 字符串中结果属性的名称.Jackson 将使用 getter 和 setter 方法的名称,而 Errai 将使用实例变量的名称.因此,请确保您的实例变量和 getter/setter 方法名称完全相同.没关系:

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

这会导致问题:

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

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

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.

注意

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

注意

您还想远离地图.有关详细信息,请参阅此错误.

注意

您不能在 JSON 服务器返回的对象层次结构中使用嵌套的参数化类型.请参阅此错误此论坛帖子了解详情.

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.

注意

Errai 的 byte[] 有问题.改用列表.有关详细信息,请参阅此论坛帖子.

您需要使用 Firefox 进行调试

当涉及到使用 GWT 通过 REST 接口发送大量数据时,您必须使用 Firefox 调试您的应用程序.根据我自己的经验,即使是将一个小文件编码为 byte[] 并通过网络发送它也会导致 Chrome 中的各种错误.当 JSON 编码器尝试处理损坏的数据时,会抛出各种不同的异常;在 GWT 应用程序的编译版本中或在 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.

不幸的是,Google 没有设法使他们的 Firefox GWT 插件与 Mozilla 的新发布周期保持同步,但是您经常可以在 GWT Google Groups 论坛中找到 Alan Leung 发布的非官方版本.此链接有适用于 Firefox 12 的 GWT 插件版本,此链接有一个适用于 Firefox 13 的版本.

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.

您需要使用 Errai 2.1 或更高版本

只有 Errai 2.1 或更高版本会产生与 Jackson 兼容的 JSON,如果你是,这是必须的尝试将 GWT 与 RESTEasy 集成.可以使用

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

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

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

如果您的 JAX-RS REST 接口返回高级对象,例如 ATOM(或者更重要的是,导入像 org.jboss.resteasy.plugins.providers.atom.Feed 这样的类),您需要将 REST 接口拆分为两个 Java 接口,因为 Errai 没有了解这些对象,并且这些类可能并不处于可以轻松导入 GWT 的状态.

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.

一个接口可以保存普通的旧 JSON 和 XML 方法,而另一个可以保存 ATOM 方法.这样您就可以避免在 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.

注意

Errai 目前仅支持 JSON mashalling,尽管将来您可能可以定义自定义编组器.

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

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

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