Im working on a web service that i want to be RESTful. I know about the CRUD way of doing things, but I have a few things that im not completly clear with. So this is the case:
I have a tracking service that collects some data in the browser (client) and then sends it off to the tracking server. There are 2 cases, one where the profile exists and one where it does not. Finally the service returns some elements that has to be injected to the DOM.
So basically i need 2 web services:
http://mydomain.tld/profiles/
http://mydomain.tld/elements/
Question 1:
Right now im only using GET, but im rewriting the server to support CRUD. So in that case i have to use POST if the profile does not exist. Something like http://mydomain.tld/profiles/ and then POST payload have the information to save. If the profile is existing i use PUT and http://mydomain.tld/profiles// and payload of PUT has data to save. All good, but problem is that as far as i understand, xmlhttp does not support PUT. Now is it ok to use POST even though its an update?
Question 2:
As said my service returns some elements to be injected into the DOM, when a track is made. Logically, to keep it RESTful, i guess that i would have to use POST/PUT to update the profile and then GET to get the elements to inject. But to save bandwidth and resources on the serverside, it makes more sense to return the elements with the POST/PUT to profiles, even though its a different resource. What are your take on this?
BR/Sune
EDIT:
Question 3:
In some cases i only want to update the profile and NOT receive back elements. Could i still use same resource and then using a payload parameter to specify if i want elements, e.g. "dont_receive_elements:true"
On question #1, are you sure that xmlhttp does not support "put"? I just ran http://www.mnot.net/javascript/xmlhttprequest/ on three browsers (Chrome, Firefox, IE) and according to the output, "put" was successful on all browsers. Following the information on http://www.slideshare.net/apigee/rest-design-webinar (and I highly recommend checking out the many Apigee videos and slideshows on restful API), "put" is recommended for the use case you mention.
But you may be able to avoid this issue entirely by thinking a little differently about your data. Is it possible to consider that you have a profile and that for each profile you have 0 or more sets of payload information? In this model the two cases are:
1. No profile exists, create profile with a POST on .../profiles/ Then add elements/tracking data with posts to .../profile/123/tracks/ (or .../profile/123/elements/)
2. Profile exists, just add the elements/tracking data
(Sorry without understanding your model in detail, it is hard to be very precise).
As for question #2 - going with a data model where a profile has 0 or more elements, you could update the profile (adding the necessary elements) and then return the updated profile (and its full graph of elements), saving you any additional gets.
More generally on question #2, as the developer of the API you have a fair amount of freedom in the REST world - if you are focused on making it easy and obvious for the consumers of your API then you are probably fine.
Bottom line: Check out www.apigee.com - they know much more than I.
#Richard - thanks alot for your links and feedback. The solution i came down to is to make the API simple and clean as you suggest in your comment, having seperate calls to each resouce.
Then to be able to save bandwidth and keep performance up, I made a "non-official" function in the API that works like a proxy internally and are called with a single GET, that updates a profile and returns an element. This, i know, is not very restful etc, but it handles my situation and is not part of the official API. The reason i need it to support GET for this i need to call it from javascript and cross domain.
I guess i could have solved the cross domain by using JSONP, but i would still have to make the API "unclean" :)
Related
I'm building a CRUD for users in my rest API, and currently my GET route looks like this:
get("/api/users/:id")
But this just occured to me: what if a users tries to search for other users via their username?
So I thought about implementing another route, like so:
get("api/users/username/:id")
But this just looks a bit reduntant to me. Even more so if ever my app should allow searching for actual names as well. Would I then need 3 routes?
So in this wonderful community, are there any experienced web developers that could tell me how they would handle having to search for a user via their username?
Obs: if you need more details, just comment about it and I'll promptly update my question 🙃
how they would handle having to search for a user via their username?
How would you support this on a web site?
You would probably have a form; that form would have an input control that would allow the user to provide a user name. When the user submit the form, the browser would copy the form input controls into an application/x-www-form-urlencoded document (as described by the HTTP standard), then substitute that document as the query_part of the form action, and submit the query.
So the resulting request would perhaps look like
GET /api/users?username=GuiMendel HTTP/x.y
You could, of course, have as many different forms as you like, with different combinations of input controls. Some of those forms might share actions, but not necessarily.
so I could just have my controller for GET "/api/users" redirect to an action based on the inputs?
REST doesn't care about "controllers" -- that's an implementation detail; the whole point is that the client doesn't need to know how the server produces a representation of the resource, we just need to know how to ask for it (via the "uniform interface").
Your routing framework might care a great deal, but again that's just another implementation detail hiding behind the facade.
for example, there were no inputs, it would return all users (index), but with the input you suggested, it would filter out only users whose usernames matched the input? Did I get it right?
Yup, that's fine.
From the point of view of a REST client
/api/users
/api/users?username=GuiMendel
These identify different resources; the two resources don't have to have any meaningful relationship with each other at all. The machines don't care (human beings do care, so we normally design our identifiers in such a way that at least some human beings have an easy time of it -- for example, we might optimize our identifiers to make things easy when operators are reading the access logs).
I started using OpenAPI a few months ago, but I'm finding multiple caveats while generating documentation. This of course leads me to believe that I must be doing something wrong.
So, I usually begin always by creating the schemes for the multiple resources of my API, I then place a reference to the schemes on the request bodies for each respective endpoint. However I find that this approach makes generated documentation confusing, adding for example an id parameter on the request body of a user create operation when it's clearly not supposed to be there (Of course the user is being created at this point, so there isn't even an ID in the first place, and why would an ID be part of the request body?).
I ended up just making multiple schemes for a single data model, I would choose which one I used based on the operation (E.g. Creating vs Displaying), this way I could generate documentation more accurately. This helps me avoid situations like displaying relations that are generated post-creation on a create operation. (I know I'm probably not phrasing all this correctly but just bear with me).
In a nutshell, I'll end up with approximately 3 or 4 schemes per data model. For the User model I'll have for example User, UserNew, UserSubscriptions, UserAll. All schemes represent the same resource, they just differ in the data that they display.
Again, I feel like this is not the way that OpenAPI is meant to be used. So I hoped that maybe someone here that has more experience with this could guide me on the right path! Thank you.
Certain forms are too complicated to have them fit on one page. If, for example, a form involves large amounts of structured data, such as picking locations on a map, scheduling events in a calendar widget, or having certain parts of a form change depending on earlier input, it is of value to be able to break up a certain form over multiple pages.
This is easy to do with dynamic web pages and Javascript, as one would simply create a tab widget with different pages, and the actual submitted form would contain the whole tab widget and all of its input fields, yielding a single POST request for the entire operation.
Sometimes, however, it takes a long time to generate certain input fields; they might even be computationally intensive even after the page has been generated, taxing the low-end computer user's browser. Additionally, it becomes difficult or impossible to create forms that adapt themselves based on earlier input.
It therefore becomes necessary to split up a certain form over multiple full page requests.
This can prove to be difficult, especially since the first page of a form will POST to /location/a, which will issue a redirect to /location/b and requested as GET by the client. Passing the stored form data from POST /location/a to GET /location/b is where the difficulty lies.
Erwin Vervaet, the creator of Spring Web Flow (A subproject of the Spring framework, mostly known for its dependency injection capabilities) once wrote a blog article demonstrating this functionality in said framework, and also comparing it to the Lift Web Framework which implemented similar functionality. He then presents a challenge to other web frameworks, which is further described in a later article.
How would Yesod face this problem, especially considering its stateless REST-based nature?
Firstly, there's no pre-built solution to this in existence yet (that I'm aware of at least). And I'm not familiar with how the other frameworks mentioned solve the problem. So what I say here is pretty much conjecture. I'm fairly certain it would work, however.
The crux of the issue here is encoding page A's POST parameters into the GET request for page B. The simplest way to do that would be to stick page A's POST parameters into a session variable. However, doing so would break navigation pretty thoroughly: back/forward wouldn't work at all as described.
So we come back to REST: we need to encode the POST parameters into the request itself. That really means putting the information in either the requested path, or the query string. And the query string probably makes the most sense.
I'd be concerned about putting the raw POST parameters into the query string, as that would allow any proxy server to easily snoop the contents. So I'd like to leverage the existing cryptography from clientsession. In other words, we'll stick a signed, encrypted version of the previous form submission in a query string parameter.
To make it a bit more concrete:
User goes to page A via GET.
User submits page A via POST.
Server validates form submission, gets a value, serializes it, encrypts/hashes it.
User is redirected to page B as a GET, with a query string parameter containing the encrypted/hashed value from page A.
Continue this process as many times as desired.
On the final page, you can decrypt the query string parameter and have all of the form submissions.
This looks like it would be a fun add-on package to write if anyone's interested.
For a website I am working on defining a RESTful API. I believe I got it (mostly) correct using proper resource URIs and proper use of GET/POST/UPDATE/DELETE.
However there is one point where I can't quite figure out what the proper way to do it "in" REST would be - comparison of lists.
Let's say I have a bookstore and a customer can have a wishlist. The wishlist consists of books (their full Book record, i.e. name, synopsis, etc) and a full copy of the list exists on the client. What would be a good way to design the RESTful API to allow a client to query the correctness of its local wishlist (i.e. get to know what books have been added/removed on the wishlist on the server side)?
One option would be to just download the full wishlist from the server and compare it locally. However this is quite a large amount of data (due to the embedded content) and this is a mobile client with a low-bandwidth connection, so this would cause a lot of problems.
Another option would be to download not the whole wishlist (i.e. not including book infos) but only a list of the books' identifiers. This would be not much data (compared to the previous option) and the client could compare the lists locally. However to get the full book record for newly added books, a REST call would have to be made for every single new book. Again, as this is a mobile client with bad network connectivity, this could be problematic.
A third option and my favorite, would be that the client sends its list of identifiers to the server and the server compares it to the wishlist and returns what books were removed and the data for books that were added. This would mean a single roundtrip and only the necessary amount of data. As the wishlist size is estimated to be less than 100 entries, sending just the IDs would be a minimal amount of data (~0.5kb). However I don't know what kind of call would be appropriate - it can't be GET as we are sending data (and putting it all in the URL does not feel right), it can't be POST/UPDATE as we do not change anything on the server. Obviously it's not DELETE either.
How would you implement this third option?
Side-question: how would you solve this problem (i.e. why is option 3 stupid or what better, simple solutions may there be)?
Thank you.
P.S.: A fourth option would be to implement a more sophisticated protocol where the server keeps track of changes to the list (additions/deletes) and the client can e.g. query for changes based on a version identifier or simply a timestamp. However I like the third option better as implementation-wise it is much more simpler and less error-prone on both client and server.
There is nothing in HTTP that says that POST must update the server. People seem to forget the following line in RFC2616 regarding one use of POST:
Providing a block of data, such as the result of submitting a
form, to a data-handling process;
There is nothing wrong with taking your client side wishlist and POSTing to a resource whose sole purpose is to return a set of differences.
POST /Bookstore/WishlistComparisonEngine
The whole concept behind REST is that you leverage the power of the underlying HTTP protocol.
In this case there are two HTTP headers that can help you find out if the list on your mobile device is stale. An added benefit is that the client on your mobile device probably supports these headers natively, which means you won't have to add any client side code to implement them!
If-Modified-Since: check to see if the server's copy has been updated since your client first retrieved it
Etag: check to see if a unique identifier for your client's local copy matches that which is on the server. An easy way to generate the unique string required for ETags on your server is to just hash the service's text output using MD5.
You might try reading Mark Nottingham's excellent HTTP caching tutorial for information on how these headers work.
If you are using Rails 2.2 or greater, there is built in support for these headers.
Django 1.1 supports conditional view processing.
And this MIX video shows how to implement with ASP.Net MVC.
I think the key problems here are the definitions of Book and Wishlist, and where the authoritative copies of Wishlists are kept.
I'd attack the problem this way. First you have Books, which are keyed by ISBN number and have all the metadata describing the book (title, authors, description, publication date, pages, etc.) Then you have Wishlists, which are merely lists of ISBN numbers. You'll also have Customer and other resources.
You could name Book resources something like:
/book/{isbn}
and Wishlist resources:
/customer/{customer}/wishlist
assuming you have one wishlist per customer.
The authoritative Wishlists are on the server, and the client has a local cached copy. Likewise the authoritative Books are on the server, and the client has cached copies.
The Book representation could be, say, an XML document with the metadata. The Wishlist representation would be a list of Book resource names (and perhaps snippets of metadata). The Atom and RSS formats seem good fits for Wishlist representations.
So your client-server synchronization would go like this:
GET /customer/{customer}/wishlist
for ( each Book resource name /book/{isbn} in the wishlist )
GET /book/{isbn}
This is fully RESTful, and lets the client later on do PUT (to update a Wishlist) and DELETE (to delete it).
This synchronization would be pretty efficient on a wired connection, but since you're on a mobile you need to be more careful. As #marshally points out, HTTP 1.1 has a lot of optimization features. Do read that HTTP caching tutorial, and be sure to have your web server properly set Expires headers, ETags, etc. Then make sure the client has an HTTP cache. If your app were browser-based, you could leverage the browser cache. If you're rolling your own app, and can't find a caching library to use, you can write a really basic HTTP 1.1 cache that stores the returned representations in a database or in the file system. The cache entries would be indexed by resource names, and hold the expiration dates, entity tag numbers, etc. This cache might take a couple days or a week or two to write, but it is a general solution to your synchronization problems.
You can also consider using GZIP compression on the responses, as this cuts down the sizes by maybe 60%. All major browsers and servers support it, and there are client libraries you can use if your programming language doesn't already (Java has GzipInputStream, for instance).
If I strip out the domain-specific details from your question, here's what I get:
In your RESTful client-server application, the client stores a local copy of a large resource. Periodically, the client needs to check with the server to determine whether its copy of the resource is up-to-date.
marshally's suggestion is to use HTTP caching, which IMO is a good approach provided it can be done within your app's constraints (e.g., authentication system).
The downside is that if the resource is stale in any way, you'll be downloading the entire list, which sounds like it's not feasible in your situation.
Instead, how about re-evaluating the need to keep a local copy of the Wishlist in the first place:
How is your client currently using the local Wishlist?
If you had to, how would you replace the local copy with data fetched from the server?
What have you done to minimize your client's data requirements when building its Wishlist view(s) and executing business logic?
Your third alternative sounds nice, but I agree that it doesn't feel to RESTfull ...
Here's another suggestion that may or may not work: If you keep a version history of of your list, you could ask for updates since a specific version. This feels more like something that can be a GET operation. The version identifiers could either be simple version numbers (like in e.g. svn), or if you want to support branching or other non-linear history they could be some kind of checksums (like in e.g. monotone).
Disclaimer: I'm not an expert on REST philosophy or implementation by any means.
Edit: Did you ad that PS after I loaded the question? Or did I simply not read your question all the way through before writing an answer? Sorry. I still think the versioning might be a good idea, though.
I'm currently using the Jira SOAP interface within a C# (I suppose the language used here isn't terribly important).
Basically, I'm creating an API and a Winform that wraps some of the functionality of the soap service so that our Devs can programmaticly add bugs when something goes wrong in our application.
As part of this, I need to know the custom field IDs that are in use in Jira, rather than hardcoding them (as they are still prone to the occasional change) I used the GetCustomFields() method in the jira-rpc api then filtered it, so that all the developer needs to know is the name of the field, then the ID is filled in for them automagically.
This all works fine, but with one quite important proviso: that you login to the SOAP/RPC service as a user with administrative privaliges.
The Jira documentation indicates that the soap/rpc service follows the usual workflows and security schemes, however I can't find anything anywhere that would appear to remove this restriction on enumerating custom fields (and quite why in any instance you would want someone to HAVE to be an administrator to gain this access, especially as the custom field id's tend to be in Jira's HTML source is beyond me)
Does anyone know if I've missed a setting somewhere? Or if there is some sort of work-around for this, short of hardcoding the custom field id's?
Or is this a case of having to delve in to Jira's RPC plugin and modifying the source for it in order to give me the functionality I require?
Cheers
Edit for the sake of google/posterity
Wow, all this time on, and it looks like Atlassian still haven't changed this behavior.
Worked around this by creating a custom dictionary that logs in as an administrative user, grabs the custom fields and then logs out. Not ideal, but it should work 'til atlassian change things
You're not missing anything - there's no way to get custom fields via standard SOAP API.
In JIRA Client, we learn about custom fields in two ways:
We download issues via RSS view of the issue navigator, or via XML representation of a specific issue. If a custom field is set for an issue, the XML will have its id, class and value (values).
From time to time we inspect the content of IssueNavigator search page - looking for searchers for the custom fields. Screen-scraping the HTML gives us not only ids of the custom fields but also possible values for enum fields.
This is hackery, of course, and it may go wrong, so a good API would have been a lot better.
In your case, I can suggest two solutions:
Create your own SOAP (or REST) remote API plugin that will give you just that info that you miss from the standard API. Since you're seemingly in control of your JIRA, you can install anything there.
Screen-scrape the "New Bug" page for the project and type of issue you need to submit. You'll get all the info - fields, options, default values, which field is required.