在Spring MVC Controller中选择JsonView
问题内容:
我目前正在使用Jackson(2.4.0-rc3)和spring mvc(4.0.3)编写REST api,并且正在尝试使其安全。
通过这种方式,我尝试使用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);
}
}
问题答案:
我已经解决了扩展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);
}