3

Background

I have a RESTful HTTP API.

I have a resource.

GET /my/cat HTTP/1.1

Clients may want to retrieve the image representation of this resource.

GET /my/cat HTTP/1.1
Accepts: image/png

Problem

Clients have a very wide variety of needed resolutions, and it is important they receive the correct resolution. If it is too large, it will consume excessive bandwidth. If it is too small, it won't look good. (Not to mention the downsampling/antialiasing the client will have to do to try to get it to fit what it wanted, with questionable results.)

Options

Query parameter

GET /my/cat?resolution=100 HTTP/1.1
Accepts: image/png

This works, but it is a little weird. If you change to accepting text/xml at that same location (URL), the it no longer makes much sense. And it requires URL mangling, which isn't really RESTful.

MIME type parameter

GET /my/cat HTTP/1.1
Accepts: image/png; resolution=100 

This makes some sense, as it very directly relates to the particular format I am requesting, similar to adding a charset parameter for text responses. I haven't seen this done before, though.

Custom header

GET /my/foo HTTP/1.1
Accepts: image/png
X-Image-Resolution: 100

I can't see much wrong with this. Custom headers are completely arbitrary, and can basically relate to anything about a request, and it can be tempting to overuse them.


What is most standard way to do this? Or at least the most "normal" or "expected" way?

3
  • 2
    What is the issue with the query parameter exactly? The 'resolution' to me feels like a filter, which I would implement that way...
    – JDT
    Nov 6, 2015 at 8:08
  • 1
    @JDT, REST has hypermedia. The client doesn't know the URL schema. Jan 27, 2016 at 18:25
  • I don't understand your comment exactly. Are you saying that the first solution you proposed yourself is not actually a viable option? I see no problem with it myself.
    – JDT
    Feb 3, 2016 at 20:09

3 Answers 3

6

What is most standard way to do this? Or at least the most "normal" or "expected" way?

I don't have statistics, but I can tell you what I would expect and what I have seen in more or less RESTful interfaces.

First of all, I wouldn't expect a cat image to be just another representation of the /my/cat resource. I would expect it to be a subresource (a kind of component) of the cat resource. In HTTP environments, images usually look like files, with an extension indicating their formats. So I would expect /my/cat/image.jpg and my/cat/image.png.

With regard to the resolution, it is common to offer an image in two or three resolutions, targeting at the desktop versus mobile platforms (two) or at the desktop, tablet and smartphone devices (three). This is expressed in their file names, e.g. /my/cat/image-large.jpg and /my/cat/image-small.jpg.

Well, this sounds really traditional, huh? No fancy use of HTTP headers etc. Yes, that's how HTTP is being used, that's what I think most people expect.

By the way, if you have a RESTful interface, I would expect /my/cat to return a JSON or XML response (maybe depending on the Accept header, indeed), which includes links to the images:

   {
      "name" : "Tom",
      "image" : {
         "large" : { "href" : "/my/cat/image-large.jpg" },
         "small" : { "href" : "/my/cat/image-small.jpg" }
      }
   }

See JAREST for more details about what I recommend for RESTful APIs.

3
  • Just to add to this. Think of rest as being just like an apache web server. CAT is a directory inside which there is a HTML page that indexes all the cat resources, only one of which is an image of a cat.
    – Richard
    May 2, 2017 at 22:49
  • I was thinking the exact same thing. Unless the REST service is e.g. an image scaling service, the querystring doesn't make sense, but having images as hypermedia links on the main resource does. Each image of each size is a single resource, which is also expected behavior. +1
    – Paul
    May 3, 2017 at 3:44
  • A query strings contribute to identify unequivocally the resource, plus it's opaque and doesn't provide meaning. When you say that they make sense for scalng services you are providing meaning. With or whitout querystring the URI are identifiers not services.
    – Laiv
    May 3, 2017 at 17:58
1

Ok, you have a resource at /my/cat.

Let's assume a cat has a name and a color.

When you request:

GET /my/cat HTTP/1.1
Accepts: application/json

You get:

{
  name: "Fluffy",
  color: "black"
}

When you request:

GET /my/cat HTTP/1.1
Accepts: image/png

You get nice, cartoon image of a black cat, maybe with a "Fluffy" label somewhere on it. This is an image representation of cat.

When you request:

GET /my/cat/photo HTTP/1.1
Accepts: image/png

You get actual photo of this particular cat as PNG image. Notice that this is a request to a different path, a request for a sub-resource - cat's photo isn't another representation of a cat resource (which has name+color).

When you request:

GET /my/cat/photo HTTP/1.1
Accepts: image/jpeg

You get JPEG photo of this cat.

Now, HTTP spec says that different path means different resource, and that includes file extensions (they have no special meaning in HTTP). By appending .jpg to a resource path you're creating a different path, pointing to a different resource.

To better explain why this is important look at the following request:

GET /my/cat/photo.jpg HTTP/1.1
Accepts: image/png

Notice that we specified .jpg in the path but we only accept image/png. This doesn't make much sense. What should the server do in this case? Well behaving servers don't treat file extensions in request paths in any special way and they return PNG file for such a request. So we get PNG image of /my/cat/photo.jpg resource, not /my/cat/photo resource.

Now what about specifying resolution/pixel density/quality of requested image?

If browser makers had their priorities right this is what would happen on high DPI (retina) device:

GET /my/cat/photo HTTP/1.1
Accepts: image/jpeg; ppi=255;

And on low-end smartphone:

GET /my/cat/photo HTTP/1.1
Accepts: image/jpeg; ppi=100;

You would use <img src="/my/cat/photo"> in your page and the browser would add these extra bits when requesting the resource. The server could then return appropriate image based on the ppi value. That's just a theoretical example, I wish browsers do that.

Given we don't have the above feature in our web browsers we have to be explicit about image quality when making a request.

So, what about having /cat/photo-small.jpg, /cat/photo-big.jpg etc?

If you want to do proper REST/HTTP then you should use the same URL for all "variants" of your image. The above two paths are two different resources in REST/HTTP semantics.

Ok, what about the query parameter, /cat/photo?size=small and /cat/photo?size=big?

According to HTTP spec whole URL identifies a resource, and that includes protocol, domain, path and query string. So the above two paths are also two different resources in REST/HTTP semantics. Unfortunately, query strings are not really compatible with proper media representation (type, language, any other variant) negotiation.

And there's no way for you to set any HTTP header (like X-Image-Resolution) when declaring <img> tags in your HTML, or when typing in the URL of the image in your browser's URL bar.

I don't think there's really "normal" or "standard" way to achieve your goal at the moment. I often see people using different image paths or query strings to specify image variants, which works in practice.

And that's fine, but then we should all stop pretending we're doing proper HTTP ;)

0

Despite this is old question, I would like to contribute.

First of all, as usual, KISS.

  1. Query component of a URI contains a set of parameters to be interpreted as variation or derivative of the resource that is identified by the path component.

    From my point of view the next URIs are similar:

    GET /resources?page=0&size=10

    GET /resource/0/image?width=x&high=y

    GET /resource/0/image?ratio=0.5

    All three are dimensioning the resource representation.

  2. Accept HTTP Header. Drop support for text/* or application/* content types

    GET /my/cat HTTP/1.1

    Accept: text/*

    ...

    HTTP/1.1 406 Not Acceptable

  3. Try avoid file extensions in the URI to indicate representation formats. Rely on media types and HTTP headers. Unless you want to get the file directly from the remote location instead of through the API.

Links that may Interest:

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.