I am currently working with an API where a POST request may create multiple resources, depending on the resource being passed. To give you an example, I have a Reservation resource, and two child resources, Ancillary and SpecialRequest. A reservation is uniquely identified by an alphanumeric string of 6 characters, and an Ancillary is identified by a unique ID within the reservation (i.e. ancillary IDs are only known to the parent reservation).
So, to create an Ancillary, my API endpoint looks like this:
POST /reservations/{reservationId}/ancillaries/
Usually REST states that the resource being created (Ancillary in this case) is the resource that should be returned. However, my use-case is somewhat more complicated than that, since the reservation system which my API is interfacing with is legacy, and is somewhat unpredictable.
There are certain ancillaries (bundles) which actually create multiple ancillaries. For example, an ancillary might be a package of two other ancillaries, which costs less than if you had to purchase the other two ancillaries. Moreover, an ancillary might also be linked automatically to a SpecialRequest.
I'm wondering what my options are ... so far I've come up with the following:
Return the entire Reservation, which is sure to include all sub resources which were created and/or modified as a result of the Ancillary creation. This is the "safest" option, but in doing so I wouldn't be able to tell the user which Ancillarys were created.
Return only the Ancillary which was created, however this approach is likely make my API less usable since the API user is extremely likely to perform a GET /reservations/{reservationId}
Return a Ancillary[] regardless of the ancillary type, although this still leaves out the link between the Ancillary and the SpecialRequest.
Thanks in advance for your thoughts and input
Related
I am using Jersey as my RESTful Web Services framework.
I have made 2 new #GET REST calls in order to search objects in the database.
Those REST calls should serve 2 different components.
The first component has the user_id in its details.
The second component has the email in its details.
The email and the user_id are 2 different unique identifiers.
One option is to enable 2 different #GET REST calls with different paths such as:
".../api/users/search/id/:user_id"
".../api/users/search/email/:email"
In order to make is as simple as possible and much more general for future demands, I am looking for the way to merge those 2 REST calls to 1 path with different identifiers on in its QueryParam.
After searching for a good example followed by the best practices for such REST calls, I am not sure if such a merge is a good practice, or not.
The main issue I am not sure about is how to build the path as generic as can be.
The questions are:
Is it a good practice to merge those 2 different paths to 1 path in 1 REST call? if yes, please provide an example.
If it is a bad practice to merge those paths, how should I handle future features requests to search using new identifiers? the thinking of building new REST call for each identifier sounds problematic.
Well, one solution would be to add new queryParameters to the uri and thus enabling the users to use the same resource but with new parameters like for instance: /uri/search/email?aa#aa.com&user_id=userAdam.
Then you will on the server side need to take care of all these different scenarios in the same method. Thus you dive into the if/else/switch type of thing.
You will then have to handle to JSON objects returned. Obviously if you have a contract you need to follow it, thus if the client expects completely different responses it is hard to deliver them from one resource. So if the email query response is something significantly different than the user_id query response it might be hairy returning it from the same resource.
My 5 cents. Maybe I misunderstood you.
Recently I've been dealing with Sring Security trying to customize it in my own way. For instance, I managed to introduce my authorization logic into a request's execution flow to tell whether the current user is authorized to call some method or not. I've done so by injecting a PreInvocationAuthorizationAdvice object where its before method is called and I can tell if the process should continue or not.
Now I want to do the same for authentication. I would like to inject my code (somehow, somewhere) where I'll be asked if some specific method needs authentication or not. I know I can do this in WebSecurityConfigurerAdapter.configure by calling antMatchers, regexMatchers etc. But I would rather do this case by case, instead of grouping URLs.
Is there anyway to do this?
It almost sounds like you are treating the ACL like an aspect that can be reused on different data sets, and if that's the assumption I'm not sure it holds up.
Last time I built a large system that included permissions, the model was something like this.
You have a number of users
You have a number of resources
You have a number of operations that can be performed on resources.
You can define roles that define different permission-sets (set of operations)
You have a number of projects
The resources are scoped by projects (they have a projectId)
A user is assigned zero or more roles in each project (mappings)
A user's access to a resource depends on the user's roles in the project which owns the resource (this could be changed at runtime).
If user U wants to delete resource A, you therefore need to find out what project resource A belongs to, and if the effective permission-set of U (join all roles U may have in the project) contained the "Delete Resource" privileged.
You need to be extremely careful on the backend when writing your SQL/JPA queries, because you can never trust the client. This means that you can't POST the projectId and resourceId, you always have to start with the resourceId, see which project it belongs to and then check if the operation is allowed.
If you have a View All feature, allowing a user to see all resources across projects, and a user can see resources in 3 of 5 projects, you need to ask your security model for a list of projects where the user has the View Resource privileged, and then add those projectIds to the query for loading the data. The projectIds needs to go into the query, just like sorting and pagination parameters. Typically you will need two queries since you also need a count query to calculate the total number of pages.
In my experience, the data model and the ACL are completely intertwined. If you want to make the ACL implementation independent of the data model, I fear you will either end up with an inefficient system that needs to load too much data and then filter away resources based on permissions afterwards. Or you will end up with a system that is overly complicated, because you need a generic way to transfer your ACL logic into the resource loading queries (and in the system I described, they are not simple to begin with).
There may be simpler systems than the one I described where a generic ACL implementation would work, but not on the enterprise stuff I have implemented over the last 8 years.
I am creating an API and I wonder why it is common to have a id parameter in URI for PUT?
Such as PUT /cars/5
Why don't have PUT /cars? The request entity contains an id field isn't that enough? I can get the id from that entity, or is it some downsides to this, and is it considered bad to do it?
Because if you were to send a PUT request to /cars, semantically that would imply you are trying to modify attributes about the set of cars, rather than modifying attributes of an individual car. The URI in a RESTful API should indicate the exact resource the action is acting upon, so if you are modifying a resource, your URI should exactly indicate that resource.
Also, from RFC 2616:
The URI in a PUT request identifies the entity enclosed with the request -- the user agent knows what URI is intended and the server MUST NOT attempt to apply the request to some other resource.
So the spec indicates that if the client knows the resource's unique ID, it should be included in the URI.
this comes from rest "ideology".
the idea is that a url uniquely represents an entity - so you must PUT the entity youre creating/editing to the url of that entity.
to quote from the wikipedia page:
Identification of resources
Individual resources are identified in requests, for example using URIs in web-based REST systems. The resources themselves are conceptually separate from the representations that are returned to the client.
PUT aims to udpate ONE precised entity.
With merely using /cars, you aren't focus on a specific entity.
And on the contrary of what you wrote, your full entity isn't passed in a basic String (URI).
Excepted if your targeted method focus on a hard-coded car id ... but I don't think so..
It comes down to the API interface. There are several approaches to API design. And, like you suggested, you can leave the id out of the request. However, since many API designs are structured in a fashion you described, like PUT /cars/5, it`s considered good practise.
Basically, you have 8 ways to interact with your API. GET, POST, PUT, DELETE and an optional HEAD. ( if you count head, the total would be 9 or 10, depending on the interactions).
So, to clear it up, you have 2 ways of GET. GET /cars would retrieve all cars, GET /cars/5 would retrieve any car with an ID of 5.So, you have 2 ways of using GET. The same goes for POST, PUT and DELETE. 4*2 = 8 right?
Now, there are people who would say that PUT /cars would be ambigious, however you are completely valid in doing it without the extra ID field, because, as you mentioned, you are already passing in the ID field in the request.
The guys at Apigee have been researching API designs for a while now. I recommend watching some of their video`s to understand better what API design means and why some arguments are valid, and others are not.
Apigee Best practises
I have an app used in 3 different countries. It is almost all the same, with a few country dependent exceptions, for example an order would have salesTax in USA, but not in the UK. Each country has its own database, completely separate to each other, with slight differences in the table structure for these country specific properties. The table names are always identical, as are the columns that all countries share; it is only the extra columns that make them different.
Currently as a result I actually have 3 separate applications, with about 80% identical code. Every time I develop something, I need to copy it into each individual application which is a bit laborious.
My plan is to try and have a global application, and then extend this with the 3 much smaller country specific ones.
For my services and daos this can be handled by my interfaces being global, and any implementations that vary between the apps being local. The local apps will pull in the global as a dependency.
However, my problem is with the business objects. With the order as an example again, many of my services and daos use the Order object, and they would be global since the code is identical across countries. So I need to have some sort of GLOBAL Order object. But I also want to make sure that depending on the country, the extra properties are available and persisted if I go and save them again.
As an example, I have a service that just checks the tracking of an order and marks it as Delivered. This service is global, so would have access to the GLOBAL Order object. it would call orderDao.getOrder(id) thanks to the orderDao interface being global. The orderDaoImpl would be local, so the getOrder(id) method would actually return a localized Order instance, complete with extra fields. When this is passed up through the layers it reaches the tracking service again, which is expecting a GLOBAL Order, meaning the localized fields are not available (this is OK because this trackingService doesn't care about them, if it did, the implementation would be localized).
When I update the status of this GLOBAL Order I need to make sure that when it gets saved the localized properties are not lost.
So my big question is how can I make this work?
Option 1: Is there any way for me to define some "selective transiency"? So I have a single Java Object with all possible properties. When it is used in each application, if any of those properties don't exist in the database table just ignore it and carry on.
OR
Option 2: Is there a way for me to use some abstract/interfaced class that could be used at both global and local level, with local apps automatically casting the object as the implemented subclass?
It is worth pointing out that the GLOBAL app as such would never actually be run by itself, it would only ever be used within the local apps. But obviously cant have references to the local classes.
Cheers for any assistance.
Might not be the most elegant solution, but you could make a global order class that isn't mapped as an #Entity and include it in your regional orders with #Embedded
class GlobalOrder {
#Column(name = "total")
private Integer orderTotal;
...
}
#Entity
#Table("emeaorder")
class EMEAOrder {
#Embedded
private GlobalOrder globalOrder;
#Column(name = "tax")
private Integer euSalesTax;
...
}
OK. I think I have come up with the least elegant, but workable solution to this.
The problem is allowing my global apps to know about the local Business Objects such as Orders, but not having them mapped to columns that may not exist in each country.
So, I have included them in my Global project, and included every possible attribute, and getters/setters.
I then ALSO include them in my local projects with only the fields needed in that particular country.
Since the global apps are never actually used by themselves, whenever I build the library using maven, I exclude any of the business objects that are in my local projects. So when the local then includes it as a dependency there are no conflicts, and only the local version of these objects are included, meaning Hibernate doesn't complain about missing columns.
Not clean, but it works!
I am creating a web application that incorporates REST-style services and I wanted some clarification as to the preferred (standard) method of how the POST requests should be accepted by my Java server side:
Method 1:
http://localhost:8080/services/processser/uid/{uidvalue}/eid/{eidvalue}
Method 2:
http://localhost:8080/services/processuser
{uid:"",eid:""} - this would be sent as JSON in the post body
Both methods would use the "application/json" content-type, but are there advantages, disadvantages to each method. One disadvantage to method 2, I can immediately think of is that the JSON data, would need to be mapped to a Java Object, thus creating a Java object any time any user access the "processuser" servlet api. Your input is much appreciated.
In this particular instance, the data would be used to query the database, to return a json response back to the client.
I think we need to go back a little from your question. Your path segment starts with:
/services/processuser
This is a mistake. The URI should identify a resource, not an operation. This may not be always possible, but it's something you should strive for.
In this case, you seem to identify your user with a uid and an eid (whatever those are). You could build paths such as a user is referred to by /user/<uid>/<eid>, /user/<uid>-<eid> (if you must /user/uid/<uid>/eid/<eid>); if eid is a specialization, and not on equal footing with uid, then /user/<uid>;eid=<eid> would be more appropriate.
You would create new users by posting to /user/ or /user/<uid>/<eid> if you knew the identifiers in advance, deleting users by using DELETE on /user/<uid>/<eid> and change state by using PUT on /user/<uid>/<eid>.
So to answer your question, you should use PUT on /user/<uid>/<eid> if "processuser" aims to change the state of the user with data you provide. Otherwise, the mapping to the REST model is not so clean, possibly the best option would be to define a resource /user/process/<uid>/<eid> and POST there with all the data, but a POST to /user/process with all the data would be more or less the same, since we're already in RPC-like camp.
For POST requests, Method 2 is usually preferred, although often the resource name will be pluralized, so that you actually post to:
http://localhost:8080/services/processusers
This is for creating new records, however.
It looks like you're really using what most RESTful services would use a GET request for (retrieving a record), in which case, Method 1 is preferred.
Edit:
I realize I didn't source my answer, so consider the standards set by Rails. You may or may not agree that it is a valid standard.