2013-10-06 2 views
2

Я делаю простой сервер RESTful с SpringMVC, Gson и развертываю его в GAE. Все работает нормально, если я попытаюсь направить свой запрос следующим образом:Интеграция SpringMVC, Gson и GAE

import java.util.Arrays; 
import java.util.List; 

import javax.ws.rs.GET; 
import javax.ws.rs.Path; 
import javax.ws.rs.Produces; 
import javax.ws.rs.core.MediaType; 

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Component; 
import org.springframework.web.bind.annotation.ResponseBody; 

import com.generic.server.model.Widget; 
import com.generic.server.services.WidgetService; 
import com.google.gson.Gson; 

@Component 
@Path("/widget") 
public class WidgetRestService { 

    /** 
    * @return All the widgets info. 
    * @uri http://localhost:8888/rest/widget/ 
    */ 
    @GET @Path("/") @Produces(MediaType.APPLICATION_JSON) 
    public @ResponseBody String getAll() { 
    Gson g = new Gson(); 
    return g.toJson(Arrays.asList(new Widget("BuyerApp", "Buy something now!"), 
      new Widget("DogSwitcher", "Tired of your dog? Switch it right now!"))); 
    } 
} 

Это печатает желаемый результат. Но я хочу избавиться от экземпляра annoyinh Gson. Поэтому я сделал свой собственный заказ HttpMessageConverter.

@Component 
public class GSONHttpMessageConverter extends AbstractHttpMessageConverter<Object> { 

    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); 

    private GsonBuilder gsonBuilder = new GsonBuilder() 
      .excludeFieldsWithoutExposeAnnotation() 
      .setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); 

    public GSONHttpMessageConverter() { 
     super(new MediaType("application", "json", DEFAULT_CHARSET)); 
    } 

    @Override 
    protected boolean supports(Class<?> clazz) { 
     // should not be called, since we override canRead/Write instead 
     throw new UnsupportedOperationException(); 
    } 

    @Override 
    public boolean canRead(Class<?> clazz, MediaType mediaType) { 
     return MediaType.APPLICATION_JSON.isCompatibleWith(mediaType); 
    } 

    public boolean canWrite(Class<?> clazz, MediaType mediaType) { 
     return MediaType.APPLICATION_JSON.isCompatibleWith(mediaType); 
    } 

    public void registerTypeAdapter(Type type, Object serializer) { 
     gsonBuilder.registerTypeAdapter(type, serializer); 
    } 

    @Override 
    protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { 
     try { 
      Gson gson = gsonBuilder.create(); 
      return gson.fromJson(StringUtils.convertStreamToString(inputMessage.getBody()), clazz); 
     } catch (JsonParseException e) { 
      throw new HttpMessageNotReadableException("Could not read JSON: " + e.getMessage(), e); 
     } 
    } 

    @Override 
    protected void writeInternal(Object o, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { 
     Type genericType = TypeToken.get(o.getClass()).getType(); 

     BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputMessage.getBody(), DEFAULT_CHARSET)); 
     try { 
      // See http://code.google.com/p/google-gson/issues/detail?id=199 for details on SQLTimestamp conversion 
      Gson gson = gsonBuilder.create(); 
      writer.append(gson.toJson(o, genericType)); 
     } finally { 
      writer.flush(); 
      writer.close(); 
     } 
    } 
} 

И добавить к applicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:security="http://www.springframework.org/schema/security" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc" 
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:util="http://www.springframework.org/schema/util" 
    xsi:schemaLocation=" 
      http://www.springframework.org/schema/beans 
      http://www.springframework.org/schema/beans/spring-beans.xsd 
      http://www.springframework.org/schema/tx 
      http://www.springframework.org/schema/tx/spring-tx-3.1.xsd 
      http://www.springframework.org/schema/aop 
      http://www.springframework.org/schema/aop/spring-aop-3.1.xsd 
      http://www.springframework.org/schema/context 
      http://www.springframework.org/schema/context/spring-context-3.1.xsd 
      http://www.springframework.org/schema/security 
      http://www.springframework.org/schema/security/spring-security-3.0.3.xsd 
      http://www.springframework.org/schema/mvc 
      http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> 


    <mvc:annotation-driven /> 

    <!-- If you hit /home/ on the browser this will redirect you to the login 
     view --> 
    <mvc:view-controller path="/" view-name="login" /> 
    <mvc:resources location="/resources/" mapping="/resources/**" /> 

    <context:component-scan 
     base-package="com.generic.server.services, 
            com.generic.server.model, 
            com.generic.server.rest, 
            com.generic.server.ui.controller" /> 

    <bean id="viewResolver" 
     class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 
     <property name="prefix"> 
      <value>/WEB-INF/client/</value> 
     </property> 
     <property name="suffix"> 
      <value>.html</value> 
     </property> 
    </bean> 

    <mvc:default-servlet-handler /> 

    <bean 
     class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> 
     <property name="messageConverters"> 
      <list> 
       <bean class="com.generic.server.util.GSONHttpMessageConverter"/> 
      </list> 
     </property> 
    </bean> 
</beans> 

Теперь я изменит способ, что запрос отображается возвращать объекты:

/** 
    * @return All the widgets info. 
    * @uri http://localhost:8888/rest/widget/ 
    */ 
    @GET @Path("/") @Produces(MediaType.APPLICATION_JSON) 
    public @ResponseBody List<Widget> getAll() { 
     return Arrays.asList(new Widget("BuyerApp", "Buy something now!"), 
       new Widget("DogSwitcher", "Tired of your dog? Switch it right now!")); 
    } 

Но когда я пытаюсь ударить Localhost: 8888/rest/widget/сервер выйдет из строя и покажет мне это сообщение:

javax.ws.rs.WebApplicationException 
at com.sun.jersey.spi.container.ContainerResponse.write(ContainerResponse.java:268) 
at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1029) 
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:941) 
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:932) 
at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:384) 
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:451) 
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:632) 
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717) 
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511) 
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166) 
at com.google.appengine.api.socket.dev.DevSocketFilter.doFilter(DevSocketFilter.java:74) 
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 
at com.google.appengine.tools.development.ResponseRewriterFilter.doFilter(ResponseRewriterFilter.java:123) 
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 
at com.google.appengine.tools.development.HeaderVerificationFilter.doFilter(HeaderVerificationFilter.java:34) 
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 
at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:63) 
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 
at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43) 
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 
at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:125) 
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 
at com.google.appengine.tools.development.DevAppServerModulesFilter.doDirectRequest(DevAppServerModulesFilter.java:368) 
at com.google.appengine.tools.development.DevAppServerModulesFilter.doDirectModuleRequest(DevAppServerModulesFilter.java:351) 
at com.google.appengine.tools.development.DevAppServerModulesFilter.doFilter(DevAppServerModulesFilter.java:116) 
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388) 
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216) 
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182) 
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765) 
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418) 
at com.google.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:97) 
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) 
at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:485) 
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) 
at org.mortbay.jetty.Server.handle(Server.java:326) 
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542) 
at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923) 
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:547) 
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212) 
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404) 
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409) 
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582) 

Я довольно новичок с GAE, но я искал SO и другие страницы, и я думаю, что это путь, чтобы отобразить запросы REST. Любой отзыв оценен.

UPDATE:

Я также попытался использовать GsonHttpMessageConverter класс от RestTemplate. Еще одна вещь, которая обратила мое внимание, заключалась в том, что если я удалю бонус AnnotationMethodHandlerAdapter, я получаю одну и ту же трассировку стека. GsonHttpMessageConverter не используется.

Я также пытался реализовать мои собственные WebMvcConfigurationSupport и программно добавить мои сообщения конвертеры там, как это:

@Configuration 
public class WebConfig extends WebMvcConfigurationSupport { 

@Bean 
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { 
    RequestMappingHandlerAdapter handlerAdapter = super.requestMappingHandlerAdapter(); 
    handlerAdapter.getMessageConverters().add(0, new GSONHttpMessageConverter()); 
    return handlerAdapter; 
} 

Но это не сработало ни переопределение requestMappingHandlerAdapter ни configureMessageConverters методы. Трассировка стека была такой же, как если бы ничего не было изменено.

РЕШЕНИЕ:

Мой контекст приложения:

<beans xmlns="http://www.springframework.org/schema/beans" 
     xmlns:security="http://www.springframework.org/schema/security" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" 
     xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc" 
     xmlns:aop="http://www.springframework.org/schema/aop" xmlns:util="http://www.springframework.org/schema/util" 
     xsi:schemaLocation=" 
       http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd 
       http://www.springframework.org/schema/tx 
       http://www.springframework.org/schema/tx/spring-tx-3.1.xsd 
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-3.1.xsd 
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context-3.1.xsd 
       http://www.springframework.org/schema/security 
       http://www.springframework.org/schema/security/spring-security-3.0.3.xsd 
       http://www.springframework.org/schema/mvc 
       http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd"> 


     <!-- If you hit /home/ on the browser this will redirect you to the login 
      view --> 
     <mvc:view-controller path="/" view-name="login" /> 
     <mvc:resources location="/resources/" mapping="/resources/**" /> 

     <context:component-scan 
      base-package="com.generic.server.services, 
             com.generic.server.model, 
             com.generic.server.rest, 
             com.generic.server.ui.controller" /> 

     <bean id="viewResolver" 
      class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 
      <property name="prefix"> 
       <value>/WEB-INF/client/</value> 
      </property> 
      <property name="suffix"> 
       <value>.html</value> 
      </property> 
     </bean> 

     <mvc:annotation-driven> 
      <mvc:message-converters register-defaults="true"> 
       <bean class="com.generic.server.util.GsonHttpMessageConverter" /> 
      </mvc:message-converters> 
     </mvc:annotation-driven> 
    </beans> 

Мои веб-сервисы:

@Controller 
    @RequestMapping("/rest") 
    public class WidgetRestService { 

     /** 
     * @return All the widgets info. 
     * @uri http://localhost:8888/rest/widget/ 
     */ 
     @RequestMapping(value="/widget", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON) 
     public @ResponseBody List<Widget> getAll() { 
      return Arrays.asList(new Widget("BuyerApp", "Buy something now!"), new Widget("DogSwitcher", "Tired of your dog? Switch it right now!"));  
     } 
    } 

Основная проблема заключалась в том, что я пытался использовать Джерси, чтобы выставить веб-сервисы , Вместо этого я меняюсь, и теперь я использую аннотации SpringMVC.

+0

Просто сомневайтесь. Вы изменили путь к '/', но вы также обновили запись остального сервлета для прослушивания '/'? –

+0

@HarshaR Единственное, что я изменил в webservice, это тип возвращаемого метода. Это все. Я уверен, что вы не ошибка отображения или что-то в этом роде. – 4gus71n

+0

Помогает ли это? http://aruld.info/handling-generified-collections-in-jersey-jax-rs/ –

ответ

5

Вы используете <mvc:annotation-driven />И вы также регистрируя AnnotationMethodHandlerAdapter, который конфликтует с RequestMappingHandlerAdapter, зарегистрированным <mvc:annotation-driven />' (So basically the AnnotationMethodHandlerAdapter` ничего не делает и только съедает сервер me me Мори).

Вместо этого вы должны использовать пространство имен для регистрации конвертера.

<mvc:annotation-driven> 
    <mvc:message-converters register-defaults="true"> 
     <bean class="com.generic.server.util.GSONHttpMessageConverter"/> 
    </mvc:message-converters> 
</mvc:annotation-driven> 

Рядом с этим вы смешиваете аннотацию JAX-WS и Spring MVC в конечной точке отдыха. Предполагая, что вы хотите использовать Spring изменить его к следующему

@Controller 
@RequestMapping("/widget") 
public class WidgetRestService { 

    /** 
    * @return All the widgets info. 
    * @uri http://localhost:8888/rest/widget/ 
    */ 
    @RequestMapping(method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON) 
    public @ResponseBody String getAll() { 
     return Arrays.asList(new Widget("BuyerApp", "Buy something now!"), new Widget("DogSwitcher", "Tired of your dog? Switch it right now!"));  
    } 
} 

Вместо того, чтобы создать свой собственный конвертер, вы можете проверить пружинный андроида проект и использовать их GsonHttpMessageConverter.

+0

Спасибо за ваш ответ, я попробую. – 4gus71n

+0

работает как шарм, большое вам спасибо, я обновлю вопрос, чтобы поместить исправленный код. – 4gus71n

+0

BTW Я не смешиваю JAX-WS и Spring MVC Я использую Джерси, чтобы разоблачить веб-службы Restfull. – 4gus71n

0

Когда serialising GSON может вывести тип из объекта, поэтому нет необходимости передавать тип:

writer.append(gson.toJson(o, genericType)); 

вместо этого попытаться просто:

writer.append(gson.toJson(o)); 
+0

Я пробовал, но получаю те же трассировки стека. Я сделал обновление. – 4gus71n

Смежные вопросы