2013-01-23 63 views
6

嘗試使用Spring MVC構建RESTful Web服務。如何使用Spring MVC設計通用響應構建器/ RESTful Web服務?

控制器應返回特定的Java類型,但響應正文必須是通用信封。如何才能做到這一點?

下面的代碼段是我到目前爲止有:

控制器的方法:

@Controller 
    @RequestMapping(value = "/mycontroller") 
    public class MyController { 

     public ServiceDetails getServiceDetails() { 
      return new ServiceDetails("MyService"); 
     } 
    } 

響應信封:

public class Response<T> { 

     private String message; 
     private T responseBody; 

    } 

ServiceDetails代碼:

public class ServiceDetails { 

     private String serviceName; 

     public ServiceDetails(String serviceName) { 
      this.serviceName = serviceName; 
     } 
    } 

給客戶的預期最終響應應顯示爲:

@Controller 
@RequestMapping(value = "/mycontroller") 
public class MyRestController { 

    @Autowired 
    private MyController myController; 

    @RequestMapping(value = "/details") 
    public @ResponseBody Response<ServiceDetails> getServiceDetails() { 
     return new Response(myController.getServiceDetails(),"Operation OK"); 
    } 
} 

該解決方案保留原始MyController獨立的來源:

{ 

    "message" : "Operation OK" 
    "responseBody" : { 
         "serviceName" : "MyService" 
         } 

    } 
+0

如果您使用的是Jackson或FlexJson,那麼爲什麼不能直接在您的方法中返回Response對象呢? JSON序列化程序應該能夠序列化任何複雜的對象。 – CodeChimp

+0

我正在使用傑克遜。但我仍然希望控制器返回請求特定的java類型。我認爲它更清潔,更直觀。 –

+0

加上有一些鍋爐板代碼來構建響應對象,所以我想只是從一個地方做到這一點 –

回答

0

你可以做的有MyRestController只是包裹導致Response這個樣子你的REST代碼。看來你需要包括傑克遜在你的類路徑,以便Spring會自動神奇地序列化到JSON(見this瞭解詳細信息)

編輯

看來你需要的東西更通用的......所以這裏一條建議。

@Controller 
@RequestMapping(value = "/mycontroller") 
public class MyGenericRestController { 

    @Autowired 
    private MyController myController; 

    //this will match all "/myController/*" 
    @RequestMapping(value = "/{operation}") 
    public @ResponseBody Response getGenericOperation(String @PathVariable operation) { 
      Method operationToInvoke = findMethodWithRequestMapping(operation); 
      Object responseBody = null; 
      try{ 
       responseBody = operationToInvoke.invoke(myController); 
      }catch(Exception e){ 
       e.printStackTrace(); 
       return new Response(null,"operation failed"); 
      } 
     return new Response(responseBody ,"Operation OK"); 
    } 

    private Method findMethodWithRequestMapping(String operation){ 
     //TODO 
     //This method will use reflection to find a method annotated 
     //@RequestMapping(value=<operation>) 
     //in myController 
     return ... 
    } 
} 

並保留原始「myController的」差不多,因爲它是:

@Controller 
public class MyController { 

    //this method is not expected to be called directly by spring MVC 
    @RequestMapping(value = "/details") 
    public ServiceDetails getServiceDetails() { 
     return new ServiceDetails("MyService"); 
    } 
} 

主要問題有這樣的:MyController的@RequestMapping無需可能被一些自定義的註釋被替換(和適應findMethodWithRequestMapping到對此自定義註釋執行自省)。

+0

的事情是,我想避免寫很多次新的Response()。我希望實現控制器方法的人不必知道響應信封,但只需返回特定類型,並讓攔截器採用該類型並插入響應信封。 –

+0

那麼消息字段呢?有人需要填寫它的地方... – ben75

+0

@Eli剛剛編輯我的帖子更通用的建議 – ben75

0

默認情況下,Spring MVC使用org.springframework.http.converter.json.MappingJacksonHttpMessageConverter通過Jackson序列化/反序列化JSON。

我不知道這是否是一個好主意,但您解決問題的方法之一是擴展此類,並重寫writeInternal方法:

public class CustomMappingJacksonHttpMessageConverter extends MappingJacksonHttpMessageConverter { 

    @Override 
    protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { 
     super.writeInternal(new Response(object, "Operation OK"), outputMessage); 
    } 
} 

如果您使用XML配置,可以啓用自定義轉換器,如下所示:

<mvc:annotation-driven> 
    <mvc:message-converters> 
     <bean class="path.to.CustomMappingJacksonHttpMessageConverter"> 
    </mvc:message-converters> 
</mvc:annotation-driven> 
+0

,看起來不錯。但這意味着這個轉換器將用於我的所有控制器?我只需要一個特定的一個...是否有可能?基本上我是重寫MappingJacksonJsonView和使用自定義視圖作爲該特定控制器的視圖,但似乎沒有工作。彈簧總是使用默認的傑克遜視圖出於某種原因 –

+0

我真的不知道如何爲單個控制器設置特定的轉換器。單調乏味,但也許一個可以接受的解決方案是爲這個特定的控制器創建一個單獨的spring context(即servlet)? – reidarok