I have a single ID REST API that I need to extend to support multiple (up to 10Ks) IDs. Basically to run update on all relevant IDs instead of sending 10Ks request in network.
Current endpoint:
#POST
#Path("{id}/update")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public ResponseVO updateBlockReason(#PathParam("id") int id, List<RequestVo> requestVo) {
One option suggested is comma-delimited values as stackexchange's answers-by-ids
Usage of /answers/{ids} GET
{ids} can contain up to 100 semicolon delimited ids. To find ids programmatically look for answer_id on answer objects.
This is the case on similar answers
http://our.api.com/Product/<id1>,<id2> :as James suggested can be an option since what comes after the Product tag is a parameter
But it seems awkward to me and RequestVo will be same for all IDs (which is currently is fine, but later to add such support will be harder)
It seems I need to change from Path variable to add it inside RequestVO
Which means the Id will be a JSON key, e.g.
[{
"id" : "1",
"name": "myAttribute"
"toggle": true
},
{
"id" : "2",
"name": "mySecondAttribute"
"toggle": false
}
]
Is this the correct approach or am I missing something?
Thank you in advance for any comments\answers
Current request VO
#Data
#AllArgsConstructor
#NoArgsConstructor
public class RequestVO {
private String name;
private boolean toggle;
// will add now private int id
}
My concern is also if I want (one of the requirement) to update with same request (as name=doA, toggle=true) for 10Ks Ids I'll have to duplicate request VO instead of sending ID separately
The best way is to keep id in your RequestVO DTO itself and not in URL as you have already suggested because even 100 ids in URL can make your URL very big and you are talking about 10K ids.
And again in future, the bit length of a single id may increase or later on you might need to update 50k or even 100K objects.
According to maximum length of a URL, there is no general specification on URL length but extremely long URLs are usually a mistake and URLs over 2,000 characters will not work in the most popular web browsers.
So I think your second approach is best here and will be good for future purposes also.
You may also want to use a PUT request because it makes more sense for an update request. So your code will become like this:
#PUT
#Path("/update")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public ResponseVO updateBlockReason(List<RequestVo> requestVo) {
I find the path product/{id}/update questionable, because you could achieve similar behavior by mapping #Put-request to product/{id} itself. The READ, WRITE differentiation is already explicit by the Request-mapping.
Also, whether or not using verbs in restful urls is a topic for itself.
Assuming you could use plural endpoints, this could look like /products/{id}.
Because you want to batch/bulk update products, you could map #Put-requests to /products now, with a list of updated Products in the RequestBody. Keep in mind, that this somewhat complicates the Response, as you may have to return Http-207 for answering the correct status of the update for each element in the list.
I want 1 logical endpoint for update
You can have a logical service method for this, but not endpoints really.
You already mentioned the problem of /{id} in your path for bulk updates.
If you really, really need to, I would remove the #Put-mapping from /products/{id} and redirect to /products where the update content would be a single element list, or a little more sophisticated, distinguished by a mediaType (what again means two endpints, but a single url).
Edit:
I just happen to understand the VO-issue. You are not updating Products, but parts of it (the name RequestVO was misleading me).
This smells like a #Patch-mapping to me, where parts of a Product get updated.
So I still would use /products but with a #Patch-mapping.
When a client needs to replace an existing Resource entirely, they can use PUT. When they’re doing a partial update, they can use HTTP PATCH.
This brings up another issue, use #Post only if the id is unknown (usually before something is CREATED and gets an id assigned, for UPDATES use #Put and reuse the assigned id)
Using post is technically doable, but because of idempotece not advisable.
Why not just pass the list of your IDs in the body of your request as JSON array? the code would be:
#POST
#Path("/update/ids")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public ResponseVO updateBlockReason(#RequestBody List<Integer> ids, List<RequestVo> requestVo) {
...
}
Related
Recently I was working on a little RESTful API using Spring and I came across the ModelAttribute annotation.
I noticed that there is some very interesting behavior associated with it, mainly the fact that you can stick it onto a method and it will get called before the handler for a given request is called, allowing you to do anything before data is bound to the arguments of your handler method.
One usage that comes to mind is default values:
#ModelAttribute("defaultEntity")
public Entity defaultEntity() {
final var entity = new Entity();
entity.setName("default name");
return entity;
}
#PostMapping("/entity")
public Entity createNewEntity(#Valid #ModelAttribute("defaultEntity") Entity entity) {
dao.saveEntity(entity);
return entity;
}
In this case, when a POST request comes to /entity, the first thing that will happen is that defaultEntity will get called, creating an entity with some default values pre-filled. Then, Spring will bind the incoming data into it (potentially overwriting the defaults or keeping them as-is) and then pass it into the createNewEntity handler. This is actually pretty nice, IMO.
Another surprising fact is that the annotated method can actually take parameters in much the same way as the handler method. A simple way to do partial entity updates could be something like this:
// first fetch the original entity from the database
#ModelAttribute("originalEntity")
public Entity originalEntity(#PathVariable("id") long id ) {
return dao.getEntity(id);
}
// then let Spring bind data to the entity and validate it
#PostMapping("/entity/{id}")
public Entity updateEntity(#Valid #ModelAttribute("originalEntity") Entity entity) {
// and finally we save it
dao.saveEntity(entity);
return entity;
}
Again, this is surprisingly easy.
Even more surprising is that different model attributes can depend on each other, so you can have a complicated multi-stage monster if you want:
// first fetch the original entity from the database
#ModelAttribute("originalEntity")
public Entity originalEntity(#PathVariable("id") long id ) {
return dao.getEntity(id);
}
// then let Spring bind data to the entity, validate it and do some processing to it
#ModelAttribute("boundAndValidatedEntity")
public Entity boundAndValidatedEntity(#Valid #ModelAttribute("originalEntity") Entity entity) {
processEntity(entity);
return entity;
}
// finally check that the entity is still valid and then save it
#PostMapping("/entity/{id}")
public Entity updateEntity(#Valid #ModelAttribute(value = "boundAndValidatedEntity", binding = false) Entity entity) {
dao.saveEntity(entity);
return entity;
}
Obviously not all of the model attributes have to be of the same type, some can depend on multiple arguments from different places. It's like a mini-DI container within a single controller.
However, there are some drawbacks:
as far as I can tell, it only works with query parameters and there is no way to make it work with other kinds of request parameters, such as the request body or path variables
all of the ModelAttribute-annotated methods within a single controller will always be called, which can
have a performance impact
be annoying to work with, since Spring will need to be able to gather all of the method's arguments (which may be impossible, for example when they reference a path variable that doesn't exist in the current request)
So, while ModelAttribute doesn't really seem too useful by itself because of these issues, I feel like the main idea behind it - essentially allowing you to control the construction of a method's parameter before it's bound/validated while being able to easily access other request parameters - is solid and could be very useful.
So, my question is simple - is there anything in Spring that would essentially act like ModelAttribute but without the drawbacks that I mentioned? Or maybe in some 3rd party library? Or maybe I could write something like this myself?
I've a simple RESTful API based on Spring MVC using a JPA connected MySQL database. Until now this API supports complete updates of an entity only. This means all fields must be provided inside of the request body.
#ResponseBody
#PutMapping(value = "{id}")
public ResponseEntity<?> update(#Valid #RequestBody Article newArticle, #PathVariable("id") long id) {
return service.updateById(id, newArticle);
}
The real problem here is the validation, how could I validate only provided fields while still require all fields during creation?
#Entity
public class Article {
#NotEmpty #Size(max = 100) String title;
#NotEmpty #Size(max = 500) String content;
// Getters and Setters
}
Example for a partial update request body {"content": "Just a test"} instead of {"title": "Title", "content": "Just a test"}.
The actual partial update is done by checking if the given field is not null:
if(newArticle.getTitle() != null) article.setTitle(newArticle.getTitle());
But the validation of course wont work! I've to deactivate the validation for the update method to run the RESTful service. I've essentially two questions:
How can I validate only a "existing" subset of properties in the
update method while still require all fields during creation?
Is there a more elegant way for update partially then checking for
null?
The complexity of partial updates and Spring JPA is that you may send half of the fields populated, and even that you will need to pull the entire entity from the data base, then just "merge" both entity and the pojo, because otherwise you will risk your data by sending null values to the database.
But merging itself is kind of tricky, because you need to operate over each field and take the decision of either send the new value to the data base or just keep the current one. And as you add fields, the validation needs to be updated, and tests get more complex. In one single statement: it doesn't scale. The idea is to always write code which is open for extension and closed for modifications. If you add more fields, then the validation block ideally doesn't need to change.
The way you deal with this in a REST model, is by operating over the entire entity each time you need. Let's say you have users, then you first pull a user:
GET /user/100
Then you have in your web page the entire fields of user id=100. Then you change its last name. You propagate the change calling the same resource URL with PUT verb:
PUT /user/100
And you send all the fields, or rather the "same entity" back with a new lastname. And you forget about validation, the validation will just work as a black box. If you add more fields, you add more #NotNull or whatever validation you need. Of course there may be situations where you need to actually write blocks of code for validation. Even in this case the validation doesn't get affected, as you will have a main for-loop for your validation, and each field will have its own validator. If you add fields, you add validators, but the main validation block remains untouchable.
I have the updateProvider(ProviderUpdateDto providerUpdt) method in my Spring controller, But I do not see the need to send the whole payload of the provider entity, if for example the client can only update the name or other attribute, that is, it is not necessary to send the whole entity if only it is necessary to update a field, this produces a Excessive bandwidth consumption when it is not necessary.
What is a better practice to send only the fields that are going to be updated and be able to build a DTO dynamically? and How would I do if I'm using Spring Boot to build my API?
You can use Jackson library, it provides the annotation #JsonInclude(Include.NON_NULL) and with this only properties with not null values will be passed to your client.
Check the link http://www.baeldung.com/jackson-ignore-null-fields for an example.
There are many technique to improve bandwidth usage
not pretty print Json
enable HTTP GZIP compression
However, it is more important to ensure ur API is logically sound, omitting some fields may break the business rules, too fine grain API design will also increase the interface complexity
Another option would be to have a DTO object for field changes which would work for every entity you have. E.g:
class EntityUpdateDTO {
// The class of the object you are updating. Or just use a custom identifier
private Class<? extends DTO> entityClass;
// the id of such object
private Long entityId;
// the fields you are updating
private String[] updateFields;
// the values of those fields...
private Object[] updateValues;
}
Example of a json object:
{
entityClass: 'MyEntityDTO',
entityId: 324123,
updateFields: [
'property1',
'property2'
],
updateValues: [
'blabla',
25,
]
}
Might bring some issues if any of your updateValues are complex objects themselves though...
Your API would become updateProvider(EntityUpdateDTO update);.
Of course you should leave out the entityClass field if you have an update API for each DTO, as you'd already know which class entity you are working on...
Still, unless you are working with huge objects I wouldn't worry about bandwidth.
I've a problem with a GET operation in a REST WS. We have a Front-end panel with several filters for searching customers. The panel contains these filters:
Customer ID (Customer property)
Customer Name (Customer property)
Account number (Account property)
License plate (Vehicle property)
...
In the domain model we have 3 entities:
Customer
Account (A customer could have 1 or more accounts)
Vehicle (An account could have 1 or more vehicles)
How can I implement REST GET operation for this seach?
GET ..../customers/?name={name}&accountNum={accountNumber}&licensePlate={licensePlate} ?????
I think it is wrong because accountNumber and licensePlate don´t belong to customer resource. I don´t need these properties in the result expected.
I think about create new resource like customerFilter but It is no sense if I have to return a customer resource.
Any idea?
Thank you!
It will not pretend to be a specific answer for your question. But I think it will clarify something related to GET method.
According with URI Specification - RFC 3986 and Http Specification - RFC 7230, there are 3 kinds of ways to send data from client to server: via query, via path or via message-body.
When you are using GET method, it is not recommended to use message-body, because GET method can be cached for improving performance stuffs and these caches could ignore the message-body or reject the request:
A payload within a GET request message has no defined semantics;
sending a payload body on a GET request might cause some existing
implementations to reject the request.
So, you can choose now query or path. Both are in URL in this format:
http://example.com/{path1}/{path2}?query1=value1&query2=value2
What are the differences between these? according with RFC 3986 - Path and RFC 3986 - Query:
The path component contains data, usually organized in hierarchical
form, that, along with data in the non-hierarchical query component
(Section 3.4), serves to identify a resource within the scope of the
URI's scheme and naming authority (if any).
The query component contains non-hierarchical data that, along with
data in the path component (Section 3.3), serves to identify a
resource within the scope of the URI's scheme and naming authority
(if any).
As conclusion, you can design whatever you want. You can use for example:
GET .../customers?name={name}&accountNum={accountNumber}&licensePlate={licensePlate}
GET .../customers/{customerId}/
GET .../customers?customerId=12345
I don't see any issue with the url you have but your concern is also valid.
There is something that we need to take into consideration in this scenario. With this search what you expect to get as the response of the API call?
If you want response should contain information about customer only, when the search condition gets satisfied then /customer is right.
If you want some generic response consisting of Customer, Account and Vehicle info, you can have some generic terminology instead of customer in the url.
I hope this will help you.
Thanks :)
I am looking for a way to have complex URL mappings that would depend on the content of the database. For instance I show two URL mappings:
/{category}/
/{category}/{slug}/{id}
Categories can be for instance:
apples, pears, lemons -> resolve to FruitListController,
beans, onions, potatoes -> resolve to VegetableListController.
Note that those categories are stored in database and might be changed.
If I have in addition the slug and id, both of them must be checked against the database if they exist.
Can I somehow write some kind "Category handler", which would return the corresponding Category and associated Controller?
Obviously this is a problem of translating URL -> Request. Also, is there a way to generate backwards Request -> URL in similar way? I would like something like in templates:
<c:foreach="apples as apple"
${apple.name}
</c:foreach>
Use WebArgumentResolvers. They have access to the WebRequest to see your URL. The resolvers can do your database lookups. The FruitWebArgumentResolver could return a Fruit or return UNRESOLVED. The VegetableArgumentResolver could return a Vegetable or return UNRESOLVED. After the argument is resolved, the handler adapter can map to the RequestMapping that takes the specific type as an argument. For example,
#RequestMapping("/{catagory}/{slug}")
public void fruitSlugList(Fruit fruit, #PathVariable("slug") String slug){
To see an example of this, I have a blog post here that uses the same type of mechanism:
http://www.adamweigold.com/2012/01/using-multpartrequestresolvers-with.html