зеркало из https://github.com/Azure/ms-rest-js.git
Move response body parsing to serializationPolicy
This commit is contained in:
Родитель
f98bfdc3b3
Коммит
547521027d
|
@ -4,7 +4,6 @@
|
|||
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";
|
||||
import * as FormData from "form-data";
|
||||
import * as tough from "isomorphic-tough-cookie";
|
||||
import * as xml2js from "isomorphic-xml2js";
|
||||
import { HttpClient } from "./httpClient";
|
||||
import { HttpHeaders } from "./httpHeaders";
|
||||
import { HttpOperationResponse } from "./httpOperationResponse";
|
||||
|
@ -149,8 +148,10 @@ export class AxiosHttpClient implements HttpClient {
|
|||
request: httpRequest,
|
||||
status: res.status,
|
||||
headers,
|
||||
|
||||
readableStreamBody: httpRequest.rawResponse && isNode ? res.data as any : undefined,
|
||||
blobBody: !httpRequest.rawResponse || isNode ? undefined : () => res.data
|
||||
blobBody: !httpRequest.rawResponse || isNode ? undefined : () => res.data,
|
||||
bodyAsText: httpRequest.rawResponse ? undefined : res.data
|
||||
};
|
||||
|
||||
if (this.cookieJar) {
|
||||
|
@ -168,50 +169,6 @@ export class AxiosHttpClient implements HttpClient {
|
|||
}
|
||||
}
|
||||
|
||||
if (!httpRequest.rawResponse) {
|
||||
try {
|
||||
operationResponse.bodyAsText = res.data;
|
||||
} catch (err) {
|
||||
const msg = `Error "${err}" occured while converting the raw response body into string.`;
|
||||
const errCode = err.code || "RAWTEXT_CONVERSION_ERROR";
|
||||
const e = new RestError(msg, errCode, res.status, httpRequest, operationResponse, res.data);
|
||||
return Promise.reject(e);
|
||||
}
|
||||
|
||||
try {
|
||||
if (operationResponse.bodyAsText) {
|
||||
const contentType = operationResponse.headers.get("Content-Type") || "";
|
||||
const contentComponents = contentType.split(";").map(component => component.toLowerCase());
|
||||
if (contentComponents.some(component => component === "application/xml" || component === "text/xml")) {
|
||||
const xmlParser = new xml2js.Parser(XML2JS_PARSER_OPTS);
|
||||
const parseString = new Promise(function (resolve: (result: any) => void, reject: (err: any) => void) {
|
||||
xmlParser.parseString(operationResponse.bodyAsText!, function (err: any, result: any) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
operationResponse.parsedBody = await parseString;
|
||||
} else if (contentComponents.some(component => component === "application/json" || component === "text/json") || !contentType) {
|
||||
operationResponse.parsedBody = JSON.parse(operationResponse.bodyAsText);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
const msg = `Error "${err}" occured while executing JSON.parse on the response body - ${operationResponse.bodyAsText}.`;
|
||||
const errCode = err.code || "JSON_PARSE_ERROR";
|
||||
const e = new RestError(msg, errCode, res.status, httpRequest, operationResponse, operationResponse.bodyAsText);
|
||||
return Promise.reject(e);
|
||||
}
|
||||
}
|
||||
return Promise.resolve(operationResponse);
|
||||
}
|
||||
}
|
||||
|
||||
const XML2JS_PARSER_OPTS: xml2js.OptionsV2 = {
|
||||
explicitArray: false,
|
||||
explicitCharkey: false,
|
||||
explicitRoot: false
|
||||
};
|
|
@ -10,6 +10,7 @@ import { Mapper, MapperType } from "../serializer";
|
|||
import * as utils from "../util/utils";
|
||||
import { WebResource } from "../webResource";
|
||||
import { BaseRequestPolicy, RequestPolicy, RequestPolicyCreator, RequestPolicyOptions } from "./requestPolicy";
|
||||
import * as xml2js from "isomorphic-xml2js";
|
||||
|
||||
/**
|
||||
* Create a new serialization RequestPolicyCreator that will serialized HTTP request bodies as they
|
||||
|
@ -30,50 +31,45 @@ export class SerializationPolicy extends BaseRequestPolicy {
|
|||
}
|
||||
|
||||
public async sendRequest(request: WebResource): Promise<HttpOperationResponse> {
|
||||
let result: Promise<HttpOperationResponse>;
|
||||
serializeRequestBody(request);
|
||||
return this._nextPolicy.sendRequest(request).then(operationResponse => deserializeResponseBody(operationResponse));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the provided HTTP request's body based on the requestBodyMapper assigned to the HTTP
|
||||
* request.
|
||||
* @param {WebResource} request - The HTTP request that will have its body serialized.
|
||||
*/
|
||||
function serializeRequestBody(request: WebResource): void {
|
||||
const operationSpec: OperationSpec | undefined = request.operationSpec;
|
||||
if (operationSpec && operationSpec.requestBody && operationSpec.requestBody.mapper) {
|
||||
const bodyMapper = operationSpec.requestBody.mapper;
|
||||
const { required, xmlName, xmlElementName, serializedName } = bodyMapper;
|
||||
const typeName = bodyMapper.type.name;
|
||||
try {
|
||||
this.serializeRequestBody(request);
|
||||
const operationResponse: HttpOperationResponse = await this._nextPolicy.sendRequest(request);
|
||||
result = this.deserializeResponseBody(operationResponse);
|
||||
} catch (error) {
|
||||
result = Promise.reject(error);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize the provided HTTP request's body based on the requestBodyMapper assigned to the HTTP
|
||||
* request.
|
||||
* @param {WebResource} request - The HTTP request that will have its body serialized.
|
||||
*/
|
||||
public serializeRequestBody(request: WebResource): void {
|
||||
const operationSpec: OperationSpec | undefined = request.operationSpec;
|
||||
if (operationSpec && operationSpec.requestBody && operationSpec.requestBody.mapper) {
|
||||
const bodyMapper = operationSpec.requestBody.mapper;
|
||||
const { required, xmlName, xmlElementName, serializedName } = bodyMapper;
|
||||
const typeName = bodyMapper.type.name;
|
||||
try {
|
||||
if (request.body != undefined || required) {
|
||||
const requestBodyParameterPathString: string = getPathStringFromParameter(operationSpec.requestBody);
|
||||
request.body = operationSpec.serializer.serialize(bodyMapper, request.body, requestBodyParameterPathString);
|
||||
if (operationSpec.isXML) {
|
||||
if (typeName === MapperType.Sequence) {
|
||||
request.body = utils.stringifyXML(utils.prepareXMLRootList(request.body, xmlElementName || xmlName || serializedName), { rootName: xmlName || serializedName });
|
||||
}
|
||||
else {
|
||||
request.body = utils.stringifyXML(request.body, { rootName: xmlName || serializedName });
|
||||
}
|
||||
} else if (typeName !== MapperType.Stream) {
|
||||
request.body = JSON.stringify(request.body);
|
||||
if (request.body != undefined || required) {
|
||||
const requestBodyParameterPathString: string = getPathStringFromParameter(operationSpec.requestBody);
|
||||
request.body = operationSpec.serializer.serialize(bodyMapper, request.body, requestBodyParameterPathString);
|
||||
if (operationSpec.isXML) {
|
||||
if (typeName === MapperType.Sequence) {
|
||||
request.body = utils.stringifyXML(utils.prepareXMLRootList(request.body, xmlElementName || xmlName || serializedName), { rootName: xmlName || serializedName });
|
||||
}
|
||||
else {
|
||||
request.body = utils.stringifyXML(request.body, { rootName: xmlName || serializedName });
|
||||
}
|
||||
} else if (typeName !== MapperType.Stream) {
|
||||
request.body = JSON.stringify(request.body);
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Error "${error.message}" occurred in serializing the payload - ${JSON.stringify(serializedName, undefined, " ")}.`);
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(`Error "${error.message}" occurred in serializing the payload - ${JSON.stringify(serializedName, undefined, " ")}.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public deserializeResponseBody(response: HttpOperationResponse): Promise<HttpOperationResponse> {
|
||||
function deserializeResponseBody(response: HttpOperationResponse): Promise<HttpOperationResponse> {
|
||||
return parse(response).then(response => {
|
||||
const operationSpec: OperationSpec | undefined = response.request.operationSpec;
|
||||
if (operationSpec && operationSpec.responses) {
|
||||
const statusCode: number = response.status;
|
||||
|
@ -143,7 +139,7 @@ export class SerializationPolicy extends BaseRequestPolicy {
|
|||
}
|
||||
}
|
||||
return Promise.resolve(response);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function isStreamOperation(responseSpecs: { [statusCode: string]: OperationResponse }): boolean {
|
||||
|
@ -157,3 +153,42 @@ function isStreamOperation(responseSpecs: { [statusCode: string]: OperationRespo
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function parse(operationResponse: HttpOperationResponse): Promise<HttpOperationResponse> {
|
||||
const errorHandler = (err: any) => {
|
||||
const msg = `Error "${err}" occurred while parsing the response body - ${operationResponse.bodyAsText}.`;
|
||||
const errCode = err.code || "PARSE_ERROR";
|
||||
const e = new RestError(msg, errCode, operationResponse.status, operationResponse.request, operationResponse, operationResponse.bodyAsText);
|
||||
return Promise.reject(e);
|
||||
};
|
||||
|
||||
if (!operationResponse.request.rawResponse && operationResponse.bodyAsText) {
|
||||
const text = operationResponse.bodyAsText;
|
||||
const contentType = operationResponse.headers.get("Content-Type") || "";
|
||||
const contentComponents = contentType.split(";").map(component => component.toLowerCase());
|
||||
if (contentComponents.some(component => component === "application/xml" || component === "text/xml")) {
|
||||
const xmlParser = new xml2js.Parser({
|
||||
explicitArray: false,
|
||||
explicitCharkey: false,
|
||||
explicitRoot: false
|
||||
});
|
||||
return new Promise<HttpOperationResponse>(function (resolve, reject) {
|
||||
xmlParser.parseString(text, function (err: any, result: any) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
operationResponse.parsedBody = result;
|
||||
resolve(operationResponse);
|
||||
}
|
||||
});
|
||||
}).catch(errorHandler);
|
||||
} else if (contentComponents.some(component => component === "application/json" || component === "text/json") || !contentType) {
|
||||
return new Promise<HttpOperationResponse>(resolve => {
|
||||
operationResponse.parsedBody = JSON.parse(text);
|
||||
resolve(operationResponse);
|
||||
}).catch(errorHandler);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve(operationResponse);
|
||||
}
|
||||
|
|
|
@ -177,32 +177,4 @@ describe("axiosHttpClient", () => {
|
|||
assert(uploadNotified);
|
||||
assert(downloadNotified);
|
||||
});
|
||||
|
||||
it("should parse a JSON response body", async function() {
|
||||
const request = new WebResource(`${baseURL}/json`);
|
||||
const client = new AxiosHttpClient();
|
||||
const response = await client.sendRequest(request);
|
||||
assert.deepStrictEqual(response.parsedBody, [123,456,789]);
|
||||
});
|
||||
|
||||
it("should parse a JSON response body with a charset specified in Content-Type", async function() {
|
||||
const request = new WebResource(`${baseURL}/json-charset`);
|
||||
const client = new AxiosHttpClient();
|
||||
const response = await client.sendRequest(request);
|
||||
assert.deepStrictEqual(response.parsedBody, [123,456,789]);
|
||||
});
|
||||
|
||||
it("should parse a JSON response body with an uppercase Content-Type", async function() {
|
||||
const request = new WebResource(`${baseURL}/json-uppercase-content-type`);
|
||||
const client = new AxiosHttpClient();
|
||||
const response = await client.sendRequest(request);
|
||||
assert.deepStrictEqual(response.parsedBody, [123,456,789]);
|
||||
});
|
||||
|
||||
it("should parse a JSON response body with a missing Content-Type", async function() {
|
||||
const request = new WebResource(`${baseURL}/json-no-content-type`);
|
||||
const client = new AxiosHttpClient();
|
||||
const response = await client.sendRequest(request);
|
||||
assert.deepStrictEqual(response.parsedBody, [123,456,789]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,8 +5,9 @@ import * as assert from "assert";
|
|||
import { HttpHeaders } from "../../../lib/httpHeaders";
|
||||
import { HttpOperationResponse } from "../../../lib/httpOperationResponse";
|
||||
import { RequestPolicy, RequestPolicyOptions } from "../../../lib/policies/requestPolicy";
|
||||
import { SerializationPolicy } from "../../../lib/policies/serializationPolicy";
|
||||
import { SerializationPolicy, serializationPolicy } from "../../../lib/policies/serializationPolicy";
|
||||
import { WebResource } from "../../../lib/webResource";
|
||||
import { HttpClient } from '../../../lib/msRest';
|
||||
|
||||
describe("serializationPolicy", () => {
|
||||
const mockPolicy: RequestPolicy = {
|
||||
|
@ -28,4 +29,68 @@ describe("serializationPolicy", () => {
|
|||
await serializationPolicy.sendRequest(request);
|
||||
assert.strictEqual(request.body, "hello there!");
|
||||
});
|
||||
|
||||
it("should parse a JSON response body", async function() {
|
||||
const request = new WebResource();
|
||||
const mockClient: HttpClient = {
|
||||
sendRequest: req => Promise.resolve({
|
||||
request: req,
|
||||
status: 200,
|
||||
headers: new HttpHeaders({ "Content-Type": "application/json" }),
|
||||
bodyAsText: "[123, 456, 789]"
|
||||
})
|
||||
};
|
||||
|
||||
const policy = serializationPolicy()(mockClient, new RequestPolicyOptions());
|
||||
const response = await policy.sendRequest(request);
|
||||
assert.deepStrictEqual(response.parsedBody, [123,456,789]);
|
||||
});
|
||||
|
||||
it("should parse a JSON response body with a charset specified in Content-Type", async function() {
|
||||
const request = new WebResource();
|
||||
const mockClient: HttpClient = {
|
||||
sendRequest: req => Promise.resolve({
|
||||
request: req,
|
||||
status: 200,
|
||||
headers: new HttpHeaders({ "Content-Type": "application/json;charset=UTF-8" }),
|
||||
bodyAsText: "[123, 456, 789]"
|
||||
})
|
||||
};
|
||||
|
||||
const policy = serializationPolicy()(mockClient, new RequestPolicyOptions());
|
||||
const response = await policy.sendRequest(request);
|
||||
assert.deepStrictEqual(response.parsedBody, [123,456,789]);
|
||||
});
|
||||
|
||||
it("should parse a JSON response body with an uppercase Content-Type", async function() {
|
||||
const request = new WebResource();
|
||||
const mockClient: HttpClient = {
|
||||
sendRequest: req => Promise.resolve({
|
||||
request: req,
|
||||
status: 200,
|
||||
headers: new HttpHeaders({ "Content-Type": "APPLICATION/JSON" }),
|
||||
bodyAsText: "[123, 456, 789]"
|
||||
})
|
||||
};
|
||||
|
||||
const policy = serializationPolicy()(mockClient, new RequestPolicyOptions());
|
||||
const response = await policy.sendRequest(request);
|
||||
assert.deepStrictEqual(response.parsedBody, [123,456,789]);
|
||||
});
|
||||
|
||||
it("should parse a JSON response body with a missing Content-Type", async function() {
|
||||
const request = new WebResource();
|
||||
const mockClient: HttpClient = {
|
||||
sendRequest: req => Promise.resolve({
|
||||
request: req,
|
||||
status: 200,
|
||||
headers: new HttpHeaders(),
|
||||
bodyAsText: "[123, 456, 789]"
|
||||
})
|
||||
};
|
||||
|
||||
const policy = serializationPolicy()(mockClient, new RequestPolicyOptions());
|
||||
const response = await policy.sendRequest(request);
|
||||
assert.deepStrictEqual(response.parsedBody, [123,456,789]);
|
||||
});
|
||||
});
|
Загрузка…
Ссылка в новой задаче