I\'m writing a RESTful web service using Spring/JPA. There\'s a JPA model which is exposed through the web service. The \'Course\' model is quite spacious - it actually is c
In Spring Data REST 2.1 there is a new mechanism for this purpose - Projections (It's now part of spring-data-commons).
You'll need to define interface, containing exactly exposed fields:
@Projection(name = "summary", types = Course.class)
interface CourseGeneralInfo {
GeneralInfo getInfo();
}
After that Spring will be able to find it automagically in your source, and you could make requests to your existing endpoints, like this:
GET /api/courses?projection=general_info
Based on https://spring.io/blog/2014/05/21/what-s-new-in-spring-data-dijkstra
Spring sample project with projections: https://github.com/spring-projects/spring-data-examples/tree/master/rest/projections
I was just reading about some really nifty features in Spring 4.1, which allow you to use different views via annotations.
from: https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring
public class View {
interface Summary {}
}
public class User {
@JsonView(View.Summary.class)
private Long id;
@JsonView(View.Summary.class)
private String firstname;
@JsonView(View.Summary.class)
private String lastname;
private String email;
private String address;
private String postalCode;
private String city;
private String country;
}
public class Message {
@JsonView(View.Summary.class)
private Long id;
@JsonView(View.Summary.class)
private LocalDate created;
@JsonView(View.Summary.class)
private String title;
@JsonView(View.Summary.class)
private User author;
private List<User> recipients;
private String body;
}
Thanks to Spring MVC @JsonView
support, it is possible to choose, on a per handler method basis, which field should be serialized:
@RestController
public class MessageController {
@Autowired
private MessageService messageService;
@JsonView(View.Summary.class)
@RequestMapping("/")
public List<Message> getAllMessages() {
return messageService.getAll();
}
@RequestMapping("/{id}")
public Message getMessage(@PathVariable Long id) {
return messageService.get(id);
}
}
In this example, if all messages are retrieved, only the most important fields are serialized thanks to the getAllMessages()
method annotated with @JsonView(View.Summary.class)
:
[ {
"id" : 1,
"created" : "2014-11-14",
"title" : "Info",
"author" : {
"id" : 1,
"firstname" : "Brian",
"lastname" : "Clozel"
}
}, {
"id" : 2,
"created" : "2014-11-14",
"title" : "Warning",
"author" : {
"id" : 2,
"firstname" : "Stéphane",
"lastname" : "Nicoll"
}
}, {
"id" : 3,
"created" : "2014-11-14",
"title" : "Alert",
"author" : {
"id" : 3,
"firstname" : "Rossen",
"lastname" : "Stoyanchev"
}
} ]
In Spring MVC default configuration, MapperFeature.DEFAULT_VIEW_INCLUSION
is set to false. That means that when enabling a JSON View, non annotated fields or properties like body or recipients are not serialized.
When a specific Message is retrieved using the getMessage()
handler method (no JSON View specified), all fields are serialized as expected:
{
"id" : 1,
"created" : "2014-11-14",
"title" : "Info",
"body" : "This is an information message",
"author" : {
"id" : 1,
"firstname" : "Brian",
"lastname" : "Clozel",
"email" : "bclozel@pivotal.io",
"address" : "1 Jaures street",
"postalCode" : "69003",
"city" : "Lyon",
"country" : "France"
},
"recipients" : [ {
"id" : 2,
"firstname" : "Stéphane",
"lastname" : "Nicoll",
"email" : "snicoll@pivotal.io",
"address" : "42 Obama street",
"postalCode" : "1000",
"city" : "Brussel",
"country" : "Belgium"
}, {
"id" : 3,
"firstname" : "Rossen",
"lastname" : "Stoyanchev",
"email" : "rstoyanchev@pivotal.io",
"address" : "3 Warren street",
"postalCode" : "10011",
"city" : "New York",
"country" : "USA"
} ]
}
Only one class or interface can be specified with the @JsonView
annotation, but you can use inheritance to represent JSON View hierarchies (if a field is part of a JSON View, it will be also part of parent view). For example, this handler method will serialize fields annotated with @JsonView(View.Summary.class)
and @JsonView(View.SummaryWithRecipients.class)
:
public class View {
interface Summary {}
interface SummaryWithRecipients extends Summary {}
}
public class Message {
@JsonView(View.Summary.class)
private Long id;
@JsonView(View.Summary.class)
private LocalDate created;
@JsonView(View.Summary.class)
private String title;
@JsonView(View.Summary.class)
private User author;
@JsonView(View.SummaryWithRecipients.class)
private List<User> recipients;
private String body;
}
@RestController
public class MessageController {
@Autowired
private MessageService messageService;
@JsonView(View.SummaryWithRecipients.class)
@RequestMapping("/with-recipients")
public List<Message> getAllMessagesWithRecipients() {
return messageService.getAll();
}
}