2014-05-14 137 views
3

我目前正在使用Jackson(2.4.0-rc3)和spring mvc(4.0.3)編寫REST api,並且試圖使其安全。在Spring MVC控制器中選擇JsonView

這樣,我嘗試使用JsonView來選擇可以序列化的對象的部分。

我發現瞭解決方案(這不適合我)用我想要的視圖註釋我的Controller方法。但是我想要在飛行中選擇控制器內的視圖。

是否可以擴展ResponseEntity類來指定我想要的JsonView?

一小塊代碼:

這裏是賬戶類

public class Account { 

    @JsonProperty(value = "account_id") 
    private Long accountId; 

    @JsonProperty(value = "mail_address") 
    private String mailAddress; 

    @JsonProperty(value = "password") 
    private String password; 

    @JsonProperty(value = "insert_event") 
    private Date insertEvent; 

    @JsonProperty(value = "update_event") 
    private Date updateEvent; 

    @JsonProperty(value = "delete_event") 
    private Date deleteEvent; 

    @JsonView(value = PublicView.class) 
    public Long getAccountId() { 
     return accountId; 
    } 

    @JsonView(value = PublicView.class) 
    public void setAccountId(Long accountId) { 
     this.accountId = accountId; 
    } 

    @JsonView(value = OwnerView.class) 
    public String getMailAddress() { 
     return mailAddress; 
    } 

    @JsonView(value = OwnerView.class) 
    public void setMailAddress(String mailAddress) { 
     this.mailAddress = mailAddress; 
    } 

    @JsonIgnore 
    public String getPassword() { 
     return password; 
    } 

    @JsonView(value = OwnerView.class) 
    public void setPassword(String password) { 
     this.password = password; 
    } 

    @JsonView(value = AdminView.class) 
    public Date getInsertEvent() { 
     return insertEvent; 
    } 

    @JsonView(value = AdminView.class) 
    public void setInsertEvent(Date insertEvent) { 
     this.insertEvent = insertEvent; 
    } 

    @JsonView(value = AdminView.class) 
    public Date getUpdateEvent() { 
     return updateEvent; 
    } 

    @JsonView(value = AdminView.class) 
    public void setUpdateEvent(Date updateEvent) { 
     this.updateEvent = updateEvent; 
    } 

    @JsonView(value = AdminView.class) 
    public Date getDeleteEvent() { 
     return deleteEvent; 
    } 

    @JsonView(value = OwnerView.class) 
    public void setDeleteEvent(Date deleteEvent) { 
     this.deleteEvent = deleteEvent; 
    } 

    @JsonProperty(value = "name") 
    public abstract String getName(); 

} 

這裏是帳戶控制

@RestController 
@RequestMapping("/account") 
public class AccountCtrlImpl implements AccountCtrl { 

    @Autowired 
    private AccountSrv accountSrv; 

    public AccountSrv getAccountSrv() { 
     return accountSrv; 
    } 

    public void setAccountSrv(AccountSrv accountSrv) { 
     this.accountSrv = accountSrv; 
    } 

    @Override 
    @RequestMapping(value = "/get_by_id/{accountId}", method = RequestMethod.GET, headers = "Accept=application/json") 
    public ResponseEntity<Account> getById(@PathVariable(value = "accountId") Long accountId) { 
     try { 
      return new ResponseEntity<Account>(this.getAccountSrv().getById(accountId), HttpStatus.OK); 
     } catch (ServiceException e) { 
      return new ResponseEntity<Account>(HttpStatus.INTERNAL_SERVER_ERROR); 
     } 
    } 

    @Override 
    @RequestMapping(value = "/get_by_mail_address/{mail_address}", method = RequestMethod.GET, headers = "Accept=application/json") 
    public ResponseEntity<Account> getByMailAddress(@PathVariable(value = "mail_address") String mailAddress) { 
     try { 
      return new ResponseEntity<Account>(this.getAccountSrv().getByMailAddress(mailAddress), HttpStatus.OK); 
     } catch (ServiceException e) { 
      return new ResponseEntity<Account>(HttpStatus.INTERNAL_SERVER_ERROR); 
     } 
    } 

    @Override 
    @RequestMapping(value = "/authenticate/{mail_address}/{password}", method = RequestMethod.GET, headers = "Accept=application/json") 
    public ResponseEntity<Account> authenticate(@PathVariable(value = "mail_address") String mailAddress, @PathVariable(value = "password") String password) { 
     return new ResponseEntity<Account>(HttpStatus.NOT_IMPLEMENTED); 
    } 
} 
+0

也許http://stackoverflow.com/questions/5772304/using-jsonview-with-spring-mvc幫助 – azarai

回答

5

我已經解決了我的問題,延長ResponseEntity這樣的:

public class ResponseViewEntity<T> extends ResponseEntity<ContainerViewEntity<T>> { 

    private Class<? extends BaseView> view; 

    public ResponseViewEntity(HttpStatus statusCode) { 
     super(statusCode); 
    } 

    public ResponseViewEntity(T body, HttpStatus statusCode) { 
     super(new ContainerViewEntity<T>(body, BaseView.class), statusCode); 
    } 

    public ResponseViewEntity(T body, Class<? extends BaseView> view, HttpStatus statusCode) { 
     super(new ContainerViewEntity<T>(body, view), statusCode); 
    } 

} 

和ContainerViewEntity封裝對象和所選擇的視

public class ContainerViewEntity<T> { 

    private final T object; 
    private final Class<? extends BaseView> view; 

    public ContainerViewEntity(T object, Class<? extends BaseView> view) { 
     this.object = object; 
     this.view = view; 
    } 

    public T getObject() { 
     return object; 
    } 

    public Class<? extends BaseView> getView() { 
     return view; 
    } 

    public boolean hasView() { 
     return this.getView() != null; 
    } 
} 

在那之後,我們只轉換對象與良好的視野。

public class JsonViewMessageConverter extends MappingJackson2HttpMessageConverter { 

    @Override 
    protected void writeInternal(Object object, HttpOutputMessage outputMessage) 
      throws IOException, HttpMessageNotWritableException { 
     if (object instanceof ContainerViewEntity && ((ContainerViewEntity) object).hasView()) { 
      writeView((ContainerViewEntity) object, outputMessage); 
     } else { 
      super.writeInternal(object, outputMessage); 
     } 
    } 

    protected void writeView(ContainerViewEntity view, HttpOutputMessage outputMessage) 
      throws IOException, HttpMessageNotWritableException { 
     JsonEncoding encoding = this.getJsonEncoding(outputMessage.getHeaders().getContentType()); 
     ObjectWriter writer = this.getWriterForView(view.getView()); 
     JsonGenerator jsonGenerator = writer.getFactory().createGenerator(outputMessage.getBody(), encoding); 
     try { 
      writer.writeValue(jsonGenerator, view.getObject()); 
     } catch (IOException ex) { 
      throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex); 
     } 
    } 

    private ObjectWriter getWriterForView(Class<?> view) { 
     ObjectMapper mapper = new ObjectMapper(); 
     mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false); 
     return mapper.writer().withView(view); 
    } 

} 

而結束,我使轉換器

<mvc:annotation-driven> 
    <mvc:message-converters> 
     <bean class="wc.handler.view.JsonViewMessageConverter"/> 
    </mvc:message-converters> 
</mvc:annotation-driven> 

就是這樣,我可以在控制器

@Override 
@RequestMapping(value = "/get_by_id/{accountId}", method = RequestMethod.GET, headers = "Accept=application/json") 
public ResponseViewEntity<Account> getById(@PathVariable(value = "accountId") Long accountId) throws ServiceException { 
    return new ResponseViewEntity<Account>(this.getAccountSrv().getById(accountId), PublicView.class, HttpStatus.OK); 
} 
3

FYI選擇查看,春季4.1已經在使用@JsonView支持直接在@ResponseBody和ResponseEntity上:

Jackson的@JsonView直接支持@ResponseBody和ResponseEntity控制器方法,用於序列化相同POJO的不同數量的細節(例如,總結與詳細信息頁面)。通過將序列化視圖類型添加爲特殊鍵下的模型屬性,也支持基於視圖的渲染。

而且在http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-jsonview,你可以找到更簡單的解決方案:

@RestController 
public class UserController { 

    @RequestMapping(value = "/user", method = RequestMethod.GET) 
    @JsonView(User.WithoutPasswordView.class) 
    public User getUser() { 
     return new User("eric", "7!jd#h23"); 
    } 
} 

public class User { 

    public interface WithoutPasswordView {}; 
    public interface WithPasswordView extends WithoutPasswordView {}; 

    private String username; 
    private String password; 

    public User() { 
    } 

    public User(String username, String password) { 
     this.username = username; 
     this.password = password; 
    } 

    @JsonView(WithoutPasswordView.class) 
    public String getUsername() { 
     return this.username; 
    } 

    @JsonView(WithPasswordView.class) 
    public String getPassword() { 
     return this.password; 
    } 
} 
5

我真的很喜歡這個解決方案提出here動態地選擇您的控制方法中一個JSON視圖。

基本上,你返回一個MappingJacksonValue,你用你想返回的值構造。之後,您可以撥打setSerializationView(viewClass)以獲得適當的視角。在我的使用情況下,我回到根據當前的用戶有不同的看法,這樣的事情:

@RequestMapping("/foos") 
public MappingJacksonValue getFoo(@AuthenticationPrincipal UserDetails userDetails) { 
    MappingJacksonValue value = new MappingJacksonValue(fooService.getAll()); 
    if(userDetails.isAdminUser()) { 
    value.setSerializationView(Views.AdminView.class); 
    } else { 
    value.setSerializationView(Views.UserView.class); 
    } 
    return value; 
} 

BTW:如果您使用Spring啓動,你可以控制,如果不具有關聯視圖屬性是序列化或不通過在application.properties設置這樣的:

spring.jackson.mapper.default_view_inclusion=true 
0

這個偉大的工程:

@RequestMapping(value = "/{id}", method = RequestMethod.GET) 
public void getZone(@PathVariable long id, @RequestParam(name = "tree", required = false) boolean withChildren, HttpServletResponse response) throws IOException { 
    LOGGER.debug("Get a specific zone with id {}", id); 

    Zone zone = zoneService.findById(id); 

    response.setContentType(MediaType.APPLICATION_JSON_VALUE); 
    if (withChildren) { 
     response.getWriter().append(mapper.writeValueAsString(zone)); 
    } else { 
     response.getWriter().append(mapper.writerWithView(View.ZoneWithoutChildren.class).writeValueAsString(zone)); 
    } 

}