This question already has an answer here:
Spring Data ReST ref link omission when null or empty
(1 answer)
Closed 5 years ago.
I developed a Spring Boot, Spring Data REST, Hibernate application that exposes REST endpoints.
A typical response is something like this:
{
"sid": "f6dddaaa-2713-4b92-844b-6f0d3cefad3f",
"createdBy": "admin",
"createdDate": "2018-01-30T15:56:38.417Z",
"lastModifiedDate": "2018-01-30T15:57:53.963Z",
"lastModifiedBy": "admin",
"status": "Annullato",
"number": "51",
"dailyCode": "VS",
"entryDate": "2018-01-30T15:56:00Z",
"exitDate": "2018-01-31T15:56:00Z",
"totalDays": 1,
"standard": true,
"minibus": false,
"schoolTrip": false,
"price": 400,
"fareRow": "Standard",
"fareColumn": "Euro 0 3",
"extSyncCode": null,
"payments": [],
"passengers": 44,
"agency": null,
"paperBlock": null,
"paperReceipt": null,
"payer": null,
"checkedMedia": false,
"checkedLicensePlate": false,
"_links": {
"self": {
"href": "http://localhost:8080/api/v1/tickets/1"
},
"ticket": {
"href": "http://localhost:8080/api/v1/tickets/1{?projection}",
"templated": true
},
"area": {
"href": "http://localhost:8080/api/v1/tickets/1/area"
},
"fareException": {
"href": "http://localhost:8080/api/v1/tickets/1/fareException"
},
"block": {
"href": "http://localhost:8080/api/v1/tickets/1/block"
},
"customer": {
"href": "http://localhost:8080/api/v1/tickets/1/customer"
},
"transitCertificate": {
"href": "http://localhost:8080/api/v1/tickets/1/transitCertificate"
},
"passengersCountry": {
"href": "http://localhost:8080/api/v1/tickets/1/passengersCountry"
},
"refund": {
"href": "http://localhost:8080/api/v1/tickets/1/refund"
},
"fine": {
"href": "http://localhost:8080/api/v1/tickets/1/fine"
},
"hotel": {
"href": "http://localhost:8080/api/v1/tickets/1/hotel"
},
"workShift": {
"href": "http://localhost:8080/api/v1/tickets/1/workShift"
}
}
}
As you can see this entity has a lot of links. These links represent bound entities. Unfortunately some of these entities are optional.
I created a Angular 5 application that consumes my server side API. When I want to display data (let's say the entity shown in the example) I need to get related entities and I've to visit all links. Because not all related entities are mandatories, some of these links return HTTP 404 and the browser display these calls as errors (see the picture).
Is my approach right? Should I visit all these links and consider the 404 response perfectly fine (I think so) even if the browser consided that as an error? Is there a better approach otherwise?
That’s a lot of work to discover a related entity doesn’t exist. HTTP requests are slow. Admittedly they will run in parallel but it still seems inefficient.
Instead, when you load the parent entity, can you check at that time which children entities will be valid? If the child will not be valid then, instead of a link, return null. Or don’t return the value at all.
Related
A hypermedia response that needs to be consumed by one of my services looks like this:
{
"_embedded": {
"content": [
{
"createdBy": "...",
"createdDate": "2020-03-07T14:21:27.507Z",
"id": "...",
"name": "item1",
"_links": {
"self": {
"href": ".."
}
}
}
]
},
"_links": {
"self": {
"href": "..."
},
},
"pageNumber": 1,
"totalItems": 20,
"pageSize": 10
}
See how the paging related info is not what's expected by Spring Hateoas PagedModel which should have a single "page" property instead of individual ones for pageNumber, totalItems and pageSize:
"page": {
"size": 2,
"totalElements": 1000,
"totalPages": 500,
"number": 5
}
What I did in the end was to extend CollectionModel by adding those individual properties. This does work correctly deserialising a response shown above. But, all the CollectionModel constructors are now deprecated and the alternative is to use "CollectionModel.of", which however returns just CollectionModel.
What's the right way of customising paging information in using Spring Hateoas?
Many thanks!
I am trying to save this very simple fhir patient bundle against https://vonk.fire.ly/Bundle, by doing a PUT using Postman, however I am not able to get it working. When I simply copy the inner Patient resource data and do a PUT directly to the https://vonk.fire.ly/Patient endpoint it works just fine (for example - I just did it to this url https://vonk.fire.ly/Patient/deb7338181).
Can someone please please point me in the direction of what exactly it is going wrong here in this bundle??
{
"resourceType": "Bundle",
"id": "b6ec685a-26a2-4bb3-814b-841fba6a6edb",
"meta": {
"lastUpdated": "2018-05-29T23:45:32Z"
}
"type": "transaction",
"entry": [
{
"resource": {
"resourceType": "Patient",
"id": "deb73381811",
"text": {
"status": "generated",
"div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">Some narrative</div>"
},
"active": true,
"name": [
{
"use": "official",
"family": "Chalmers1",
"given": [
"Peter1",
"James1"
]
}
],
"gender": "male",
"birthDate": "1974-12-25"
},
"request": {
"method": "POST",
"url": "Patient"
}
}
]
}
If you want to send a transaction to a FHIR server, you do a POST of the transaction Bundle to the endpoint, just like you mention in your comment. Within the transaction, for each entry you have to set the request part to the kind of request you want.
For your Patient entry you have asked the server to do a POST, which means a create with a server assigned ID. If you want the server to use your own ID, you should instruct it to perform a PUT, which is usually an update, but can also be used for create with your own ID.
The syntax for the update request is:
"request": {
"method": "PUT",
"url": "Patient/<my_patient_id>"
}
Please note that although it is a valid FHIR request and Vonk allows it, not all servers will.
I have an entity called Document and inside that I have a list of dispatchDetails. I have only created a repository for documents.When I make a GET request for a document by documentID, I get below result
{
"docType": "SAP_ACCOUNTS_PAYABLE",
"docStoreId": 456651,
"qualityChecked": true,
"format": "pdf",
"bookingId": -1,
"dispatchDetails": [
{
"dispatchQueId": 207443,
"dispatchStatus": "S",
"recipient": "fldcvisla12678.wdw.disney.com|#|/opt/apps/shared/shuttle/SAP/OUT/|#|f-tbxshuttlenp|#|D1$NeY984|#|SFTP|#|22|#|null",
"description": "Upload :FileUploadDispatcher; FTP:null/null;\n2d89df3d-ca51-4d35-9528-439923fa48d4..",
"dispatcher": "AD",
"_links": {
"generatedDocument": {
"href": "http://localhost:7070/booking-documents-service/docs/5999571"
}
}
}
],
"_links": {
"self": {
"href": "http://localhost:7070/booking-documents-service/docs/5999571"
},
"generatedDocument": {
"href": "http://localhost:7070/booking-documents-service/docs/5999571"
}
}
}
In dispatchDetails, I can't see a link for self. i.e. actions on sub resource level is not activated. Is there any way to generate that and enable sub resource level calls by adding anything to the existing repository without creating a separate repository for dispatchDetails? or with 2 repositories?
Simply what I want is to see both hateoas link and attributes loading inline.
Set dispatchDetails fetch policy for LAZY.
I'm currently building an API using spring hateoas. Most of my controllers provide a list method which returns a PagedResources<>. For some reason the selfrel does not contain the {?page,size,sort} template which is found in all examples. Instead I only get the base URI.
So my e.g. my ProjectContoller looks like
#GetMapping
public PagedResources<ProjectResource> list(Pageable pageable, PagedResourcesAssembler<Project> pagedResourcesAssembler){
Page<Project> projects = service.findAll(pageable);
return pagedResourcesAssembler.toResource(projects, assembler);
}
and returns
{"_embedded":{
"projectResourceList":[
{
"begin":1462053600000,
"end":1469829600000,
"name":"Cool Big Project",
"_links":{"self":{"href":"http://localhost/projects/1"}}
}
]
},
"_links":{"self":{"href":"http://localhost/projects"}},
"page":{
"size":20,
"totalElements":1,
"totalPages":1,
"number":0
}
}
I guess I'm missing something trivial but can't find out what :-/
After reading how HAL should behave in Spring Data Rest it seems there could be some bug.
This is the standard Spring Data Rest links output for a tasks collection resource:
"_links": {
"first": {
"href": "http://localhost:8080/tasks?page=0&size=20"
},
"self": {
"href": "http://localhost:8080/tasks"
},
"next": {
"href": "http://localhost:8080/tasks?page=1&size=20"
},
"last": {
"href": "http://localhost:8080/tasks?page=2&size=20"
},
"profile": {
"href": "http://localhost:8080/profile/tasks"
},
"search": {
"href": "http://localhost:8080/tasks/search"
}
No link is being templated in out of the box HAL responses rendered by Spring Data Rest, contrary to what is shown in docs.
If I follow next link, the self link is not correct:
"_links": {
"first": {
"href": "http://localhost:8080/tasks?page=0&size=20"
},
"prev": {
"href": "http://localhost:8080/tasks?page=0&size=20"
},
"self": {
"href": "http://localhost:8080/tasks"
},
"next": {
"href": "http://localhost:8080/tasks?page=2&size=20"
},
"last": {
"href": "http://localhost:8080/tasks?page=2&size=20"
},
"profile": {
"href": "http://localhost:8080/profile/tasks"
},
"search": {
"href": "http://localhost:8080/tasks/search"
}
}
If I override the Controller:
#RequestMapping(method = RequestMethod.GET, path = "/tasks")
public ResponseEntity<Page<Task>> read(Pageable pageRequest, PersistentEntityResourceAssembler assembler) {
Page<Task> pendingTasks = taskService.read(pageRequest);
return new ResponseEntity(pageAssembler.toResource(pendingTasks, (ResourceAssembler) assembler),
HttpStatus.OK);
}
And add a service between the controller and repository that initializes a Pageable instance to default values even if none is specified:
public Page<Task> read(Pageable pageRequest) {
Pageable effectivePageRequest = pageRequest;
if (effectivePageRequest == null) {
effectivePageRequest = new PageRequest(0, 20, DEFAULT_SORT);
}
if (effectivePageRequest.getSort() == null) {
//override Sort
effectivePageRequest = new PageRequest(effectivePageRequest.getPageNumber(),
effectivePageRequest.getPageSize(), DEFAULT_SORT);
}
return taskRepository.findByStatus(Task.Status.PENDING, effectivePageRequest);
}
I can overcome the problem with the self link. But have seen no way of generating the templlated URIs. These are the links for the second page:
"_links": {
"first": {
"href": "http://localhost:8080/tasks?page=0&size=20&sort=created,desc"
},
"prev": {
"href": "http://localhost:8080/tasks?page=0&size=20&sort=created,desc"
},
"self": {
"href": "http://localhost:8080/tasks?page=1&size=20&sort=created,desc"
},
"next": {
"href": "http://localhost:8080/tasks?page=2&size=20&sort=created,desc"
},
"last": {
"href": "http://localhost:8080/tasks?page=2&size=20&sort=created,desc"
}
Hope this helps discarding possibilities, but default behavior shown here suggests that there may be some problem with Spring HATEOAS/Spring Data Rest when generating collection links.
I'm using org.springframework.boot:spring-boot-starter-data-rest:jar:1.4.0.RELEASE, org.springframework.data:spring-data-rest-webmvc:jar:2.5.2.RELEASE, org.springframework.hateoas:spring-hateoas:jar:0.20.0.RELEASE
This is a newbie question. I see that the browsable API for Django rest framework is able to detect readonly fields defined in my serializer and exclude them from the HTML form that is presented to the user for PUT/POST operations.
What is the mechanism used here and can I use the same to indicate readonly fields to the remote client? I don't see any difference in the format between readonly fields and writable fields in the JSON object itself, so I'm guessing that there is some metadata involved but I couldn't find anything in the documentation.
I'm using Django version 1.7 and django-rest-framework version 3.1.3.
You should send an OPTION request to Django REST framework to get an insight of the expected content - see the option button on the browsable interface.
See the id field under the following sample:
{
"name": "Task List",
"description": "",
"renders": [
"application/json",
"text/html"
],
"parses": [
"application/json",
"application/x-www-form-urlencoded",
"multipart/form-data"
],
"actions": {
"POST": {
"id": {
"type": "integer",
"required": false,
"read_only": true,
"label": "ID"
},
"name": {
"type": "string",
"required": true,
"read_only": false,
"label": "Name",
"max_length": 64
},
"owner": {
"type": "field",
"required": true,
"read_only": false,
"label": "Owner",
"choices": [
{
"display_name": "admin",
"value": "admin"
},
{
"display_name": "cody",
"value": "cody"
}
]
}
}
}
}
U should use django-swagger, it is an app to document django rest framework APIs.
In you serializer field, you can set a help text, you can use it to mark as read only field the your client should read that on APIs document.