X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/a6633642232efd164d4708967ab59e498fbff896..refs/pull/4191/head:/resources/js/services/http.js diff --git a/resources/js/services/http.js b/resources/js/services/http.js index b05dd23bf..d0d33e317 100644 --- a/resources/js/services/http.js +++ b/resources/js/services/http.js @@ -1,90 +1,48 @@ - /** - * Perform a HTTP GET request. - * Can easily pass query parameters as the second parameter. - * @param {String} url - * @param {Object} params - * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>} + * @typedef FormattedResponse + * @property {Headers} headers + * @property {Response} original + * @property {Object|String} data + * @property {Boolean} redirected + * @property {Number} status + * @property {string} statusText + * @property {string} url */ -async function get(url, params = {}) { - return request(url, { - method: 'GET', - params, - }); -} /** - * Perform a HTTP POST request. - * @param {String} url - * @param {Object} data - * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>} + * Get the content from a fetch response. + * Checks the content-type header to determine the format. + * @param {Response} response + * @returns {Promise} */ -async function post(url, data = null) { - return dataRequest('POST', url, data); -} +async function getResponseContent(response) { + if (response.status === 204) { + return null; + } -/** - * Perform a HTTP PUT request. - * @param {String} url - * @param {Object} data - * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>} - */ -async function put(url, data = null) { - return dataRequest('PUT', url, data); -} + const responseContentType = response.headers.get('Content-Type') || ''; + const subType = responseContentType.split(';')[0].split('/').pop(); -/** - * Perform a HTTP PATCH request. - * @param {String} url - * @param {Object} data - * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>} - */ -async function patch(url, data = null) { - return dataRequest('PATCH', url, data); -} + if (subType === 'javascript' || subType === 'json') { + return response.json(); + } -/** - * Perform a HTTP DELETE request. - * @param {String} url - * @param {Object} data - * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>} - */ -async function performDelete(url, data = null) { - return dataRequest('DELETE', url, data); + return response.text(); } -/** - * Perform a HTTP request to the back-end that includes data in the body. - * Parses the body to JSON if an object, setting the correct headers. - * @param {String} method - * @param {String} url - * @param {Object} data - * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>} - */ -async function dataRequest(method, url, data = null) { - const options = { - method: method, - body: data, - }; - - // Send data as JSON if a plain object - if (typeof data === 'object' && !(data instanceof FormData)) { - options.headers = { - 'Content-Type': 'application/json', - 'X-Requested-With': 'XMLHttpRequest', - }; - options.body = JSON.stringify(data); +export class HttpError extends Error { + + constructor(response, content) { + super(response.statusText); + this.data = content; + this.headers = response.headers; + this.redirected = response.redirected; + this.status = response.status; + this.statusText = response.statusText; + this.url = response.url; + this.original = response; } - // Ensure FormData instances are sent over POST - // Since Laravel does not read multipart/form-data from other types - // of request. Hence the addition of the magic _method value. - if (data instanceof FormData && method !== 'post') { - data.append('_method', method); - options.method = 'post'; - } - - return request(url, options) } /** @@ -92,34 +50,35 @@ async function dataRequest(method, url, data = null) { * to communicate with the back-end. Parses & formats the response. * @param {String} url * @param {Object} options - * @returns {Promise<{headers: Headers, original: Response, data: (Object|String), redirected: boolean, statusText: string, url: string, status: number}>} + * @returns {Promise} */ async function request(url, options = {}) { - if (!url.startsWith('http')) { - url = window.baseUrl(url); + let requestUrl = url; + + if (!requestUrl.startsWith('http')) { + requestUrl = window.baseUrl(requestUrl); } if (options.params) { - const urlObj = new URL(url); - for (let paramName of Object.keys(options.params)) { + const urlObj = new URL(requestUrl); + for (const paramName of Object.keys(options.params)) { const value = options.params[paramName]; if (typeof value !== 'undefined' && value !== null) { urlObj.searchParams.set(paramName, value); } } - url = urlObj.toString(); + requestUrl = urlObj.toString(); } const csrfToken = document.querySelector('meta[name=token]').getAttribute('content'); - options = Object.assign({}, options, { - 'credentials': 'same-origin', - }); - options.headers = Object.assign({}, options.headers || {}, { - 'baseURL': window.baseUrl(''), + const requestOptions = {...options, credentials: 'same-origin'}; + requestOptions.headers = { + ...requestOptions.headers || {}, + baseURL: window.baseUrl(''), 'X-CSRF-TOKEN': csrfToken, - }); + }; - const response = await fetch(url, options); + const response = await fetch(requestUrl, requestOptions); const content = await getResponseContent(response); const returnData = { data: content, @@ -132,37 +91,98 @@ async function request(url, options = {}) { }; if (!response.ok) { - throw returnData; + throw new HttpError(response, content); } return returnData; } /** - * Get the content from a fetch response. - * Checks the content-type header to determine the format. - * @param {Response} response - * @returns {Promise} + * Perform a HTTP request to the back-end that includes data in the body. + * Parses the body to JSON if an object, setting the correct headers. + * @param {String} method + * @param {String} url + * @param {Object} data + * @returns {Promise} */ -async function getResponseContent(response) { - if (response.status === 204) { - return null; - } +async function dataRequest(method, url, data = null) { + const options = { + method, + body: data, + }; - const responseContentType = response.headers.get('Content-Type'); - const subType = responseContentType.split('/').pop(); + // Send data as JSON if a plain object + if (typeof data === 'object' && !(data instanceof FormData)) { + options.headers = { + 'Content-Type': 'application/json', + 'X-Requested-With': 'XMLHttpRequest', + }; + options.body = JSON.stringify(data); + } - if (subType === 'javascript' || subType === 'json') { - return await response.json(); + // Ensure FormData instances are sent over POST + // Since Laravel does not read multipart/form-data from other types + // of request. Hence the addition of the magic _method value. + if (data instanceof FormData && method !== 'post') { + data.append('_method', method); + options.method = 'post'; } - return await response.text(); + return request(url, options); +} + +/** + * Perform a HTTP GET request. + * Can easily pass query parameters as the second parameter. + * @param {String} url + * @param {Object} params + * @returns {Promise} + */ +export async function get(url, params = {}) { + return request(url, { + method: 'GET', + params, + }); +} + +/** + * Perform a HTTP POST request. + * @param {String} url + * @param {Object} data + * @returns {Promise} + */ +export async function post(url, data = null) { + return dataRequest('POST', url, data); +} + +/** + * Perform a HTTP PUT request. + * @param {String} url + * @param {Object} data + * @returns {Promise} + */ +export async function put(url, data = null) { + return dataRequest('PUT', url, data); +} + +/** + * Perform a HTTP PATCH request. + * @param {String} url + * @param {Object} data + * @returns {Promise} + */ +export async function patch(url, data = null) { + return dataRequest('PATCH', url, data); +} + +/** + * Perform a HTTP DELETE request. + * @param {String} url + * @param {Object} data + * @returns {Promise} + */ +async function performDelete(url, data = null) { + return dataRequest('DELETE', url, data); } -export default { - get: get, - post: post, - put: put, - patch: patch, - delete: performDelete, -}; \ No newline at end of file +export {performDelete as delete};