Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clean url vs Queried strings - Web service request from my iOS Client

I've been searching a lot for a clear answer or explanation, but can't find a thorough one.

I'm building mobile app, which is mostly based on data from my backend. I can structure most of my requests to my server (php based) using pure restful request or requests with query strings.

2 questions:

1. Let's say I have a friend class. I want to get or set Dani's friends.

In rest I would do:

http://www.example.com/Dani/friends - GET (to get all his friends)
http://www.example.com/Dani/friends - POST (to create a new friend for Dani)

Using queried strings:

Http://www.example.com/ user=Dani & action=get_friends (GET method I assume?)
http://www.example.com/ user=Dani & action=add_friend (POST method I assume?)

So, for the first example we have caching in place, plus one constant very readable URL.

In the second, we don't really have a cache (even if there is, you somehow have to tell your proxy to refresh the cache of the list of friends, after one call the second URL to add friend), since those are 2 different URIs. In rest, it's done automatically by definition of http methods (post/put makes the resource "dirty")

Am I right here?

I want to know what's the best option: Caching-wise, security, single point of entry (in php code), simper to implement (in both client and server) and so...

2. How would I construct a url to find only a certain friends' photos? (Let's say the ones at a certain location.)

I thought of:

http://www.example.com/Dani/friends?long=1&lat=2&field=photos

Is this right, or is there a better way?

like image 976
Idan Avatar asked Dec 05 '22 17:12

Idan


2 Answers

URL Construction
One of the most critical things to remember about REST URL construction is that each URL should identify a single resource. In general, this means that URLs are typically broken down into parts:

  1. Lists of top-level objects: /users and /photos
  2. Top-level object instance: /users/1/Dani and /photos/4356
  3. Instance-level lists:
    • /users/1/Dani/friends - Dani's friends
    • /users/1/Dani/photos - Dani's photos

Resource Interaction
Interacting with these resources (ie. Creating, Reading, Updating, Deleting) are handled through the "HTTP verbs", or the "HTTP Methods" that each URL is actually called with. The advantage of this is that each resource (or "thing) you have only needs to know how to do 4 different things, meaning you have a much more simple application.

Your application is also much more structured and compartmentalized, making it easier to test and allows you to update and make changes to it much more easily, since things are more loosely coupled.

Once you don't have a clear 1-1 connection between a single resource and a single URL, you don't have RESTful URLs anymore. Once you start putting things like action into the query string, you are essentially doing remote procedure calls (RPC) rather than REST. Everything flowing through a central point couples things together more than they need to be, which makes your architecture rigid, hard to change, and very hard to test.

Searching
The trick is that, for any sort of "list" type resource, you can have that list be the result of some sort of query. There's nothing that says the list always has to be the same. It wouldn't make sense to use http://www.example.com/Dani/friends?long=1&lat=2&field=photos because that would return you a list of photos of Dani's friends, which is getting rather far away from Dani the User.

Since you are looking for Photos, and we already have a URL that identifies a "List of Photos" resource, that is the URL we should be using, but just to get those photos with certain attributes.

So, for your example of finding all of the photos that belong to a certain user (who might be one of Dani's friends, you might do something like:

GET /photos?owner=[userId]

and you could perhaps look for only photos taken within 1km from some lat/long coordinate:

GET /photos?owner=[userId]&radius=1&lat=[someLat]&long=[someLong]

Or if you are looking a bit more broadly, perhaps you want the photos of all of Dani's friends from that area:

GET /photos?ownerFriendOf=[Dani's userId]&radius=1&lat=[someLat]&long=[someLong]

In all of these cases, you are searching the list of photos based on the query string you are sending to the photo list, which lives at /photos.

Caching
Caching is only an "added bonus". In theory, just about any request can be cached, but you don't need to worry about that right now. In general, however, if you stick to a REST architecture, you will be fine once the times comes.

Security
HTTP has a number of built-in ways of dealing with security, and any of them will work, depending on how secure you need your application to be. Basic and Digest security work well for app-to-app communication since they send their authentication tokens (ie. username and password) along with the request. For a security flow that involves a user, however, you are likely to want to use a Session mechanism and use HTTP Cookie headers to keep track of the session.

In all cases, however, any time a username/password is moving from the client to the server, it should be over a secure SSL (https) connection to keep evildoers from sniffing it. For particularly sensitive applications, all interaction may be through an SSL connection, and for other applications only the login sequence might be. In general, however, being more secure is better than less.

Implementation Simplicity
On the security front, most web frameworks have built-in methods that can handle all of the security methods I just mentioned. You may be wondering if you need to use a web framework, and while it isn't strictly required, it will dramatically reduce the amount of work you have to do and at the same time it will reduce the number of bugs because most of the "heavy lifting" is handled by the framework and has been very well tested.

Many frameworks today have built-in support for handling RESTful requests and you can quickly get up and running. RPC based support is often less supported, since it doesn't have as clearly a defined application architecture as REST, but it is still possible with just about any framework.

In the long-run, however, you are likely to get much more bang for your buck by going with a RESTful architecture.

like image 72
cdeszaq Avatar answered Dec 07 '22 08:12

cdeszaq


Before we address your specific questions, should note that the idea that having "clean", / delimited URIs means your app is more RESTful is a misconception. A URI with a "query string" can be just as good. Having said that, URIs can usually have read flags that you're doing something not so RESTful. In your case, this is the mention of action=method in your URI is a flag. The action to take should be entirely derived from the HTTP method (more generically speaking, your "uniform interface method").

The most important argument for option 1 may be that making a GET request to a URI should always be safe. When you start having methods like add_friend or worse, delete_friend in a URI that is accessible via a GET request, you run the risk of someone accidentally navigating to one of those URIs and performing an unwanted, unsafe operation. Separate HTTP methods exist to prevent mistakes like this from happening.

On to your questions:

  1. Caching: Most modern clients/browsers and proxies should be able to cache resources with query strings in the URI (assuming the order of the query string doesn't change), however you are correct, the former URLs would be safer in terms of caching.

    Single Point of Entry: You can still have a single point of entry with the "more restful" URLs. It just requires using something like mod_rewrite (assuming you're using Apache) to direct all URLs to a single php controller file. Other HTTP servers like nginx have similar support built in. If you are using Apache and don't have mod_rewrite you can take advantage of the fact that a request to /api.php/Dani/friends will still go to the php file api.php.

    Implementation: Client side implementation is equal effort. Server side implementation really depends on how the internals of your app are structure. Are you using an ORM? Is "friends" a relationship on "User"? If so, it should be straightforward to be able to map a URI like /Dani/friends into (pseudocode) 1. initialize "Dani" user 2. Get Dani's friends relationship 3. echo out representation.

    If your underlying code is already heavily instance method driven (e.g. get_friends) then it will take a little more work upfront, however it may be worth it. When you expose direct methods in a URI, you've now coupled your URI with your underlying source code making maintenance/updates tricky and error prone. Also there are potential security risks here if you're not careful when exposing methods like that in the URI.

    Worth noting that the first psuedocode recommendation could also suffer from similar issues on a less severe scale. Sometimes it's just best to create unique handles for URIs (like Sinatra, Express, or similar PHP clones do nicely). This is your safest option when it comes to flexibility, safety, maintenance, and "future-proofing".

  2. http://www.example.com/Dani/friends?long=1&lat=2&field=photos is a perfectly acceptable URI (see opening remarks). It is no more or less restful than a URI with a bunch of '/'s and it doesn't have any red flags (like verbs in the URI).

like image 44
nategood Avatar answered Dec 07 '22 07:12

nategood