2016-10-29 19 views
2

我有一個彈簧REST控制器,其返回以下JSON有效載荷:如何從Spring REST存儲庫中包裝JSON響應?

[ 
    { 
    "id": 5920, 
    "title": "a title" 
    }, 
    { 
    "id": 5926, 
    "title": "another title", 
    } 
] 

與其對應的GET請求方法的其餘部分的控制器:

@RequestMapping(value = "example") 
public Iterable<Souvenir> souvenirs(@PathVariable("user") String user) { 
    return new souvenirRepository.findByUserUsernameOrderById(user); 
} 

現在紀念品類是一個POJO:

@Entity 
@Data 
public class Souvenir { 

    @Id 
    @GeneratedValue 
    private long id; 

    private String title; 

    private Date date; 
} 

關於https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outsidehttp://haacked.com/archive/2009/06/25/json-hijacking.aspx/我想包裹在一個對象的響應,所以臨屋區它不容易受到攻擊。當然,我可以做這樣的事情:

@RequestMapping(value = "example") 
public SouvenirWrapper souvenirs(@PathVariable("user") String user) { 
    return new SouvenirWrapper(souvenirRepository.findByUserUsernameOrderById(user)); 
} 

@Data 
class SouvenirWrapper { 
    private final List<Souvenir> souvenirs; 

    public SouvenirWrapper(List<Souvenir> souvenirs) { 
    this.souvenirs = souvenirs; 
    } 
} 

這將導致以下JSON有效載荷:

{ 
    "souvenirs": [ 
     { 
      "id": 5920, 
      "title": "a title" 
     }, 
     { 
      "id": 5926, 
      "title": "another title", 
     } 
    ] 
    } 

這有助於防止一些JSON/JavaScript攻擊,但我不喜歡的詳細程度包裝類。我當然可以用泛型概括上述方法。在Spring生態系統中是否有另一種方法可以實現相同的結果(帶有註釋或類似的東西)?一個想法是,行爲是由Spring自動完成的,所以無論何時有一個REST控制器返回一個對象列表,它可以將這些對象包裝在一個對象包裝器中,以便沒有直接的對象列表被序列化?

+0

春天的選擇?你在說什麼? Struts有它,Spring沒有。闡述你的問題。只有包裝是帽子春天對你來說,或者如果em不結束,你可以隨時擴展消息。 –

+0

這裏你去:http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/method/support/HandlerMethodReturnValueHandler.html。在你自己的實現中,你可以把它包裝成你需要的任何結構。 –

+1

什麼是一個有趣的問題..如果實際值是可迭代的,你可以嘗試使用@ControllerAdvice來返回你的通用包裝器,或者你可以嘗試去解壓縮反序列化器,可能是Jackson Http消息轉換器。讓我們張貼:) –

回答

1

我結束了以下解決方案(感謝@瓦迪姆 - kirilchuk):

我的控制器看起來還是與之前完全相同:

@RequestMapping(value = "example") 
public Iterable<Souvenir> souvenirs(@PathVariable("user") String user) { 
    return new souvenirRepository.findByUserUsernameOrderById(user); 
} 

我增加了以下實施ResponseBodyAdvice基本上得到執行當引用的包中的控制器試圖響應客戶端呼叫時(據我瞭解):

@ControllerAdvice(basePackages = "package.where.all.my.controllers.are") 
public class JSONResponseWrapper implements ResponseBodyAdvice { 
    @Override 
    public boolean supports(MethodParameter returnType, Class converterType) { 
     return true; 
    } 

    @Override 
    @SuppressWarnings("unchecked") 
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { 
     if (body instanceof List) { 
      return new Wrapper<>((List<Object>) body); 
     } 
     return body; 
    } 

    @Data // just the lombok annotation which provides getter and setter 
    private class Wrapper<T> { 
     private final List<T> list; 

     public Wrapper(List<T> list) { 
      this.list = list; 
     } 
    } 
} 

所以用這個appr oach我可以在我的控制器(public Iterable<Souvenir> souvenirs(@PathVariable("user") String user))中保留我現有的方法簽名,未來的控制器不必擔心將它的Iterables包裝在這樣的包裝中,因爲框架完成了這部分工作。

+0

很高興它幫助你。在這裏需要注意的一點是:出於安全原因,這很好,但是如果您將手動創建集合的包裝,並且例如提供集合的大小,這可能非常有用。 {size:10,souvenirs:[..]}手動創建包裝對象時還有其他可能性。 –

0

@RequestBody是你想要的。然後,您將使用字段「id」和「title」來創建一個POJO,並且它會自動將負載JSON解析到您的POJO。非常容易使用,您只需將它放入控制器的方法參數中,就如同使用@PathVariable一樣。

相關問題