Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

Because uniapp network request uni.request return callback function, and there is no request interception to be implemented by itself, but found uniapp uview framework encapsulated request is very good, so it is used separately

Here are the tool functions needed for deep cloning and object deep merging
// deep merge of JS objects
function deepMerge(target = {}, source = {}) {
    target = deepClone(target);
    if (typeoftarget ! = ='object' || typeofsource ! = ='object') return false;
    for (var prop in source) {
        if(! source.hasOwnProperty(prop))continue;
        if (prop in target) {
            if (typeoftarget[prop] ! = ='object') {
                target[prop] = source[prop];
            } else {
                if (typeofsource[prop] ! = ='object') {
                    target[prop] = source[prop];
                } else {
                    if (target[prop].concat && source[prop].concat) {
                        target[prop] = target[prop].concat(source[prop]);
                    } else{ target[prop] = deepMerge(target[prop], source[prop]); }}}}else{ target[prop] = source[prop]; }}return target;
}

// Deep clone
function deepClone (obj) {
    // For common "not" values, return the original value directly
    if([null.undefined.NaN.false].includes(obj)) return obj;
    if(typeofobj ! = ="object" && typeofobj ! = ='function') {
        // The primitive type returns directly
        return obj;
    }
    var o = isArray(obj) ? [] : {};
    for(let i in obj) {
        if(obj.hasOwnProperty(i)){
            o[i] = typeof obj[i] === "object"? deepClone(obj[i]) : obj[i]; }}return o;
}

/** * Verify the URL format */
function testUrl(value) {
    return /http(s)? :\/\/([\w-]+\.) +[\w-]+(\/[\w-.\/?%&=]*)? /.test(value)
}
Copy the code
It is encapsulated in the form of the Request class, and is used to generate the instance of the Request class. The Request interceptor can execute the incoming callback function before the Request, add token, add signature, etc., and then make the network Request after processing, while the response interceptor formats the data of the response, as detailed in the notes
class Request {
    // Set the global default configuration
    setConfig(customConfig) {
        // Merge objects in depth, otherwise deep attributes of the object will be lost
        this.config = deepMerge(this.config, customConfig);
    }

    // The main request section
    request(options = {}) {
        // Check request interception
        if (this.interceptor.request && typeof this.interceptor.request === 'function') {
            let tmpConfig = {};
            let interceptorRequest = this.interceptor.request(options);
            if (interceptorRequest === false) {
                // Return a Promise in a pending state to cancel the original Promise and avoid the then() callback
                return new Promise(() = >{});
            }
            this.options = interceptorRequest;
        }
        options.dataType = options.dataType || this.config.dataType;
        options.responseType = options.responseType || this.config.responseType;
        options.url = options.url || ' ';
        options.params = options.params || {};
        options.header = Object.assign({}, this.config.header, options.header);
        options.method = options.method || this.config.method;

        return new Promise((resolve, reject) = > {
            options.complete = (response) = > {
            // Hide loading after the request is returned (if the request is returned quickly, no loading may occur)
            uni.hideLoading();
            // Clear the timer. If the request comes back, no loading is required
            clearTimeout(this.config.timer);
            this.config.timer = null;
            If originalData is true, return all data (response) to the interceptor, otherwise return only response.data
            if(this.config.originalData) {
                // Determine whether an interceptor exists
                if (this.interceptor.response && typeof this.interceptor.response === 'function') {
                    let resInterceptors = this.interceptor.response(response);
                    // If the interceptor does not return false, the interceptor returns the content to the then callback of this.$u.post
                    if(resInterceptors ! = =false) {
                        resolve(resInterceptors);
                    } else {
                        // If the interceptor returns false, it means that the interceptor definer thinks there is a problem with the return and calls the catch callback directlyreject(response); }}else {
                    // If raw data is required, the original data is returned, even if there is no interceptorresolve(response); }}else {
                    if (response.statusCode == 200) {
                        if (this.interceptor.response && typeof this.interceptor.response === 'function') {
                            let resInterceptors = this.interceptor.response(response.data);
                            if(resInterceptors ! = =false) {
                                    resolve(resInterceptors);
                            } else{ reject(response.data); }}else {
                        // If originalData is not returned and there is no interceptor, pure data is returned to the then callbackresolve(response.data); }}else {
                        // When the original data is not returned, the server status code is not 200, and the modal pop-up prompts
                        // if(response.errMsg) {
                        // uni.showModal({
                        // title: response.errMsg
                        / /});
                        // }
                        reject(response)
                    }
                }
            }

            // Determine if the URL passed by the user begins with a /, and if not, add a /, using the URL () method of uView's test.js validation library
            options.url = testUrl(options.url) ? options.url : (this.config.baseUrl + (options.url.indexOf('/') = =0 ?
            options.url : '/' + options.url));

            // Whether to display loading
            If there are two simultaneous requests, the latter will clear the timer ID of the former
            // If the former timer is not cleared, the former times out and loading is displayed
            if(this.config.showLoading && !this.config.timer) {
                this.config.timer = setTimeout(() = > {
                    uni.showLoading({
                        title: this.config.loadingText,
                        mask: this.config.loadingMask
                    })
                    this.config.timer = null;
                }, this.config.loadingTime);
            }
            uni.request(options);
        })
        // .catch(res => {
        / / / / if return reject (), don't let it into the enclosing $u.p ost (). Then (). The catch () at the back of the catct ()
        // // Because many people forget to write catch(), an error is reported that a catch is not caught
        // return new Promise(()=>{});
        // })
    }

    constructor() {
        this.config = {
            baseUrl: ' '.// The requested root domain name
            // The default request header
            header: {},
            method: 'POST'.// Set to JSON and uni.request will parse the data once it is returned
            dataType: 'json'.// This parameter needs no processing, because 5+ and Alipay applet do not support, the default is text
            responseType: 'text'.showLoading: true.// Whether to display loading in the request
            loadingText: 'In the request... '.loadingTime: 800.// If the request is not returned within this time, the loading animation is displayed in ms
            timer: null./ / timer
            originalData: false.// Whether to return server raw data in interceptor, see documentation
            loadingMask: true.// Show whether loading is covered with a transparent layer to prevent touch penetration
        }

        / / the interceptor
        this.interceptor = {
            // Interception before request
            request: null.// Interception after request
            response: null}}export default new Request
Copy the code
You can also customize the request function after inheritance, such as uploading images, etc., and obtain the configuration information of the instance in real time
/ / get request
this.get = (url, data = {}, header = {}) = > {
    return this.request({
        method: 'GET',
        url,
        header,
        data
    })
}

/ / post request
this.post = (url, data = {}, header = {}) = > {
    return this.request({
        url,
        method: 'POST',
        header,
        data
    })
}

// Put request does not support Alipay small program (HX2.6.15)
this.put = (url, data = {}, header = {}) = > {
    return this.request({
        url,
        method: 'PUT',
        header,
        data
    })
}

// Delete request, alipay and Toutiao are not supported (HX2.6.15)
this.delete = (url, data = {}, header = {}) = > {
    return this.request({
        url,
        method: 'DELETE',
        header,
        data
    })
}
}
Copy the code