javascript/ievv_jsbase/http/HttpRequest.js
import HttpResponse from "./HttpResponse";
import QueryString from "./QueryString";
import {UrlParser} from "./UrlParser";
/**
* API for performing HTTP requests.
* @example <caption>Make a POST request</caption>
* const request = new HttpRequest('http://example.com/api/users/');
* request.post('Hello world').then((response) => {
* // Success - response is a HttpResponse object.
* console.log(response.toPrettyString());
* if(response.isSuccess()) {
* console.log('Success: ', response.body);
* } else if (response.isRedirect) {
* console.log('Hmm strange, we got a redirect instead of a 2xx response.');
* }
* }, (response) => {
* // Error - response is a HttpResponse object.
* console.error(response.toPrettyString());
* if(response.isRedirect()) {
* // Yes - redirect is treated as an error by default.
* // you can change this by supplying an extra argument
* // to HttpResponse()
* console.log('We got a 3xx response!', response.body);
* } else if(response.isClientError()) {
* console.log('We got a 4xx response!', response.body);
* } else if (response.isServerError()) {
* console.log('We got a 5xx response!', response.body);
* } else if (response.isConnectionRefused()) {
* console.log('Connection refused.');
* }
* });
*
* @example <caption>Make a GET request with a querystring</caption>
* const request = new HttpRequest('http://example.com/api/users/');
* request.urlParser.queryString.set('search', 'doe');
* request.get().then((response) => {
* // Success - response is a HttpResponse object.
* console.log(response.toPrettyString());
* }, (response) => {
* // Error - response is a HttpResponse object.
* console.error(response.toPrettyString());
* });
*/
export default class HttpRequest {
/**
* @param {string} url The URL to request.
* If this is supplied, it is passed to
* {@link HttpRequest#setUrl}
*/
constructor(url) {
this._treatRedirectResponseAsError = true;
this.request = new XMLHttpRequest();
this._urlParser = null;
if(typeof url !== 'undefined') {
this.setUrl(url);
}
}
/**
* Get the parsed URL of the request.
*
* @returns {UrlParser} The UrlParser for the parsed URL.
*/
get urlParser() {
return this._urlParser;
}
/**
* Set the URL of the request.
*
* @param {String} url The URL.
*/
setUrl(url) {
this._urlParser = new UrlParser(url);
}
/**
* Set how we treat 3xx responses.
*
* By default they are treated as errors, but you can change
* this behavior by calling this function.
*
* @param {bool} treatRedirectResponseAsError Treat 3xx responses as
* errors?
*
* @example <caption>Do not treat 3xx responses as error</caption>
* const request = HttpRequest('http://example.com/api/');
* request.setTreatRedirectResponseAsError(false);
*/
setTreatRedirectResponseAsError(treatRedirectResponseAsError) {
this._treatRedirectResponseAsError = treatRedirectResponseAsError;
}
/**
* Send the request.
*
* @param method The HTTP method. I.e.: "get", "post", ...
* @param data Request body data. This is sent through
* {@link HttpRequest#makeRequestBody} before it
* is sent.*
* @return A Promise where both the
*/
send(method, data) {
method = method.toUpperCase();
if(this._urlParser === null) {
throw new TypeError('Can not call send() without an url.');
}
return new Promise((resolve, reject) => {
this.request.open(method, this.urlParser.buildUrl(), true);
this.setDefaultRequestHeaders(method);
this.request.onload = () => this._onComplete(resolve, reject);
this.request.onerror = () => this._onComplete(resolve, reject);
this.request.send(this.makeRequestBody(data));
});
}
/**
* Shortcut for ``send("get", data)``.
*
* @see {@link HttpRequest#send}
*/
get(data) {
return this.send('get', data);
}
/**
* Shortcut for ``send("head", data)``.
*
* @see {@link HttpRequest#send}
*/
head(data) {
return this.send('head', data);
}
/**
* Shortcut for ``send("post", data)``.
*
* @see {@link HttpRequest#send}
*/
post(data) {
return this.send('post', data);
}
/**
* Shortcut for ``send("put", data)``.
*
* @see {@link HttpRequest#send}
*/
put(data) {
return this.send('put', data);
}
/**
* Shortcut for ``send("patch", data)``.
*
* @see {@link HttpRequest#send}
*/
patch(data) {
return this.send('patch', data);
}
/**
* Shortcut for ``send("delete", data)``.
*
* Named httpdelete to avoid crash with builtin keyword ``delete``.
*
* @see {@link HttpRequest#send}
*/
httpdelete(data) {
return this.send('delete', data);
}
/**
* Make request body from the provided data.
*
* By default this just returns the provided data,
* but subclasses can override this to perform automatic
* conversion.
*
* Must return a string.
*/
makeRequestBody(data) {
return data;
}
/**
* Creates a {@link HttpResponse}.
* @returns {HttpResponse}
*/
makeResponse() {
return new HttpResponse(this.request);
}
/**
* Set a request header.
*
* @param header The header name. E.g.: ``"Content-type"``.
* @param value The header value.
*/
setRequestHeader(header, value) {
this.request.setRequestHeader(header, value);
}
/**
* Set default request headers.
*
* Does nothing by default, but subclasses can override this.
*
* @param method The HTTP request method (GET, POST, PUT, ...).
* Will always be uppercase.
*/
setDefaultRequestHeaders(method) {}
_onComplete(resolve, reject) {
let response = this.makeResponse();
let isSuccess = false;
if(this._treatRedirectResponseAsError) {
isSuccess = response.isSuccess();
} else {
isSuccess = response.isSuccess() || response.isRedirect();
}
if(isSuccess) {
resolve(response);
} else {
reject(response);
}
}
}