I want to use gzip or deflate compression on outgoing POST and PUT JSON requests to an API project from an Angular 4 application.
Presently, I'm using HttpClient to send the requests. I've tried using pako or zlib to generate the compressed content, but the server returns back responses indicating a bad implementation of the compression algorithm.
My POST TypeScript looks like the following:
public post(url: string, content: any): Observable < any > {
const fullUrl: string = `${HttpService.baseUrl}/${url}`;
Logger.debug(`Beginning HttpPost invoke to ${fullUrl}`, content);
// Optionally, deflate the input
const toSend: any = HttpService.compressInputIfNeeded(content);
return Observable.create((obs: Observer < any > ) => {
this.client.post(fullUrl, toSend, HttpService.getClientOptions()).subscribe(
(r: any) => {
Logger.debug(`HttpPost operation to ${fullUrl} completed`, r);
// Send the response along to the invoker
obs.next(r);
obs.complete();
},
(err: any) => {
Logger.error(`Error on HttpPost invoke to ${fullUrl}`, err);
// Pass the error along to the client observer
obs.error(err);
}
);
});
}
private static getClientOptions(): {
headers: HttpHeaders
} {
return {
headers: HttpService.getContentHeaders()
};
}
private static getContentHeaders(): HttpHeaders {
let headers: HttpHeaders = new HttpHeaders({
'Content-Type': 'application/json; charset=utf-8'
});
// Headers are immutable, so any set operation needs to set our reference
if (HttpService.deflate) {
headers = headers.set('Content-Encoding', 'deflate');
}
if (HttpService.gzip) {
headers = headers.set('Content-Encoding', 'gzip');
}
return headers;
}
private static compressInputIfNeeded(content: any): string {
const json: string = JSON.stringify(content);
Logger.debug('Pako Content', pako);
if (HttpService.deflate) {
const deflated: string = pako.deflate(json);
Logger.debug(`Deflated content`, deflated);
return deflated;
}
if (HttpService.gzip) {
const zipped: string = pako.gzip(json);
Logger.debug(`Zipped content`, zipped);
return zipped;
}
return json;
}
I've tried various permutations of deflating and gzipping the content, but nothing seems to work. I've also inspected the outgoing requests in Fiddler and verified that Fiddler could not interpret the request JSON.
I've also verified that content is being sent with Content-Type: application/json; charset=UTF-8 and Content-Encoding: deflate with appropriate Accept-Encoding values.
At this point I'm sure I'm either doing something wrong I haven't figured out, or that I'm trying to do more than what HttpClient will allow me to do.
I've just gotten this working myself.
I think the problem may be the way you are using pako
.
pako.gzip(obj)
does not return a string, unless you explicitly pass that option. It returns a byte array. (Uint8Array
, specifically)
The default HttpClient
will try to turn this into a json string, which is wrong. I did the following:
const newHeaders: Headers = new Headers();
newHeaders.append('Content-Encoding', 'gzip')
newHeaders.set('Content-Type', 'application/octet-stream');
var options = { headers: newHeaders, responseType: ResponseContentType.Json };
var compressedBody = pako.gzip(JSON.stringify(body))
client.post(url, compressedBody.buffer, options);
Note a few things:
Content-Type
and Content-Encoding
headers need to be set correctly for a zipped byte array..buffer
property on the compressedBody
object. HttpClient
needs it this way so that it knows it is dealing with a byte array.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With