I'm trying to use Spring-data-rest with spring-data-mongodb to expose read-only resources.
The problem I met, is that I want to have different views of my documents. Let's say I have some private information in a document, I don't want to expose them publicly.
So I tried several ways. I read this post https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring describing how to use JsonView in order to select the fields we want to expose.
I've tried like this :
@RepositoryRestResource(collectionResourceRel = "recommandation", path = "recommandations")
interface RecommandationRepository extends MongoRepository<Recommendation, ObjectId> {
@Override
@JsonView(View.Public.class)
Iterable<Recommendation> findAll(Iterable<ObjectId> objectIds);
... // other find methods
}
It doesn't works. It is however said in the comments : https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring#comment-1725671983 The answer suggests to use @Projections However @Projections result in url like that : "…/recommandations{?projection}" It means that the projection is just an option, so the full object is still exposed.
There is another method described here https://github.com/spring-projects/spring-data-rest/wiki/Configuring-the-REST-URL-path It suggests to use @RestResource(exported = false) annotation for the fields we don't want to expose.
But it's not flexible. If I want to expose a public read-only API and a private full access API. This annotation can't be disabled per api.
Is there another suggestion ?
The important point is that Spring Data REST uses Jackson serialization parameters based on the domain object, not the repository definition. One simple way to hide a particular field from appearing in the JSON is like this:
@Entity
public class User {
@Id @GeneratedValue
private Long id;
private String name;
@JsonIgnore
private String password;
...
In this example, my User object will NEVER export a password field no matter how this entity is used. Jackson supports either putting this on the field, or putting on the corresponding getter method.
When you put @JsonIgnore in the domain model, it makes it the default definition. Projections are options to alter what fields get rendered. Look at the following example:
@Projection(name = "noImages", types = {Item.class})
public interface NoImages {
public Link getHtmlUrl();
}
This project can only be used when rendering Item domain objects. It isn't the default view, but instead an option to use via ?projection=noImages. But don't forget: when it comes time to apply Jackson serialization, the project will override the domain model's settings. This means you can write a projection for that User object up above and have it include String getPassword(). This would override the domain model's default setting an in turn export a password. Responsibility is yours.
One last thing. Spring Data REST supports Excerpt Projections. The most common use case is where you have a Customer object related to an Address object. By default, the relationship to see a customer's address would show a URI to navigate. But if you are wanting the address information all the time, you can avoid this extra GET operation by creating a projection that renders the address details. Then you can configure that for Customer objects, turn on this projection by default and essentially inline the address details whenever you fetch a customer record.
I realize the reference docs aren't quite up to date on all these details. You can track our progress to update the docs suitably as follows:
There is also a bug in that the ALPS metadata from Spring Data REST also needs to filter out domain fields tagged with @JsonIgnore (see https://jira.spring.io/browse/DATAREST-463)
P.S. @RestResource is deprecated and not the recommended approach to setting what fields get exported. Instead, use @JsonIgnore as shown earlier.
Per @johannes-rudolph suggestion...
Consider applying this setting to the field (or property if you're mapping from accessors):
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String password;
This does what the access value implies: marking the associated field as write-only. Thus the value can be set but not retrieved via the Jackson/JSON serialized form.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With