当前位置: 首页 > 工具软件 > GWT-REST > 使用案例 >

在GWT中使用Restlet及JSON访问Spring MVC REST应用

越狐若
2023-12-01
在GWT中使用Restlet及JSON访问Spring MVC REST应用

GWT基于RPC方式调用GAE应用,虽然开发效率还可以,但是将客户端和服务器端紧耦合在了一起,不适用于与基于其他框架的服务器端应用互操作,例如基于REST的Web Service等。
另一方面,Spring 3 mvc 框架提供了REST支持,并且简化了mvc配置,可以通过annotation快速完成controller等部件的开发及 restful url 的设定,并且可以通过ContentNegotiation的方式实现View的自动匹配,从而能够实现Controller或Service的一种实现应对多种不同的表现层实现的需求,例如应用需要同时在浏览器和其他移动客户端中展现的场景。
下面以Spring 3 mvc框架作为服务器端实现,以GWT作为浏览器端实现为例进行描述

一、基本架构
浏览器端使用GWT+Restlet GWT extension+piriti,通过JSON对象与基于Spring mvc的服务器端应用互操作,Spring MVC中使用MappingJacksonJsonView作为JSON View的实现,同时使用ContentNegotitationViewResolver实现多View的自动匹配和同步使用。

二、浏览器端
传统的GWT module,html,entrypoint, uibinder仍然需要,但是涉及RPC的delegate,async remote service interface, async remote service callback, remote service interface, remote service interface implementation等等都不再需要,从此也可以看出不使用RPC带来的一定程度上的简化。
使用Restlet GWT Edition与服务器端通讯。注意一定要下载GWT Edition,其他版本的Restlet package目录结构与GWT版本的不同。 代码片段如下:

  ... in widget event handler method

    ClientResource res =new ClientResource("/report.json");     //declare a restlet client resource


      // Set the callback object invoked when the response is received.     
    res.setOnResponse(new Uniform() {
        publicvoid handle(Request request, Response response) {
            // Get the representation as an JsonRepresentation

            JsonRepresentation rep =new JsonRepresentation(response.getEntity());


            // Displays the properties and values.

            try {

                JSONObject object = rep.getValue().isObject();

                if (object !=null) {

                    for (String key : object.keySet()) {

                        System.out.println(key+":"+object.get(key));

                        //... use object.get(key).isXXX to retrive the value of key

                     }

                 }

            } catch (IOException e) {

                e.printStackTrace();

            } catch (Exception e) {

                e.printStackTrace();

            }

        }
    });
    res.get();     //send a rest-get request, or use res.get(MediaType.xxx) to specify request media type
    ...


浏览器与服务器端数据交换格式使用JSON,这是一种轻量的数据表现形式,简述如下:
JSON TypeData FormatJava Type
JSONArray[{"name":"aaaaa","size":100},{"name":"bbb","size":200}]List
JSONObject{"name":"bbb","size":200}POJO Object
JSONArray wrapped by JSONObject{"reportList":[{"name":"aaaaa","size":100},{"name":"bbb","size":200}]}Map(only one entry)
complicated JSONObject

{"reportList":[{"name":"aaaaa","size":100},{"name":"bbb","size":200}],

"report":{"name":"ccccc","size":0}}
Map

当应用涉及的实体对象很多时,POJO和JSON之间的相互转换就成了影响开发和运行时效率的问题,(POJO到JSON的转换称为Serialize,反之称为Deserialize)像上边代码片段中的处理方式显然是低效的,可以在实体内部增加fromJson(JSONObject) 和toJson()方法,在方法内部根据实体的结构与JSON数据进行映射,这种方法运行时效率不错但开发时仍然低效, 有没有更好的方式呢? 目前在GWT中可用的方法及方法间比较可见  http://code.google.com/p/piriti/wiki/Comparison http://stackoverflow.com/questions/683123/json-java-serialization-that-works-with-gwt  ,在已知的方法中,piriti(毛利语‘桥’的意思)是一个很优雅的解决方案,它支持的数据类型比较全,包括部分的java.util.Collection类型,更新比较迅速,已支持目前最新的GWT 2.4,对已有的POJOs改动不大,自带Restlet扩展能够使代码呈现GWT风格的onSuccess和onError处理方式;缺点是目前(0.8版本)还不支持java.util.Map数据类型,另外依赖其他类库比较多,当然如果使用maven进行依赖管理这就不是问题了。这部分的代码片段如下:

对POJO的改动

实际上对POJO可以不做改动,只需要分别建立接口继承自JsonReader(Deserialize时用)和JsonWriter(Serialize时用)即可,不需要具体的实现类。例如针对一个Report POJO对象,建立

public interface ReportJsonReader extends JsonReader<Report> {}

public interface ReportJsonWriter extends JsonWriter<Report> {}

然后在uibinder初始化时通过GWT生成具体的实现类
public  static  final  ReportJsonReader  reportReader  = GWT.create(ReportJsonReader. class );
public  static  final  ReportJsonWriter  reportWriter  = GWT.create(ReportJsonWriter. class );  
当然也可以把reader和writer作为成员加入到POJO中,如果你有POJO对象的源码并且这一对象不用在客户端和服务器端共享的话。

Deserialize处理

    ... in widget event handler method

    ClientResource res =new ClientResource("/report.json");     //declare a restlet client resource


    res.setOnResponse(new JsonModelsResponse<Report>(reportReader) {

        @Override

        public void onError(IOException error, Request request, Response response) {

             //TODO: Handle error          

        }


        @Override
         publicvoid onSuccess(List<Report> models, Request request,  Response response) {

                     // models is deserialized list filled with POJO objects

        }


  });
  res.get();

  res.setReference(" /report/abc.json ");

    res.setOnResponse(new JsonModelResponse<Report>(reportReader) {

        @Override

        public void onError(IOException error, Request request, Response response) {

             //TODO: Handle error          

        }


        @Override
         publicvoid onSuccess(Report model, Request request,  Response response) {

                     // model is deserialized POJO objects

        }


  });
  res.get();


Serialize处理(未完)



如果手工对项目中依赖的类库进行管理, 开发时需要的类库包括:
[piriti]
piriti-user-0.8.jar 
piriti-dev-0.8.jar 
piriti-restlet-0.8.jar 
[piriti 依赖的]
gin-1.5-post-gwt-2.2.jar
guice-assistedinject-snapshot.jar 
totoe-0.4.jar 
velocity-1.7.jar 
guice-3.0.jar 
javax.inject.jar aopalliance.jar 
[Restlet]
org.restlet.jar
org.restlet.ext.json.jar
这些依赖项除了开发时需要,运行时也需要用到。

Module描述符中需要引用的项包括:
<!-- 
 此项被后续项间接引用  
 <inherits name="com.google.gwt.json.JSON" />
 -->

<inheritsname="name.pehl.piriti.json.JSON"/>

<inheritsname="name.pehl.piriti.restlet.Restlet"/>

< inherits name="name.pehl.piriti.restlet.json.JSON"/>

处理异常(未完)

三、服务器端
Spring MVC部分没有特殊的内容,在解决上述已知问题的前提下,Controller的那些响应方法可以继续使用ModelMap类型作为返回值(这对于还需要进行JSP/JSTL展现是必要的),但注意目前其中只能包含一个Entry(piriti0.8还不支持Map)。
MVC Dispatcher配置中使用CotentNegotiatingViewResolver进行View解析,使用MappingJacksonJsonView作为针对json请求的响应View。

    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver" ]]>

        <propertyname="ignoreAcceptHeader"value="false"/>

        <property name="defaultContentType"value="text/html"/> 

        <property name="mediaTypes" ]]>

            <map ]]>

                <entry key="atom"value="application/atom+xml"/>

                <entry key="html"value="text/html"/>

                <entry key="json"value="application/json"/>

            </map ]]>

        </property ]]>

        <property name="viewResolvers" ]]>

            <list ]]>

                <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" ]]>

                    <property name="prefix" value="/WEB-INF/views/"/>

                    <property name="suffix" value=".jsp"/>

                </bean ]]>

            </list ]]>

        </property ]]>

        <property name="defaultViews" ]]>

            <list ]]>

                <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" ]]>

                    <!-- 默认即处理application/json请求 <property name="contentType" value="application/json"/> -->

                </bean ]]>

            </list ]]>

        </property ]]>

    </bean ]]> 


由于GWT应用还会请求一些html或css等资源,因此还需要在MVC Dispatcher配置中将针对这些资源的请求直接转发,例如:

<mvc:resources mapping="/**.html" location="/"/>

<mvc:resources mapping="/**.css" location="/"/>

<mvc:resources mapping="/**.ico" location="/"/>

<mvc:resources mapping="/gwt-module-name/**" location="/gwt-module-name/"/>


参考资料
 类似资料: