Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exclude some fields of Spring-data-rest resource

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 ?

like image 819
Hugo Lassiège Avatar asked Feb 04 '15 13:02

Hugo Lassiège


2 Answers

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:

  • https://jira.spring.io/browse/DATAREST-449
  • https://jira.spring.io/browse/DATAREST-450
  • https://jira.spring.io/browse/DATAREST-451
  • https://jira.spring.io/browse/DATAREST-452
  • https://jira.spring.io/browse/DATAREST-453
  • https://jira.spring.io/browse/DATAREST-454

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.

like image 147
gregturn Avatar answered Nov 08 '22 15:11

gregturn


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.

like image 29
sofend Avatar answered Nov 08 '22 13:11

sofend