2011-02-23 81 views
0

我正在通過Spring配置RESTful Web服務,並使用各種表示形式(包括JSON)。我希望接口是對稱的,這意味着通過GET序列化爲JSON的對象的格式也是POST/PUT可以接受的格式。不幸的是,我只能讓GETs工作。使用Spring MVC 3.0生成/消費對稱JSON

這是我的用於發送和接收JSON配置,它由一個JSON消息轉換器和視圖的:

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> 
    <property name="messageConverters"> 
     <util:list> 
      <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/> 
     </util:list> 
    </property> 
</bean> 

<bean id="contentNegotiatingViewResolver" class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"> 
    <property name="mediaTypes"> 
     <util:map> 
      <entry key="json" value="application/json"/> 
     </util:map> 
    </property> 
    <property name="defaultViews"> 
     <util:list> 
      <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView"/> 
     </util:list> 
    </property> 
</bean> 

當我擊控制器,其與一個GET來返回一個對象,例如,一本書,它輸出這樣的東西。

{"book":{"isbn":"1234","author":"Leo Tolstoy","title":"War and Peace"}} 

如果我轉身,並通過POST重新提交一些類似的JSON或PUT,春都不能消耗它,抱怨Unrecognized field "book" (Class com.mycompany.Book), not marked as ignorable。此外,如果我剝離「書籍」包裝元素(我寧願不要,但只是爲了看看會發生什麼),我會得到一個400 BAD REQUEST。無論哪種情況,我的控制器代碼都不會被擊中。

這裏是我的控制器 - 我寧願沒有任何特定於JSON的代碼(或者我的類上的註釋被編組/解組),因爲它們將具有多個表示形式 - 我想使用Spring的解耦MVC基礎結構來推動這種類型事情(編組/視圖解決的/ etc。)到配置文件:

@RequestMapping(method=PUT, value="/books/{isbn}") 
@ResponseStatus(NO_CONTENT) 
public void saveBook(@RequestBody Book book, @PathVariable String isbn) { 
    book.setIsbn(isbn); 
    bookService.saveBook(book) 
} 

@RequestMapping(method=GET, value="/books/{isbn}") 
public ModelAndView getBook(@PathVariable String isbn) { 
    return new ModelAndView("books/show", "book", bookService.getBook(isbn)); 
} 

回答

1

雖然很尷尬,我回答我的問題留給後人:-)

原來,在我真正的代碼相當於控制方法,通過這個例子的方法,我貼表示:

void saveBook(@RequestBody Book book, @PathVariable String isbn) 

其實看起來更像這個(注:LongString):

void saveBook(@RequestBody Book book, @PathVariable Long isbn) 

而且值爲PA ssed不能轉換爲Long(它是字母數字)。所以......我搞砸了! :-)

然而,春天不是很好,只是吐出400 Bad Request。我附加了一個調試器來發現這一點。

使用ModelAndView仍然生成一個外部包裝元素,我將不得不以某種方式處理(因爲我想用戶ModelAndView支持JSP視圖等)。我可能必須爲此提供自定義視圖。


更新包裝元素上:

事實證明,它是由Spring創建一個編組地圖表示模型對象。這張地圖有一個名爲「書」的關鍵字(從我想的類名生成,因爲它在那裏,即使我只是返回一本書)。這裏是一個黑客的方式,直到我能找到更好的方式:

/** 
* When using a Spring Controller that is ignorant of media types, the resulting model 
* objects end up in a map as values. The MappingJacksonJsonView then converts this map 
* to JSON, which (possibly) incorrectly wraps the single model object in the map 
* entry's key. This class eliminates this wrapper element if there is only one model 
* object. 
*/ 
public class SimpleJacksonJsonView extends MappingJacksonJsonView { 

    @Override 
    @SuppressWarnings("unchecked") 
    protected Object filterModel(Map<String, Object> model) { 
     Map<String, Object> filteredModel = (Map<String, Object>) super.filterModel(model); 
     if(filteredModel.size() != 1) return filteredModel; 
     return filteredModel.entrySet().iterator().next().getValue(); 
    } 
} 
+0

MappingJackson2JsonView具有屬性extractValueFromSingleKeyModel。當它設置爲true時,它的行爲與您的SimpleJacksonJsonView完全相同。 – Felix 2013-10-15 15:43:38

+0

在這個問題的時候,我認爲財產不存在。謝謝! – SingleShot 2013-10-17 04:11:01

0

注意您指定使用得到你的控制器的方法,這樣你就可以配置特定POST方法能夠接收GET和POST表單REST Full方法,類似這樣:

@RequestMapping(method=GET, value="/books/{isbn}") 
public ModelAndView getBook(@PathVariable String isbn) { 
    return new ModelAndView("books/show", "book", bookService.getBook(isbn)); 
} 

@RequestMapping(method=POST, value="/books/{isbn}") 
public ModelAndView getByPostBook(@PathVariable String isbn) { 
    return getBook(isbn); 
} 
+0

謝謝。我不想「通過郵件獲取」,我想將PUT和/或POST JSON對象發佈到服務器(我正在創建一個RESTful服務)。我不相信問題在於如何定義我的請求映射,但我如何爲傳入的JSON配置消息轉換器。看到我的標記爲「PUT」的示例控制器方法 - 即由於消息解組中的錯誤而無法調用的接口。 – SingleShot 2011-02-23 18:52:17

+1

Sory @SingleShot,我不明白你的問題。那麼,如果你的問題是與消息轉換器,我認爲你將不得不customyze自己的。其實我不喜歡Jackson在做傑森對象時所做的事情。所以我找到了一種通過構建一個custon Gson消息轉換器來隱藏它的方法[看看它是否可以幫助你得到一個新想法](http://stackoverflow.com/questions/5019162/custom-httpmessageconverter-with-responsebody-to -do-json-things) – Iogui 2011-02-24 02:35:34

+0

謝謝。即使你沒有回答我確切的問題,你讓我瞭解Gson :-) – SingleShot 2011-02-24 03:38:38

0

沒有包裝書的@SingleShot,{「isbn」:「1234」,「作者」:「獅子座托爾斯泰」,「標題」:「戰爭與和平」}是JSON書籍實例的正確表示,由於您在GET方法中返回ModelAndView時所擁有的modelName「book」,因此添加了書籍包裝,而MappingJacksonJSONView則附加在書包裝上。

一個更好的模式是簡單地用

@ResponseBody
@RequestMapping(method=RequestMethod.GET, value="/books/{isbn}") 
public @ResponseBody Book getBook(@PathVariable String isbn) { 
    return new Book("isbn","title","author"); 
} 

返回你拿到書的對象和註釋的方法,關於你的PUT不解決正確的控制器方法,可以確認,你有你的要求內容類型爲application/json。

+0

謝謝。我更喜歡你的@ResponseBody建議,但是發現使用ModelAndView是必要的,以便讓我的控制器在需要HTML時解析爲JSP(除JSON外還生成HTML和XML)。我確實將Content-Type設置爲application/json。不過,我會探討你的建議。 – SingleShot 2011-02-23 21:20:02

+0

我測試了你建議的樣式 - 它有相同的結果:「書」包裝元素。 – SingleShot 2011-02-24 05:52:19

+0

我有一個hacker解決方案的包裝元素。看到我的答案。 – SingleShot 2011-02-24 07:07:52