I am using Spring Boot and Spring HATEOAS to build a REST API.
I have 2 simple objects. Let's say:
// Model
#Entity
public class Person {
private String name;
private Address address;
// ... usual methods omitted for brievity
}
#Entity
public class Address {
private String street;
private String city;
// ...
}
// Repository. It exposes directly a REST service
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {}
// Application entry point
#ComponentScan
#EnableAutoConfiguration
#EnableJpaRepositories
#Import(RepositoryRestMvcConfiguration.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
This simple project creates output like the following:
{
"_links": {
"self": {
"href": "http://localhost:8080/persons{?page,size,sort}",
"templated": true
}
},
"_embedded": {
"persons": [
{
"name": "Some name",
"_links": {
"self": {
"href": "http://localhost:8080/persons/1"
},
"address": {
"href": "http://localhost:8080/persons/1/address"
}
}
}
]
}
}
Fine, but I would like the application to send the Address object directly in the response. In order not to have to query the URL of the address.
Something like:
...
"persons": [
{
"name": "Some name",
"address": {
"street": "Some street name"
"city": "Some city name"
}
"_links": {
"self": {
"href": "http://localhost:8080/persons/1"
},
"address": {
"href": "http://localhost:8080/persons/1/address"
}
}
}
]
...
Are there any configuration to do that? I could not find any configuration about it in Spring HATEOAS docs. And this is the default behavior when using only regular Spring controllers.
The latest release of Spring Data REST's docs illustrate excerpt projections. This provides an alternative default view of a collection of data.
Your use case is the exact angle it was originally designed for.
Delete AddressRepository interface, and object Address will be embedded in json in Person class. But it will not be possible to get Address by ID
Related
I am using Java annotations to build our swagger documentation. In one case, we want to provide output in either JSON or csv depending on the "Accepts" header. I created the rest interface like this:
#RestController
#EnableAutoConfiguration
#RequestMapping(path="/api/v2/swaggerTest")
#Api
public class SwaggerDocResource {
private static class ItemDto {
String name;
Integer age;
}
#ApiOperation(value = "Get the requested items in json")
#GetMapping(produces = "application/json")
public ResponseEntity<ItemDto> getItems() {
return null;
}
#ApiOperation(value = "Get the requested items in csv")
#GetMapping(produces = "text/csv")
public ResponseEntity<String> exportItems() {
return null;
}
}
If I comment out the csv method, the generated Swagger doc generates a schema for my DTO class and references it:
...
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ItemDto"
}
}
}
},
...
However, if I do include the csv method, the schema for my DTO class is no longer generated and both types of response are given the same schema:
...
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"type": "string"
}
},
"text/csv": {
"schema": {
"type": "string"
}
}
}
},
...
Is it possible to assign a different schema to these different content types? I have been unable to figure out how.
I would like to deal with JSON that can be either:
{
"data": {
"id": "1",
"type": "permissions",
"attributes": { "permission": "VIEW" }
"relationships": {
"user": { "data": { "id": "U1", "type": "users" } }
"resource": { "data": { "id": "G1", "type": "groups" } }
}
}
}
OR
{
"data": {
"id": "1",
"type": "permissions",
"attributes": { "permission": "VIEW" }
"relationships": {
"user": { "data": { "id": "U1", "type": "users" } }
"resource": { "data": { "id": "P1", "type": "pages" } }
}
}
}
That is, I would like the "resource" relationship type to be entirely customizable ("groups" or "pages" or anything else).
Is there a way to do that with Katharsis? I was hoping for some kind of inheritance...
#JsonApiResource(type = "permissions")
public class Permission {
...
#JsonApiToOne
private SharedResource resource;
...
}
public interface SharedResource {
...
}
#JsonApiResource(type = "pages")
public class Page implements SharedResource {
...
}
But that doesn't work completely. I've finagled it enough where a findAll returns pretty well (though the links don't reflect the type "pages"), but any POST with the relationships set returns a 405 Method Not Allowed.
Not sure it's possible, but I'd really like it to be since I like Katharsis.
I think what you're referring to is polymorphic entity types. If so, this has been asked for repeatedly but currently doesn't exist.
I use MongoRepository in Spring boot data rest and it is working just fine without implementing my own controller. But I want to put "Register Date" in my newly created objects and default implementation is not supporting that. I need to implement my own custom controller to put extra fields in every new objects. The problem is that HATEOAS stop working when I implement my own controller.
Repository class:
#RepositoryRestResource(collectionResourceRel = "users", path = "users")
public interface UserRepository extends MongoRepository<User, String> {
}
Controller class:
#RestController
#RequestMapping("/users")
public class UserController {
#Autowired
UserRepository repository;
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<List<User>> getAll() {
List<User> list = repository.findAll();
return new ResponseEntity<>(list, HttpStatus.OK);
}
Payload with this custom controller looks like this:
[
{
"id": "571de80ebdabf25dd6cdfb73",
"username": "mark",
"password": "mark123",
"email": "mark#gmail.com",
"createdAt": "2016-04-25 11:49"
},
{
...
Payload without my custom controller looks like this:
{
"_embedded": {
"users": [
{
"username": "mark",
"password": "mark123",
"email": "mark#gmail.com",
"createdAt": "2016-04-25 11:49",
"_links": {
"self": {
"href": "http://localhost:8080/users/571de80ebdabf25dd6cdfb73"
},
"user": {
"href": "http://localhost:8080/users/571de80ebdabf25dd6cdfb73"
}
}
},
{
.....
I tried to use #RepositoryRestController instead of #RestController but it didn't help. I wonder if there is another way to put "register date" in newly created objects without implementing own custom controller? If not, what can I do HATEOAS to work again?
I solved my problem thanks to comments which gave me a perspective :)
1 - Extended User class with ResourceSupport. (Note: Do not use just id for userId because ResourceSupport needs getId() method.)
public class User extends ResourceSupport {
#Id
private String userId;
2 - Updated my controller class as followed
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo;
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<List<User>> getAll() {
List<User> list = repository.findAll();
for(User user : list) {
Link self = linkTo(UserController.class).slash(user.getUserId()).withSelfRel();
Link users = linkTo(UserController.class).slash(user.getId()).withRel("users");
user.add(self);
user.add(users);
}
return new ResponseEntity<>(list, HttpStatus.OK);
}
Now, my payloads look like this:
[
{
"userId": "571e44ecbdab7b1ffc668f02",
"username": "newton",
"password": "gravity",
"email": "nevton#gmail.com",
"createdAt": "2016-04-25 18:
"links": [
{
"rel": "self",
"href": "http://localhost:8080/users/571e44ecbdab7b1ffc668f02"
},
{
"rel": "users",
"href": "http://localhost:8080/users"
}
]
},
{
....
I'm trying to marshal a recursive bean to JSON with MOXy in Jersey, following the specification of RFC6901 aka JSON Pointer.
E.g., I'd like to marshal this:
public class Bean {
public Integer id;
public String name;
public Bean other;
public List<Bean> next;
}
x = new Bean(123, "X");
a = new Bean(456, "A");
x.other = a;
x.next.add(x);
x.next.add(a);
into this:
{
"id": 123,
"name": "X",
"a": { "id": 456, "name": "A", "next": [ ] },
"next": [
{ "$ref": "#" },
{ "$ref": "#/a" }
]
}
and then unmarshal this JSON to the original bean. Does someone have any suggestion/solution to this problem?
Lets say I have the following Hibernate Entities (fields ommitted)
#Entity
#DiscriminatorColumn(name = "T")
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class SuperClass {
}
#Entity
#DiscriminatorValue(name = "subClassA")
public SubClassA extends SuperClass {
}
#Entity
#DiscriminatorValue(name = "subClassB")
public SubClassB extends SuperClass {
}
With Spring Data REST I would get the following JSON representation:
{
"_links": {
},
"_embedded": {
"subclassA": [
{
"field1": "",
"field2": ""
}
],
"subclassB": [
{
"field1": "",
"field2": "",
"field3": ""
}
]
}
}
Again ommiting the _links attributes. Is there some sort of configuration I can use so the Serializer can ignore the subclasses and do a representation like this:
{
"_links": {
},
"_embedded": {
"superClass": [
{
"field1": "",
"field2": ""
},
{
"field1": "",
"field2": "",
"field3": ""
}
]
}
}
One way to solve the problem would be the implementation of a RelProvider. All you need to do is to implement it and add it to spring container (this could be done but i.e. annotating the implementation with #Component).
Considering that you would be able to get the response you are expecting simply by adding the following implementation (considering it will be scanned by spring):
#Component
public class MessageRelProvider implements RelProvider {
public boolean supports(Class<?> arg0) {
return SuperClass.class.isAssignableFrom(arg0);
}
public String getItemResourceRelFor(Class<?> type) {
return "superClass";
}
public String getCollectionResourceRelFor(Class<?> type) {
return "superClasses";
}
}