Change PagedResources<T>'s name in Json representation - java

I am using spring boot to develop an small rest api that returns informations about an object named Chapter in HAL representation and using pagination.
A normal HAL representation would require the following controller
#RestController
#RequestMapping("/chapters")
public class ChapterController {
#Autowired ChapterRepository chapterRepo;
#RequestMapping(value="/slice", produces="application/hal+json")
public PagedResources<Chapter> slice(Pageable p, PagedResourcesAssembler assembler){
Page<Chapter> page = chapterRepo.findAll(p);
return assembler.toResource(page);
}
}
wich will return the following
{
"_embedded":{
"chapterList":[
//list of chapters here
]
},
"_links":{
"self":{
"href":"http://localhost:8090/chapters/slice?page=0&size=20"
}
},
"page":{
"size":20,
"totalElements":4,
"totalPages":1,
"number":0
}
}
But there is one thing i want to change which is the "chapterList" nomination.
Is there a way to do it

Related

Spring Hateoas issues rendering HAL content

I've been checking similar issues and haven't found any answer addressing what I am observing.
The issue is that I've easily managed to get Hypermedia in HAL format in my REST API when I retrieve 1 resource, but when I hit the controller methods retrieving a list of entities, then the hypermedia is NOT the same.
Here are the ouputs:
single resource returned
"_links": {
"self": {
"href": "http://localhost:8080/celsvs/api/books/123567891099"
},
"books": {
"href": "http://localhost:8080/celsvs/api/books"
}
}
List of resources
"links": [
{
"rel": "self",
"href": "http://localhost:8080/celsvs/api/books/123567891099"
},
{
"rel": "books",
"href": "http://localhost:8080/celsvs/api/books"
}
]
I started with Spring hateoas 0.25, but as I had to uplift anyway Spring boot and I saw that the Hateoas API had changed, I am now on Spring hateoas 1.0... And even after adapting my code to the new API I am still getting the same result.
I am using the RepresentationModelAssemblerSupport class to keep my controllers clean from code to generate hateoas content. So this is how it looks like:
#Component
public class BookModelAssembler extends RepresentationModelAssemblerSupport<BookDto, BookDto> {
public BookModelAssembler() {
super(BooksController.class, BookDto.class);
}
#Override
public BookDto toModel(BookDto entity) {
return entity.add(linkTo(methodOn(BooksController.class).getResource(entity.getIsbn())).withSelfRel())
.add(linkTo(methodOn(BooksController.class).getAllResources()).withRel("books"));
}
}
And in the Controller, the endpoints to retrieve one or all resources:
#Override
#GetMapping(value = {QueryConstants.BOOKS_ISBN_PATH, QueryConstants.BOOKS_SIG_PATH})
public BookDto getResource(#PathVariable("isbn") final String isbn) {
return this.modelAssembler.toModel(this.findOneResource(isbn));
}
// GET - retrieve all resources
#Override
#GetMapping (produces = { "application/hal+json" })
public List<BookDto> getAllResources() {
return (findAllResources().stream().map(this.modelAssembler::toModel).collect(Collectors.toList()));
}
As you can see, the Hypermedia rendered is different even when all the entities in the list returned have been mapped using the same method toModel() used in the method getResource().
The only way I've managed to see in the case of all resources the proper HAL format returned is when I've changed the implementation of the controller to return a collection Model:
//#GetMapping
public CollectionModel<BookDto> getAll() {
return this.modelAssembler.toCollectionModel(findAllResources());
}
but then all the entities are bundled inside an _embedded element, which is NOT what I want when I return the collection of entities.
Spring Hateoas documentation states that HAL is the default, so I have not thought about configuring anything for now...
So, I only see:
I am missing some configuration so that when I can get a collection of entities rendered (no under the _embedded element)... But I haven't seen anything suitable in HalConfiguration bean.
I am assuming that the proper way of returning the collection of the type of resources requested is NOT inside the _embedded property... but maybe I am wrong. So far my understanding was that when you request, say, resource Person and you want to return it's contacts, those being also directly reachable via their own endpoint, then you embed those in the content returned along with the rest of Person properties... I haven't found anything stating that collections are expected to be rendered inside the _embedded property.
Does anyone have any advice? I am running out of time and ideas and the team implementing the client side is waiting to consume the Hypermedia content.
Thanks!
The HAL specification says that the property _embedded is used to store an array of resource object.
Edit:
To answer Alberto's question in his own answer
Still, if someone can tell me why in the previous implementation the attached links did not follow the HAL format, I would appreciate. Thanks
Spring HATEOAS customizes JSON serialization of RepresentationModel which is the parent class of CollectionModel.
// org.springframework.hateoas.mediatype.hal.RepresentationModelMixin
public abstract class RepresentationModelMixin extends RepresentationModel<RepresentationModelMixin> {
#Override
#JsonProperty("_links")
#JsonInclude(Include.NON_EMPTY)
#JsonSerialize(using = Jackson2HalModule.HalLinkListSerializer.class)
#JsonDeserialize(using = Jackson2HalModule.HalLinkListDeserializer.class)
public abstract Links getLinks();
}
#JsonProperty("_links") defines the JSON property name to be _links. #JsonSerialize defines the serializer to be used. Look into method org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalLinkListSerializer#serialize for serialization logic.
After some more reading I've made my mind and I have concluded that the proper thing to send back in the response is a collection inside the _embedded element.
I've followed several references:
Section 6 of JSON HAL rfc draft (this is the latest version I've found), there is an example of a document returned as a result of a request of a list of resources, where the list of them is embedded in an array inside the _embedded element.
In a section dedicated to Collections by other writers, the interpretation is the same and the elements of a collection are returned inside an _embedded property.
So in line with this, if I change my controller to return CollectionModel, then I get a proper content formatted in HAL.
The code being:
#GetMapping
public CollectionModel<BookDto> getAll() {
return this.modelAssembler.toCollectionModel(findAllResources()).add(linkTo(methodOn(BooksController.class).getAll()).withSelfRel());
}
And the result being:
{
"_embedded": {
"bookDtoList": [
{
"isbn": "123567891099",
"signature": "AA-23-EEE",
"title": "Electromagnetismo",
"subtitle": "Introducción a las aplicaciones del electromagnetismo",
"authors": [
"Idoia Mendieta",
"Bonifacio Pérez"
],
"available": false,
"numOfCopies": 0,
"library": null,
"detailedInfo": null,
"_links": {
"self": {
"href": "http://localhost:8080/celsvs/api/books/123567891099"
},
"books": {
"href": "http://localhost:8080/celsvs/api/books"
}
}
},
{
"isbn": "123567891012",
"signature": "AA-23-EFD",
"title": "Electromagnetismo",
"subtitle": "Introducción a las aplicaciones del electromagnetismo",
"authors": [
"Idoia Mendieta",
"Bonifacio Pérez"
],
"available": false,
"numOfCopies": 0,
"library": null,
"detailedInfo": null,
"_links": {
"self": {
"href": "http://localhost:8080/celsvs/api/books/123567891012"
},
"books": {
"href": "http://localhost:8080/celsvs/api/books"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/celsvs/api/books"
}
}
}
Still, if someone can tell me why in the previous implementation the attached links did not follow the HAL format, I would appreciate. Thanks

Create API to pass integer value from spring boot to angular and View in web page

I'm developing an application which stores data in mongoDB and the data is passed to the angular frontend using spring rest api. In that process I need to pass an integer value to the frontend but integers can not be directly sent and as far as I know I have to use ResponseEntity but I my knowledge regarding that is low, so please help me.
After the answers I changed my api as below,
#RestController
#RequestMapping("/api")
public class Controller {
#Autowired
Repository repository;
private ArrayList<Member> arrayList = new ArrayList<>();
#GetMapping("/members")
public List<Member> getAll(){
return repository.findAll();
}
#GetMapping("/members/all")
public ResponseEntity<Integer> findCount(){
arrayList.addAll(repository.findAll());
return new ResponseEntity<>(arrayList.size(),HttpStatus.OK);
}
}
So after this api created how to get show it in the web page?
service.ts
export class HttpClientService {
constructor(
private httpClient: HttpClient
) { }
private userUrl= 'http://localhost:8080/api/members';
getMembers(){
return this.httpClient.get<DefaultMember[]>(this.userUrl);
}
getCount(){
return this.httpClient.get('http://localhost:8080/api/members/all');
}
}
dashboardcomponent.ts
export class DashboardComponent implements OnInit {
constructor(
private httpClientService: HttpClientService
) { }
ngOnInit(): void {}
count(){
this.httpClientService.getCount()
}
}
dashboard.html
<div class="col-xs-9 text-right">
<div class="huge">****members****</div>
<div class="phrase">Total Members</div>
</div>
I want to get the value inside div class huge. Please help me with this.
A common practice is to return an object with the field(s) that you need.
This way, if in the future you like to add more fields, it is relatively easy.
So something like:
#Getters / Setters /...
class MyResponse{
int count;
}
And also, if you are using Repository there should be count() method in there, so you don't need to findAll and count it.
If you want your API to pass a Http Status Code you either use the annotation
#ResonseStatus(HttpStatus.YOUR_STATUS_CODE)
Or use the response entity as shown below:
//With ResponseStatus
#GetMapping("/members/all")
#ResponseStatus(HttpStatus.OK)
public int findCount(){
arrayList.addAll(repository.findAll());
return arrayList.size();
}
// With ResponseEntity
#GetMapping("/members/all")
public ResponseEntity<Integer> findCount(){
arrayList.addAll(repository.findAll());
return new ResponseEntity<Integer>(arrayList.size());
}
Still, I would higly recommend you to create a specific Object to receive this number, I've made and example using a mock API and some Angular code, this way you can check it working in real time and see the code in action:
The app working is here on this link.
And the source code you can check here.
For references you can go here to see how the response is buid.

How to pass JSON String as Request Parameter to other service

I have two services running. One service will generate a JSON response. Below is the generated Json response.
{ "appliedCostMatrix": { "regionCostMatrix": "","costAreaCode": "", "costBoundaryId": 0, "isActive": "Y", "costRegionCode": "", "regionTypeCode": 3, "regionTypeDescription": "California", "solveTypeDescription": "Coaxial" }, "networkSolveRegion": "CALIFORNIA"}
Now To save the response i have to call other service and have to pass this Json as string.
http://localhost:8080/putSolvedRoutes?responseJson=(Above Mentioned JSON string)
Can any one tell me the best way to achieve this.
Thanks in Advance.
What have you tried? Your tags imply this is a Spring question so within the context of Spring boot, set up your controller (that receives the second request you send with the json data) as follows:
#RestController
public class YourRestController {
#RequestMapping(value="/your/endpoint", method=RequestMethod.POST)
public ResponseEntity<String> someMethod(#RequestBody YourDTO data) {
// business logic here
}
}
and send the request with whatever client you are using, in post.
If the client is also Spring, have a look at the documentation for one possible way to solve the problem. A brief example:
#Service
public class YourService {
private final RestTemplate rt;
public YourService(RestTemplateBuilder rtb) {
this.rt = rtb.build();
}
public YourDTO someRestCall(String param) {
return this.rt.postForObject("/your/endpoint", YourDTO.class, param);
}
}

HAL-style rendering issues with spring-hateoas

This time I'm really confused and stuck...
I'm working on a spring-boot (1.5.3) application exposing a REST API with HAL style using spring-hateoas library. Some of the endpoints already work correctly and return HAL-links. Currently I'm trying to change another endpoint to also return the response with HAL, but the result is serialized in the "collection+json" style.
What I found so far:
There are 3 ObjectMapper instances in the spring context:
objectMapper
_halObjectMapper
halObjectMapper
I converted the response with all three of these and found that the first two produce "collection+json" output ("links" with "rel") and the third one produces the expected HAL style ("_links" without "rel").
Why are there three and how can I inspect and control which of these objectMappers get used for rendering the response? How is it possible that my other endpoints return the expected format?
Here's my simplified code, in case it's of any interest:
#RestController
public class SearchController{
#RequestMapping(value = "/search", produces = { "application/hal+json" }, method = RequestMethod.GET)
public ResponseEntity<SearchResponse> search(...){
SearchResponse searchResponse = ...; // search & build response
return new ResponseEntity<>(searchResponse, HttpStatus.OK);
}
}
public class SearchResponse {
#JsonProperty("_meta")
private PageSortInfo meta;
#JsonUnwrapped
private Resources<Resource<SearchResultItem>> resultlist;
...// getters & setters
}
The response is supposed to look like this:
{
"_meta" : {
...
},
"_embedded" : {
"searchResultItemList" : [ {
...,
"_links" : {
"self" : {
"href" : "..."
}
}
},
...
]
},
"_links" : {
"self" : {
"href" : "http://localhost:8882/api/search"
}
}
}
Well, after another day of digging through google search results, sprint-boot code and a lot of trial & error I finally found the answer, which is of course trivial:
Spring will choose the MessageConverter used to convert the response object to the appropriate format by matching the registered converters to the type of the object (in my case SearchResponse) and the response type ("application/hal+json" for me). The halJacksonHttpMessageConverter will only be chosen if the response object is of type ResourceSupport. SO all I had to do is make my SearchResponse extend ResourceSupport and I got the expected response format.
public class SearchResponse extends ResourceSupport {...}

How to mix Spring Data Repositories and Spring Rest Controllers

Currently I am exposing a few Spring Data Repositories as RESTful services by annotating them with #RepositoryRestResource like this:
#RepositoryRestResource(collectionResourceRel = "thing1", path = "thing1")
public interface Thing1Repository extends PagingAndSortingRepository<Thing1, String> {}
#RepositoryRestResource(collectionResourceRel = "thing2", path = "thing2")
public interface Thing2Repository extends CrudRepository<Thing2, String> {}
This all works great. When you hit my first endpoint is also shows all the Spring Data Repositories I have exposed, like this:
{
_links: {
thing1: {
href: "http://localhost:8080/thing1{?page,size,sort}",
templated: true
},
thing2: {
href: "http://localhost:8080/thing2"
}
}
}
Now I have some endpoints I want to expose that cannot be represented by Spring Data Repositories, so I am using a RestController.
Here is a simple example:
#RestController
#ExposesResourceFor(Thing3.class)
#RequestMapping("/thing3")
public class Thing3Controller {
#Autowired
EntityLinks entityLinks;
#Autowired
Thing3DAO thing3DAO;
//just assume Thing3.class extends ResourceSupport. I know this is wrong, but it makes the example shorter
#RequestMapping(value = "/{id}", produces = "application/json")
Thing3 thing3(#PathVariable("id") String id)
{
Thing3 thing3 = thing3DAO.findOne(id);
Link link = entityLinks.linkToSingleResource(Thing3.class, id);
thing3.add(link);
return thing3;
}
}
Now if I run this app and go to:
http://localhost:8080/thing3/{id}
I do get a JSON representation of the Thing3 with a link to itself, that works as expected.
What I want to figure out how to do is have the first endpoint also describe this controller. I basically want this:
{
_links: {
thing1: {
href: "http://localhost:8080/thing1{?page,size,sort}",
templated: true
},
thing2: {
href: "http://localhost:8080/thing2"
},
thing3: {
href: "http://localhost:8080/thing3"
}
}
}
What do I need to do to get my base endpoint to have a link to this controller?
You could override RepositoryLinkResource, and add a resource pointing to your thing3:
resource.add(ControllerLinkBuilder.linkTo(Thing3Controller.class).withRel("thing3"));
Check this question: Custom response for root request int the Spring REST HATEOAS with both RepositoryRestResource-s and regular controllers

Categories

Resources