gwt - 如何使用Errai将GWT与JAX-RS/RESTEasy服务器集成?

标签 gwt jboss resteasy errai

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

最佳答案

我们都喜欢REST。它与供应商,平台和语言无关;调试,实现和访问非常简单;并且为您的云,浏览器,移动和 table 面应用程序提供了坚实的后端。

Java开发人员可以使用支持JAX-RS的库(例如RESTEasy)来在短短几分钟内启动并运行REST服务器。然后,只需几行代码就可以从Java客户端应用程序调用这些JAX-RS REST服务器using JAX-RS clients

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

这是Errai出现的地方。Errai是一个JBoss项目,其中包括implements the JAX-RS standard within GWT。从理论上讲,这意味着您可以在Java和GWT项目之间共享JAX-RS接口(interface),并提供定义REST服务器功能的单一来源。

从Errai调用REST服务器仅涉及几个简单步骤。首先,您需要REST JAX-RS接口(interface)。这是一个带有JAX-RS注释的Java接口(interface),该接口(interface)定义了REST服务器将提供的方法。此接口(interface)可以在Java和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);
}

然后将REST接口(interface)注入(inject)到您的GWT客户端类中。
@Inject
private Caller<CustomerService> customerService;

定义了响应处理程序。
RemoteCallback<Long> callback = new RemoteCallback<Long>() {
  public void callback(Long id) {
    Window.alert("Customer created with ID: " + id);
  }
};

最后调用REST方法。
customerService.call(callback).listAllCustomers();

很简单吧?

从这个例子中,您可能会相信,Errai将为您当前的JAX-RS基础架构提供一个解决方案,但是不幸的是,这个简单的例子并没有涉及您在尝试合并自己的组合时可能会遇到的一些复杂情况。 GWT和Java REST代码库。这是使用Errai和JAX-RS时要注意的一些陷阱。

您需要实现CORS

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

如果您使用的是RESTEasy,则可以通过两种方法来实现CORS。第一个是使用MessageBodyInterceptors接口(interface)完成的。您提供write()方法,并使用@Provider和@ServerInterceptor批注对类进行批注。然后使用write()方法将“Access-Control-Allow-Origin” header 添加到对任何简单请求的响应(“简单”请求未设置自定义 header ,并且请求正文仅使用纯文本)。

第二种方法处理CORS预检请求(对于可能对用户数据造成副作用的HTTP请求方法,尤其是对于GET以外的HTTP方法或某些MIME类型的POST使用)。这些请求使用HTTP OPTIONS方法,并期望在回复中接收“Access-Control-Allow-Origin”,“Access-Control-Allow-Methods”和“Access-Control-Allow-Headers” header 。在下面的handleCORSRequest()方法中对此进行了演示。

笔记

下面的REST接口(interface)允许任何和所有CORS请求,从安全的角度来看,这可能不适合。但是,认为在此级别上阻止或限制CORS将提供任何程度的安全性是不明智的,因为setting up a proxy代表客户端发出这些请求非常简单。
@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服务器的任何调用都将提供适当的响应,以允许跨源请求。

您需要通过简单的POJO接受并回复

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

在一个真实的示例中,您的REST接口(interface)将响应更复杂的对象。这是不同的实现可能发生冲突的地方。

首先,JAX-RS和Errai使用不同的注释来自定义JSON和Java对象之间的对象编码。 Errai具有@MapsTo和@Portable之类的注释,而RESTEasy(或Jackson,the JSON marshaller)则使用@JsonIgnore和@JsonSerialize之类的注释。这些注释是互斥的:GWT将提示Jackson注释,而Jackson无法使用Errai注释。

一种简单的解决方案是在您的rest接口(interface)中使用一组简单的POJO。简单来说,我的意思是说,这些类具有无参数构造函数,并且仅具有直接与JSON对象在通过导线传递时将存在于属性中的属性相关的getter和setter方法。 Errai和Jackson可以使用它们的默认设置来整理简单的POJO,而无需处理不兼容的注释。

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 doesn’t support感到惊讶:日期格式化,克隆数组,将String转换为byte []。 .. list 继续。因此,最好坚持使用REST数据对象中的基础知识,并使用诸如合成或基于组件的设计等方法完全在REST数据对象继承树之外实现任何业务逻辑。

笔记

如果没有@Portable批注,则需要manually list any classes used Errai when calling the REST interface in the ErraiApp.properties file

笔记

您还希望远离 map 。有关详细信息,请参见this bug

笔记

您不能在JSON服务器返回的对象层次结构中使用嵌套的参数化类型。有关详细信息,请参见this bugthis forum post

笔记

Errai的byte []有问题。请改用列表。有关更多详细信息,请参见this forum post

您需要使用Firefox进行调试

如果要使用GWT通过REST接口(interface)发送大量数据,则必须使用Firefox调试应用程序。以我自己的经验,即使将很小的文件编码为byte []并通过网络发送,也会导致Chrome中出现各种错误。 JSON编码器尝试处理损坏的数据时,将引发各种不同的异常。在GWT应用程序的已编译版本中或在Firefox上调试时未看到的异常。

不幸的是,Google尚未设法使其Mozilla Firefox GWT插件与Mozilla的新发行周期保持同步,但您经常可以在GWT Google网上论坛中找到Alan Leung的非官方发行版本。 This link具有适用于Firefox 12的GWT插件版本,而this link具有适用于Firefox 13的版本。

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

Only Errai 2.1 or later will produce JSON that is compatible with Jackson,如果您想将GWT与RESTEasy集成,则必须使用ATOM。可以使用以下命令启用 jackson 编码
RestClient.setJacksonMarshallingActive(true);

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

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

如果您的JAX-RS REST接口(interface)返回诸如Errai only supports JSON mashalling at this point之类的高级对象(或者更重要的是,导入诸如org.jboss.resteasy.plugins.providers.atom.Feed之类的类),则需要将REST接口(interface)拆分为两个Java接口(interface),因为Errai不了解这些对象,并且类可能处于不容易导入GWT的状态。

一个接口(interface)可以容纳您简单的旧JSON和XML方法,而另一个接口(interface)可以容纳ATOM方法。这样,您可以避免在GWT应用程序中引用带有未知类的接口(interface)。

笔记

ojit_a,尽管将来您可能可以定义自定义编码器。

关于gwt - 如何使用Errai将GWT与JAX-RS/RESTEasy服务器集成?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10909665/

相关文章:

gwt - 在 DockLayoutPanel 中嵌入 TabLayoutPanel

java - JBoss 和 Oracle 12c : datasource bound successfully but cannot connect

mysql - Hibernate 选择子查询

eclipse - 更新项目配置后,Maven 从 Eclipse 和 JBossAS 工具中隐藏 Web.xml

resteasy - 在 RestEASY 中设置连接超时

rest - 如何将日历参数作为输入传递给休息服务?

java - jax-rs ResponseFilter 中抛出的异常不会导致回滚

gwt - 如何在GinMapBinder中正确使用TypeLiteral?

java - 如何从客户端简单地将对象添加到 hibernate 中的集合

gwt - GWT和PlayFramework结合使用的策略