Multiple response types in a single $http call [duplicate] - angularjs

I have an Angular 1.x application that is expecting to receive a binary file download (pdf) using a $http.post() call. The problem is, I'd like to alternatively get a processing error message that's sent as json. I can do this with the config
headers: {
'Accept': 'application/pdf, application/json'
}
The problem is I have have to set responseType: 'arraybuffer', otherwise the pdf binary is escaped (or altered such that it doesn't load). However, that prevents the json from being read or interpreted correctly.
How can I have both?
Edit: I'm going to try to clarify; perhaps my understanding is incorrect.
$http({
method: 'POST',
url: "/myresource",
headers: {
'Accept': 'application/pdf, application/json'
},
responseType: 'arraybuffer'
})
.then(
function(response) {
// handle pdf download via `new Blob([data])`
}, function(response) {
// pop up a message based on response.data
}
)
In a scenario where I return a pdf data block and a http status of 200, the first function handles the response and prompts the user to save the file. However if the status is an error (422), the response.data is undefined. I assume this is because the responseType is preventing the json from being handled correctly.
If I remove the responseType line, the error data is correctly read, but when the pdf is saved, some of the file bytes aren't correct and it's effectively corrupted. I assume this is because the file is being encoded because javascript was expecting a string.

An XHR responseType property can not be changed after a response has been loaded. But an arraybuffer can be decoded and parsed depending on Content-Type:
var config = {
responseType: "arraybuffer",
transformResponse: jsonBufferToObject,
};
function jsonBufferToObject (data, headersGetter, status) {
var type = headersGetter("Content-Type");
if (!type.startsWith("application/json")) {
return data;
};
var decoder = new TextDecoder("utf-8");
var domString = decoder.decode(data);
var json = JSON.parse(domString);
return json;
};
$http.get(url, config);
The above example sets the XHR to return an arraybuffer and uses a transformResponse function to detect Content-Type: application/json and convert it if necessary.
The DEMO on PLNKR

Related

Using angular js post to django back end yields a 414 error

var reader = new FileReader();
reader.onload = function(){
var fileData = reader.result;
$http({
url: 'rs/submit',
method: 'POST',
data: {
'img': fileData,
'query': query,
'limit': limit
}
})
.then(function successCallback(response) {
}, function errorCallback(response) {
});
};
reader.readAsDataURL(document.getElementById('submitFileInput').files[0]);
I'm trying to send a image to my django back end using angular js as you can see above, but I get a error in the back end that says:
[22/Dec/2018 12:59:34] "POST /dynamic-mosaic/rs/submit HTTP/1.1" 200 4
[22/Dec/2018 12:59:34] code 414, message Request-URI Too Long
[22/Dec/2018 12:59:34] "" 414 -
I thought POST can send requests of almost any size, the image isnt even that big like 1-2MB
Anyone knows the cause? Maybe I'm not using angular js $http service properly.
Saving Blob type is a bit different
var fd = new FormData();
fd.append('img', reader.result);
fd.append('query', query);
fd.append('limit', limit);
$http.post('rs/submit', fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
}).then(...)
Probably you should also consider similar answer
UPD: here is an external resource for your consideration
Angular’s default transformRequest function will try to serialize
our FormData object, so we override it with the identity function to
leave the data intact.
Angular’s default Content-Type header for POST
and PUT requests is application/json, so we want to change this, too.
By setting ‘Content-Type’: undefined, the browser sets the
Content-Type to multipart/form-data for us and fills in the correct
boundary. Manually setting ‘Content-Type’: multipart/form-data will
fail to fill in the boundary parameter of the request.

Can AngularJS set the responseType after a response has been received?

I have an Angular 1.x application that is expecting to receive a binary file download (pdf) using a $http.post() call. The problem is, I'd like to alternatively get a processing error message that's sent as json. I can do this with the config
headers: {
'Accept': 'application/pdf, application/json'
}
The problem is I have have to set responseType: 'arraybuffer', otherwise the pdf binary is escaped (or altered such that it doesn't load). However, that prevents the json from being read or interpreted correctly.
How can I have both?
Edit: I'm going to try to clarify; perhaps my understanding is incorrect.
$http({
method: 'POST',
url: "/myresource",
headers: {
'Accept': 'application/pdf, application/json'
},
responseType: 'arraybuffer'
})
.then(
function(response) {
// handle pdf download via `new Blob([data])`
}, function(response) {
// pop up a message based on response.data
}
)
In a scenario where I return a pdf data block and a http status of 200, the first function handles the response and prompts the user to save the file. However if the status is an error (422), the response.data is undefined. I assume this is because the responseType is preventing the json from being handled correctly.
If I remove the responseType line, the error data is correctly read, but when the pdf is saved, some of the file bytes aren't correct and it's effectively corrupted. I assume this is because the file is being encoded because javascript was expecting a string.
An XHR responseType property can not be changed after a response has been loaded. But an arraybuffer can be decoded and parsed depending on Content-Type:
var config = {
responseType: "arraybuffer",
transformResponse: jsonBufferToObject,
};
function jsonBufferToObject (data, headersGetter, status) {
var type = headersGetter("Content-Type");
if (!type.startsWith("application/json")) {
return data;
};
var decoder = new TextDecoder("utf-8");
var domString = decoder.decode(data);
var json = JSON.parse(domString);
return json;
};
$http.get(url, config);
The above example sets the XHR to return an arraybuffer and uses a transformResponse function to detect Content-Type: application/json and convert it if necessary.
The DEMO on PLNKR

$http post multipart/form-data boundary not set

I am setting the boundary for the below post call but on chrome the boundary looks different from the one I set. how do I get my custom boundary "--test" to show up on the request payload?
var url = '/site/apkUpload';
var deferred = $q.defer();
console.log(formdata);
$http.post(url, formdata, {
processData: false,
headers: {'Content-Type': "multipart/form-data; charset=utf-8; boundary='--test'",
'Accept': "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
'x-access-token': token,
'cache-control': 'max-age=0'}
})
.success(function (response) {
deferred.resolve(response);
})
.error(function (reject) {
deferred.reject(reject);
});
return deferred.promise;
Request payload on chrome:
------WebKitFormBoundaryB5LjM2a6Qly3Xruj
Content-Disposition: form-data; name="packageName"
helo1
------WebKitFormBoundaryB5LjM2a6Qly3Xruj
Content-Disposition: form-data; name="name"
......
Thanks a lot!
I can interpret your question in 2 ways:
You are talking about why you are not getting ------WebKitFormBoundaryB5LjM2a6Qly3Xruj in the POST request header, which you are getting in the request payload. I had the same issue while sending a multi-part formdata using $http.post (also I was using FormData).The solution to this is using $http(config).To my understanding $http.post underlying uses $http() itself to generate XMLHttpRequest object, which should run the multipart/form-data encoding algorithm to set the multipart boundary in payload and the header as well. In $http.post, it seems whenever you give a custom config object it overwrites the header generated by the algorithm. This answer is also helpful.
If you just want to add a custom multipart boundary in the content-header, then you can achieve that by add a tranformRequest function in the config object:
var url = '/site/apkUpload';
var deferred = $q.defer();
console.log(formdata);
$http.post(url, formdata, {
processData: false,
transformRequest: function (data, headers) {
var boundary = yourCustomLogic();
headers()['Content-Type'] = 'multipart/form-data;charset=utf-
8;boundary=' + boundary;
},
'Accept': ...,
'x-access-token': token,
'cache-control': 'max-age=0'
})
.success( function () {...});

What is type of data angular sending?

What is type of data angular sending? I use laravel + angular. I`m trying, but this script return 405 error. Method not allowed.
.controller('adminCtrl', function( $scope, $http ){
$scope.collection = [];
$scope.newData = [];
$scope.newrecord = function() {
$scope.collection.push($scope.newData);
$http({
url: '/newrecord',
method: "POST",
data: $.param($scope.collection),
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}
}).success(function(data){
console.log(data);
})
}
})
You are getting 405 - Method not Allowed because the server you are sending your request does not have POST it the white list of methods allowed to be used to perform requests to that given API.
It's not an angularJS issue, it's a server configuration issue.
$http sends data as json.
You do not need to serialize params using "$.param", data is plain javascript object, which is send to your REST endpoint.
So attach just "$scope.collection) and do not set Content Type manually, it is json by default.
POST can be send also with convenience method.
$http.post('/someUrl', data, config).then(successCallback, errorCallback);

AngularJS $http POST request different results with then and success

Something is driving me nuts; maybe someone can help me out with the following? :
I am using AngularJS 1.2.26 (have to because IE8 needs to be supported)
I have to invoke some backend services that were initially build for a backbone frontend. I managed to do that in the following way:
$http({
method: 'POST',
url: url,
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
transformRequest: this._transformRequest,
data: formData
})
.success(function (data) {
// bla bla not relevant
}).error(function (error) {
// bla bla not relevant
});
Now i try to work with the then function as i find that more consequent, so i change the code into:
$http({
method: 'POST',
url: url,
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
transformRequest: this._transformRequest,
data: formData
}).then(
function (response) {
// not relevant
}, function (error) {
// not relevant
});
According to me, in theory this should have the same result as the initial implementation, however to my surprise the request now fails on the server. While debugging I noticed that the result of the transform request function delivers a very different result in both scenario's in the request that is handled with success and error the result of transform request is as follows:
com.bank.token.session=XXXXX&model=%7B%22relatieRol%22%3A%22AANVRAGER%22%2C%22evaUitgevoerdDat%22%3Anull%2C%22sfhUitgevoerdDat%22%3Anull%2C%22bkrUitgevoerdDat%22%3Anull%2C%22bkrBekendCd%22%3A%22GOED_BEKEND%22%7D
When i use the 'then' function as the way to handle the result the transformRequest function returns the following (wrong) result:
com.bank.token.session=XXXXXXXXXXX&model=%7B%22data%22%3A%7B%22relatieRol%22%3A%22AANVRAGER%22%2C%22evaUitgevoerdDat%22%3Anull%2C%22sfhUitgevoerdDat%22%3Anull%2C%22bkrUitgevoerdDat%22%3Anull%7D%2C%22status%22%3A200%2C%22config%22%3A%7B%22method%22%3A%22POST%22%2C%22transformResponse%22%3A%5Bnull%5D%2C%22url%22%3A%22http%3A%2F%2Flocalhost%2Femployee%2Findex.html%2Ffoo-web%2Fxchannel-foo-secure-portlet%2F1598792178%2Fver%3D2.0%2Fresource%2Fid%3Dfoo-fetch-%2Frparam%3Dportal%3DfooPortal.wsp%22%2C%22headers%22%3A%7B%22Content-Type%22%3A%22application%2Fx-www-form-urlencoded%22%2C%22Accept%22%3A%22application%2Fjson%2C%20text%2Fplain%2C%20*%2F*%22%7D%2C%22data%22%3A%7B%22com.bank.token.session%22%3A%22XXXXXXXXXX%22%2C%22model%22%3A%22%7B%5C%22relatieRol%5C%22%3A%5C%22AANVRAGER%5C%22%7D%22%7D%7D%2C%22statusText%22%3A%22OK%22%2C%22bkrBekendCd%22%3A%22GOED_BEKEND%22%7D
This really surprises me; how can the handler on the $http service influence the way the request is handled? I would like to use 'then' for handling my $http POST; but it seems not to work. Anybody knows why? Many thanks in advance!
my transformRequest function looks like this:
_transformRequest: function (obj) {
var str = [];
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
}
}
console.log('transform: ', str.join("&"));
return str.join("&");
}
success and error unpack the data property of the response for you (as well as route to the pretty names). So, if you change to then you need to manually address the data property of the response in order to get the same information:
$http({
method: 'POST',
url: url,
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
transformRequest: this._transformRequest,
data: formData
}).then(
function (response) {
var data = response.data;
// not relevant
}, function (error) {
var data = error.data;
// not relevant
});
Here is the relevant part in the $http documentation:
Returns a promise object with the standard then method and two http
specific methods: success and error. The then method takes two
arguments a success and an error callback which will be called with a
response object. The success and error methods take a single argument
- a function that will be called when the request succeeds or fails respectively. The arguments passed into these functions are
destructured representation of the response object passed into the
then method. The response object has these properties:
data – {string|Object} – The response body transformed with the transform functions.
status – {number} – HTTP status code of the response.
headers – {function([headerName])} - Header getter function.
config – {Object} – The configuration object that was used to generate the request.
statusText – {string} – HTTP status text of the response.
The $http legacy promise methods success and error have been deprecated. Use the standard then method instead.1

Resources