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 :)
Related
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) {
...
}
I am trying to delete resource using id and name, so I have two different methods to delete the resource using id and name. The think I realized later is that this will throw me ambiguous http operation.
example Delete an animal from the database using following operation:
/animal/1 or animal/elephant
I do not believe query parameters is the right answer for this. You are trying to delete a particular resource and I feel path param would be the right answer for it (delete a resource with a sepcific path, query params are mostly used for Getting a resource). However, I am not sure how can I achieve this without getting an exception. Any ideas?
A resource should be identifiable by only one URL, in your case the ID.
Locating the object by other means is a search, aka a query, e.g. by name, even if names are guaranteed to be unique.
So, the following would be good, clear URLs:
/animal/1
/animal?name=elephant
/animal?color=grey
You could also using matrix parameters, e.g. if the tail of an elephant is a resource:
/animal/1/tail
/animal;name=elephant/tail
See URL matrix parameters vs. request parameters.
Is there any way of getting the metadata for a solr core ?
For instance I know the core name, and can obtain a SolServer from that and I also know the field name.
Is there any way to determine the metadata though. Specifically I would like to know whether the field type is an int or a double.
Thanks
You can make a request to the luke request handler:
http://localhost:8983/solr/corename/admin/luke?show=schema&wt=json&_=1453816769771
The output will include the schema for the core, along with the defined fields, their settings and their types:
{"fields":{"xyz":{"type":"string","flags":"I-S-M---OF-----l","copyDests":[],"copySources":[]}, .... }
A neat trick to find these endpoints is to watch the 'network' tab when browsing the admin interface to Solr, as the admin interface is just a static HTML / Javascript frontend that makes all the requests for actual content from the Solr server behind the scenes.
Just curious to know in which scenario we should go for #RequestParam and #PathVariable. I know that:
#RequestParam takes parameter value whereas #PathVariable takes placeholder value
#RequestParam can be optional (required=false) while making request whereas #PathVariable value has to be provided.
When we want to use #RequestParam we have to know the property syntax but for #PathVariable not required
Is there any other reason to go for specific one?
Use #PathVariable if you want to adhere to 'statefull' URLs.
For Example:-
/customer/:id Customer view/edit page
/customer/ Customer Add page
/customer/list List Customer Page
/customer/:cid/order All order of a Customer
/customer/:cid/order/:oid Specific order of a partucular Customer.
Wisely using Path Variable will result in URL that gives you hint/clue about what the resulting view/page means.
This also lets you support refresh,back & forward operation with no
extra effort.
#RequestParams can be used to exatract data which is not passed as path params. Your MVC handler can have combination of two as required.
org.springframework.web.bind.annotation.RequestParam is used to bind Query String.
org.springframework.web.bind.annotation.PathVariable is used to bind URL path.
org.springframework.web.bind.annotation.RequestBody is used to bind HTTP Body.
org.springframework.http.RequestEntity will give you some added flexibility in defining arbitrary HTTP Entity headers along with HTTP Body.
Best Practice:
If you want to identify a resource, you should use Path Variable.
But if you want to sort or filter items, then you should use query parameter.
Example:
/users # Fetch a list of users
/users?occupation=programmer # Fetch a list of user with filter programmer
/users/123 # Fetch a user who has id 123
you can get side effects. You don’t have to define other URL and other query parameter to achieve basic CRUD functions.You change HTTP method depends on what you want to do.
/users [GET] # Fetch a list of users
/users [POST] # Create new user
/users/123 [PUT] # Update user
/users/123 [DELETE] # remove user
Putting optional parameters in the Template URL will end up getting really messy, So I would recommend to put optional parameter in Query String.
Could anyone please clarify the defination of attribute?
for example, in the following code, what is an attribute:
request.setAttribute("ja",new foo.Employee());
Is the attribute in the above code an object of type foo.Employee(), or it is key/value pair, or it is actually "ja"?
Request attributes are values indexed by a key (in your case "ja") which are shared in the life of the request object. In Java filter, servlet, jsp, include and forward use same request object so for example you can push an object in a servlet and pull it in a JSP.
The same approach is for session and application scopes
Request attributes are (or at least act like) a map of objects, in this case the key is "ja" and the value is a new foo.Employee.
The session, page, and application have the same data structure.
From the servlet API specification:
Attributes are objects associated with a request. Attributes may be set by the
container to express information that otherwise could not be expressed via the API,
or may be set by a servlet to communicate information to another servlet (via the
RequestDispatcher). Only one attribute value may be associated with an attribute name.
Here an attribute is a custom piece of information (here a new foo.Employee) added to your request (in a Map,Object> . This information will last as long as this request is processed and it can be used later in the process, for example by a JSP.
It's a key value pair
From the docs:
setAttribute
public void
setAttribute(java.lang.String name,
java.lang.Object o)
Stores an attribute in this request. Attributes are reset between
requests. This method is most often
used in conjunction with
RequestDispatcher.
Attribute names should follow the same conventions as package names.
Names beginning with java., javax.,
and com.sun.*, are reserved for use by
Sun Microsystems.
If the value passed in is null, the effect is the same as calling
removeAttribute(java.lang.String).