8

I am working on an REST API which returns, for example, a list of ebooks. Each ebook has many photos and one PDF. I have the name and url of each image file and pdf file.

How should I include this information in a REST API response?
1. Have a property Photos and another Document?
2. Or simply one saying Files and specifying the file type?
3. Or some other way?

At the moment I have the following JSON:

{
  "ebooks": [
    { 
      "title": "ebook 1"
    },
    { 
      "title": "ebook 2"
    }
  ]  
}

I am trying to have a standard way of doing this so it is consistence across my API endpoints.

Miguel Moura
  • 36,732
  • 85
  • 259
  • 481

3 Answers3

5

You should really return just meta-data in an API like the one you described, and inside every ebook record insert links to the files.

A response from your API should look like the following JSON:

{
  "ebooks": [
    { 
      "title": "ebook 1",
      "pictures:" [
          "http://myhost/pictures/picture1.jpg",
          "http://myhost/pictures/picture2.jpg",        
          ],
      "document": "http://myhost/ebooks/ebook1.pdf"
    },
    { 
      "title": "ebook 2",
      "pictures:" [
          "http://myhost/pictures/picture3.jpg",
          "http://myhost/pictures/picture4.jpg",        
          ],
      "document": "http://myhost/ebooks/ebook2.pdf"
    }
  ]  
}

This approach is fully RESTful and is exactly what the HATEOAS constraint suggest you to do: let your resources be addressable.

You can't return both JSON and raw binary content using the same response, and I strongly suggest you to avoid converting your files into Base64 strings and returning them into the JSON response for two main reasons:

  1. Base64 encoding increases up to the 33% percent the size of your files
  2. You API response will become huge. Imagine you have to return even just 10 ebooks records (a small amout): this will mean that you have to encode a great amount of data (pdf, images, etc.) into Base64. A simple response from you API could lead the browser to download hundreds of MBs of data.
Community
  • 1
  • 1
Federico Dipuma
  • 17,655
  • 4
  • 39
  • 56
-1

As I see it, both solution 1 and 2 are acceptable.

JSON is by definition extensible. The day you want to add a new type of resource, the JSON will not be a problem. You'll simply add new stuff to the response and it will either be ignored by the client (the serialization should ignore the new properties) or never used (like in Javascript, the property will be there, but the client code will not know how to use it and will leave it alone). Otherwise, the client has the problem, but not your API.

The only problem I can see with the solution 1 would be that on the C# level, it clashes a bit with the Open-Closed principle. If you have a collection of resources and you specify the type along with the URL to access it, you will be able to create new types of resources without having to change any code to your Response class. This is why solution 2 would be my pick.

As I said though, both solutions work and are acceptable.

gretro
  • 1,934
  • 15
  • 25
-2

In the past ive returned files as a base 64 string then reassembled in the client. Here is a quick example of the the action

    public IHttpActionResult GetEbooks() 
    {
        var filePath = "{some file path}";
        var fileName = System.IO.Path.GetFileName(filePath);
        var fileBytes = System.IO.File.ReadAllBytes(filePath);
        var fileString = Convert.ToBase64String(fileBytes);

        var returnData = new EbookCollection();
        returnData.Ebooks.Add(new Ebook()
        {
            Title = "ebook1",
            FileName = fileName,
            Content = fileString
        });

        return Ok(JsonConvert.SerializeObject(returnData));
    }

public class EbookCollection 
    {
        public List<Ebook> Ebooks { get; set; }

        public EbookCollection() 
        {
            this.Ebooks = new List<Ebook>();
        }
    }

    public class Ebook 
    {
        public string Title { get; set; }
        public string Content { get; set; }
        public string FileName { get; set; }
    }

Then on the client you can change back into a byte array and write the file