2014-05-14 110 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)); 
    } 

}