问题
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.
回答1:
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:
- The
Content-Type
andContent-Encoding
headers need to be set correctly for a zipped byte array. - the use of the
.buffer
property on thecompressedBody
object.HttpClient
needs it this way so that it knows it is dealing with a byte array. - Your API will need to be smart enough to convert the byte array to json on the other end. This usually isn't handled by default.
回答2:
You don't have to compress anything yourself, just set appropriate headers, browser does this automatically using negotiation process with server, if server supports gzip encoding then browser will send encoded request.
来源:https://stackoverflow.com/questions/51942451/compress-outgoing-requests-in-angular-2