在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);
}