http – File upload as part of REST-JSON concept

Question:

We make a REST service, JSON is used as a transport (if specifics are required, the service is done in Java / Spring). There was a need to upload files, and I do not understand how the request should look right. At the moment, all data is transferred as a JSON array inside the request body without any parameters, and I don’t understand if it’s possible to organize file uploads inside this concept – at the moment you just need to transfer the file, but you may need to transfer some – or data – name, purpose, etc. How is it organized in a human way?

Answer:

A bit of brainstorming.

Option 1

We upload the file by POST, we apply the metadata as query parameters.

POST /someurl/upload?date=...&comment=...

Option 2

  1. Allocate a resource that describes the metadata of the file:

     /someurl/document/{id}

    and the subresource is the file itself:

     /someurl/document/{id}/data
  2. We create an instance, passing metadata.

     POST /someurl/document { 'date':'2007-03-01T13:00:00Z', 'comment':'....' }
  3. We receive an identifier in the response

     HTTP/1.1 201 Created { 'id': 123456, 'date':'2007-03-01T13:00:00Z', 'comment':'....' }

    At this point, we can already query the file's metadata:

     GET /someurl/document/123456 HTTP/1.1 200 OK { 'id': 123456, 'date':'2007-03-01T13:00:00Z', 'comment':'....' }

    But the file itself is not yet available (no subresource created)

     GET /someurl/document/123456/data HTTP/1.1 404 Not Found

    Status 404 is not scary, it suggests that the resource may still appear along this path.

  4. Upload the POST file as a subresource:

     POST /someurl/document/123456/data

    Now no problem, the file is available:

     GET /someurl/document/123456/data HTTP/1.1 200 OK

If you want 100% bold REST with HATEOAS, then at step 3 you need to include a hypertext link to yourself in the response and you can get rid of the identifier:

{ 
   'date':'2007-03-01T13:00:00Z',
   'comment':'....',
   '_links':{
       'self': { 'href': '/someurl/document/123456' }
   }
}

And after step 4, when requesting metadata, return a link to the file:

{ 
   'date':'2007-03-01T13:00:00Z',
   'comment':'....',
   '_links':{
       'self': { 'href': '/someurl/document/123456' },
       'data': { 'href': '/someurl/document/123456/data' },
   }
}

This way, the client side will be less tempted to access a file that has not yet been downloaded.


Option 3

We upload everything with json. We pack the file in base64. Good for small files.

POST /someurl/document

{ 
   'date':'2007-03-01T13:00:00Z',
   'comment':'....',
   'file-data':'SGVsbG8gd29ybGRIZWxsbyB3b3JsZEhlbGxvIHdvcmxkSGVsbG8gd29ybGRIZWxsbyB3b3JsZEhlbGxvIHdvcmxkSGVsbG8gd29ybGRIZWxsbyB3b3JsZEhlbGxvIHdvcmxk'  
}

Option 4

We use a multipart request. The description in json and the file itself are two parts.

POST /someurl/document

Content-Type: multipart/form-data; boundary=spacer


--spacer
Content-Disposition: form-data; name="metadata"
Content-Type: application/json

{ 
   'date':'2007-03-01T13:00:00Z',
   'comment':'....'
}
--spacer
Content-Disposition: form-data; name="file-data"
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64

<...байты в base64...>

I am impressed by the 2nd option, as the most ideologically correct.

Scroll to Top