Fix the loss of JSON types when using multipart/form-data

To pass a binary blob in a POST request, browser sets the header
'Content-Type: multipart/form-data', which in turn causes all form
fields' values to be passed as strings. Circumvent this by storing
original field values as a JSON string on client-side and decoding it
on server-side. As a result the setting HORIZON_IMAGES_UPLOAD_MODE =
'legacy' will start working together with Glance V2.

Closes-Bug: #1613703
Change-Id: I53a8fbba15e4c3c6c17d6ef1ffe701634efda149
This commit is contained in:
Timur Sufiev 2016-08-11 13:40:36 +03:00
parent a0f18e3444
commit f85d2fdbb7
4 changed files with 27 additions and 3 deletions

View File

@ -57,6 +57,9 @@ limitations under the License.
for (var key in config.data) {
if (config.data.hasOwnProperty(key) && uploadService.isFile(config.data[key])) {
backend = uploadService.upload;
// NOTE(tsufiev): keeping the original JSON to not lose value types
// after sending them all as strings via multipart/form-data
config.data.$$originalJSON = JSON.stringify(config.data);
break;
}
}

View File

@ -128,7 +128,10 @@
it('upload() is used when there is a File() blob inside data', function () {
api.post('/good', {first: file, second: 'the data'});
expect(Upload.upload).toHaveBeenCalled();
expect(called.config.data).toEqual({first: file, second: 'the data'});
var expected = {first: file, second: 'the data'};
expected.$$originalJSON = JSON.stringify(expected);
expect(called.config.data).toEqual(expected);
});
it('upload() is NOT used when a File() blob is passed as data', function () {

View File

@ -170,14 +170,15 @@ class Images(generic.View):
# note: not an AJAX request - the body will be raw file content mixed with
# metadata
@rest_utils.post2data
@csrf_exempt
def post(self, request):
form = UploadObjectForm(request.POST, request.FILES)
form = UploadObjectForm(request.DATA, request.FILES)
if not form.is_valid():
raise rest_utils.AjaxError(500, 'Invalid request')
data = form.clean()
meta = create_image_metadata(request.POST)
meta = create_image_metadata(request.DATA)
meta['data'] = data['data']
image = api.glance.image_create(request, **meta)

View File

@ -163,3 +163,20 @@ def parse_filters_kwargs(request, client_keywords=None):
else:
filters[param] = request.GET[param]
return filters, kwargs
def post2data(func):
"""The sole purpose of this decorator is to restore original form values
along with their types stored on client-side under key $$originalJSON.
This in turn prevents the loss of field types when they are passed with
header 'Content-Type: multipart/form-data', which is needed to pass a
binary blob as a part of POST request.
"""
def wrapper(self, request):
request.DATA = request.POST
if '$$originalJSON' in request.POST:
request.DATA = json.loads(request.POST['$$originalJSON'])
return func(self, request)
return wrapper