UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

233 lines (214 loc) 9.01 kB
/*! * OpenUI5 * (c) Copyright 2026 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ sap.ui.define([], function () { "use strict"; function parseHeaders(sAllResponseHeaders) { var result = new Headers(); if (typeof sAllResponseHeaders === "string") { sAllResponseHeaders.trim().split("\r\n").forEach(function (sHeader) { if (sHeader) { var pos = sHeader.indexOf(":"); if (pos > 0) { result.append(sHeader.slice(0, pos), sHeader.slice(pos + 1)); } else { result.append(sHeader, ""); } } }); } return result; } /** * Represents the response object to a {@link module:sap/base/util/fetch} request and {@link module:sap/base/util/syncFetch} request. * The implementation is based on the Response interface of the global <code>fetch()</code> method, * but brings a much reduced set of properties and methods. * * The properties that are provided: * <ul> * <li>The <code>headers</code> property containing the <code>Headers</code> object</li> * <li>The <code>ok</code> property containing a boolean stating whether the response was successful</li> * <li>The <code>status</code> property containing the HTTP status code</li> * <li>The <code>statusText</code> property containing an HTTP status message</li> * </ul> * * The methods that are provided: * <ul> * <li>The <code>json()</code> method returns a promise that resolves with the result of parsing the XHR response text as JSON</li> * <li>The <code>text()</code> method returns a promise that resolves with the XHR response text as String</li> * </ul> * * In case of a response to a synchronous <code>module:sap/base/util/syncFetch</code> request, * all methods will return the XHR response directly, according to the respective output format. * * * @param {XMLHttpRequest} xhr The XMLHttpRequest object * @param {Promise|sap.ui.base.SyncPromise} PromiseImpl A Promise for asynchronous requests, and * an <code>sap.ui.base.SyncPromise</code> for synchronous requests. * @interface * @alias module:sap/base/util/SimpleResponse * @private * @ui5-restricted SAPUI5 Distribution Layer Libraries */ function SimpleResponse(xhr, PromiseImpl) { var headers = parseHeaders(xhr.getAllResponseHeaders()); Object.defineProperties(this, { headers: { value: headers }, ok: { value: xhr.status >= 200 && xhr.status < 300 }, status: { value: xhr.status }, statusText: { value: xhr.statusText } }); this.json = function() { if (xhr.responseType === "json") { return PromiseImpl.resolve(xhr.response); } else { try { var oData = JSON.parse(xhr.responseText); return PromiseImpl.resolve(oData); } catch (err) { return PromiseImpl.reject(err); } } }; this.text = function() { return PromiseImpl.resolve(xhr.responseText); }; } // Allowed request credentials var ALLOWED_CREDENTIALS = ["include", "omit", "same-origin"]; /** * Performs an asynchronous XMLHttpRequest (XHR) with the provided resource URL and request settings. * It returns a Promise that resolves with an <code>module:sap/base/util/SimpleResponse</code> object, which is * a simplified implementation of the global Response interface, representing the response of the XHR. * * If the request encounters network failures, the returned promise will be rejected with a <code>TypeError</code>. * In case of an HTTP error status (e.g. error status 404), the returned promise will resolve instead. * The <code>response.ok</code> or <code>response.status</code> flags can be used to distinguish * a success status from an error status. * * The Promise will reject with a <code>DOMException</code> if the request gets aborted. * To abort a request, an instance of the global <code>AbortSignal</code> must be provided to the settings via property <code>init.signal</code>. * An abort signal can be created via an instance of the <code>AbortController</code>, and then using * the <code>AbortController.signal</code> property. The signal associates the abort controller with the request * and allows it to abort the XHR by calling <code>AbortController.abort()</code>. * * Although the usage of this method is very similar to the native <code>fetch()</code> method, * it allows a much reduced set of request settings (in the <code>init</code> argument). * * @param {string} resource A string containing the URL to which the request is sent * @param {object} [init] A set of key/value pairs that configure the request. * @param {any} [init.body] Any body that you want to add to your request: this can be a Blob, BufferSource, FormData, URLSearchParams, string, or ReadableStream object. * Note that a request using the GET or HEAD method cannot have a body. * @param {"omit"|"same-origin"|"include"} [init.credentials='same-origin'] Controls what browsers do with credentials. * Must be either 'omit', 'same-origin' or 'include'. * @param {Headers|object} [init.headers] A Headers object or an object with key/value pairs containing the request headers * @param {string} [init.method='GET'] The request method, e.g. 'GET', 'POST' * @param {AbortSignal} [init.signal] An AbortSignal object instance which allows to abort the request * @return {Promise<module:sap/base/util/SimpleResponse>} Returns a Promise resolving with a <code>SimpleResponse</code> * * @alias module:sap/base/util/fetch * @private * @ui5-restricted SAPUI5 Distribution Layer Libraries */ function fetch(resource, init, _mImplementations) { /** * see "https://developer.mozilla.org/en-US/docs/Web/API/Request/Request" * regarding default values */ init = Object.assign({ body: null, credentials: "same-origin", method: "GET", signal: new AbortController().signal // mode: "cors", // redirect: "follow", // referrer: "about:client" }, init); // "sap/base/util/syncFetch" might pass a SyncPromise implementation var PromiseImpl = (_mImplementations && _mImplementations.promiseImpl) || Promise; return new PromiseImpl(function(resolve, reject) { // check for credentials in the resource URL var oUrl = new URL(resource, document.baseURI); if (oUrl.username || oUrl.password) { reject(new TypeError("Failed to execute 'fetch': Request cannot be constructed from a URL that includes credentials:" + resource)); } // adding the missing protocol back to the URL string which is taken from the document.baseURI resource = resource.replace(/^\/\//, oUrl.protocol + "//"); if (init.body !== null && (init.method == "GET" || init.method == "HEAD")) { reject(new TypeError("Failed to execute 'fetch': Request with GET/HEAD method cannot have body.")); } var xhr = new XMLHttpRequest(); // event listener xhr.addEventListener("load", function() { var oResponse = new SimpleResponse(xhr, PromiseImpl); if (_mImplementations && _mImplementations.responseMixin) { _mImplementations.responseMixin.apply(oResponse); } resolve(oResponse); }); xhr.addEventListener("error", function() { reject(new TypeError("Failed to fetch.")); }); xhr.open(init.method, resource, _mImplementations ? false : true); // see https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort init.signal.addEventListener("abort", function () { xhr.abort(); reject(new DOMException("The user aborted a request.", "AbortError")); }); // set request headers var oHeaders; if (init.headers instanceof Headers) { oHeaders = Object.fromEntries(init.headers); } else { oHeaders = init.headers || {}; } Object.keys(oHeaders).forEach(function(key) { xhr.setRequestHeader(key, oHeaders[key]); }); // request credentials if (ALLOWED_CREDENTIALS.includes(init.credentials)) { // set credentials if (init.credentials === "omit") { xhr.withCredentials = false; } else if (init.credentials === "include") { xhr.withCredentials = true; } } else { reject(new TypeError("Failed to execute 'fetch': Failed to read the 'credentials' property from 'RequestInit': The provided value " + init.credentials + " is not a valid enum value of type RequestCredentials.")); } // send request try { xhr.send(init.body); } catch (error) { reject(new TypeError(error.message)); } }); } /** * Header values that can be used with the "Accept" and "Content-Type" headers * in the fetch call or the response object. * * @type {Object} * @private * @ui5-restricted SAPUI5 Distribution Layer Libraries * */ fetch.ContentTypes = { TEXT: "text/plain", HTML: "text/html", XML: "application/xml, text/xml", JSON: "application/json, text/javascript" }; return fetch; });