类名 common/service/BaseServer.js
import { Zondy } from '../base'
import { deepClone, defaultValue, getGUID, Log } from '../util'
import { FetchMethod, TokenAttachType } from '../base/enum'
import RequestInterceptor from './RequestInterceptor'
import ResponseInterceptor from './ResponseInterceptor'
import FetchServer from './FetchServer'
import { checkParam, checkPathParameters, formatBaseUrl } from './Utils'

/**
 * 服务查询基类
 * @class BaseServer
 * @moduleEx ServiceModule
 * @param {Object} options 构造参数
 * @param {String} [options.url = 无] 基地址,必填
 * @param {Object} [options.headers = 无] 自定义请求头参数
 * @param {String} [options.tokenKey = 'token'] token名
 * @param {String} [options.tokenValue = 无] token值
 * @param {TokenAttachType} [options.tokenAttachType = TokenAttachType.url] 指定token附加到何处
 * @param {RequestInterceptor} [options.requestInterceptor = 无] 请求发送拦截器
 * @param {ResponseInterceptor} [options.responseInterceptor = 无] 请求响应拦截器
 * @param {Number} [options.requestTimeout = 45000] 请求超时时间,默认45000ms,即45s
 */
class BaseServer {
  constructor(options) {
    options = defaultValue(options, {})
    /**
     * 服务基地址
     * @member {String} BaseServer.prototype.url
     * @example
     * //设置请求基地址
     * // ES5引入方式
     * const { BaseServer } = Zondy.Service
     * // ES6引入方式
     * import { BaseServer } from "@mapgis/webclient-common"
     * let BaseServer = new BaseServer({
     *   //请求基地址
     *   url: '你的URL'
     * });
     * //动态修改
     * BaseServer.url = '新URL';
     */
    this.url = defaultValue(options.url, '')
    const _urlObj = formatBaseUrl(this.url)
    // 获取纯粹的基地址
    this._url = _urlObj.baseUrl
    // 保存?之后的部分
    this._paramString = _urlObj.paramString ? `${_urlObj.paramString}&` : ''
    /**
     * 是否使用确据唯一的fetch对象,默认为true,当设为false时,会使用自己私有的fetch对象,所有的请求设置不会影响全局
     * @member {Boolean} BaseServer.prototype.enableGlobeFetch
     * @example
     * //设置请求基地址
     * // ES5引入方式
     * const { BaseServer } = Zondy.Service
     * // ES6引入方式
     * import { BaseServer } from "@mapgis/webclient-common"
     * let BaseServer = new BaseServer({
     *   //请求基地址
     *   url: '你的URL',
     *   //使用私有的fetch对象
     *   enableGlobeFetch: false,
     *   //此时设置token等属性,不会影响全局的fetch对象
     *   tokenValue: '你的token'
     * });
     * //继续使用全局fetch
     * BaseServer.enableGlobeFetch = true;
     */
    this.enableGlobeFetch = defaultValue(options.enableGlobeFetch, true)

    /**
     * 客户端id
     * @member {Boolean} BaseServer.prototype.clientId
     */
    this.clientId = defaultValue(options.clientId, getGUID())

    // 私有变量
    // 所有请求发送前会触发的函数
    this._requestInterceptor = defaultValue(
      options.requestInterceptor,
      new RequestInterceptor()
    )
    // 所有请求发送后会触发的函数
    this._responseInterceptor = defaultValue(
      options.responseInterceptor,
      new ResponseInterceptor()
    )
    // 请求超时时间
    this._requestTimeout = defaultValue(options.requestTimeout, 45000)
    // 请求头参数
    this._headers = defaultValue(options.headers, undefined)
    // token名
    this._tokenKey = defaultValue(options.tokenKey, 'token')
    // token值
    this._tokenValue = defaultValue(options.tokenValue, undefined)
    // 指定token附加到何处
    this._tokenAttachType = defaultValue(
      options.tokenAttachType,
      TokenAttachType.url
    )
    // 私有的fetch对象
    this._fetchService = undefined

    // 保存公有fetch的构造参数
    if (this.enableGlobeFetch) {
      this._globeOptions = {
        requestInterceptor: this._requestInterceptor,
        responseInterceptor: this._responseInterceptor,
        requestTimeout: this._requestTimeout,
        headers: this._headers,
        tokenKey: this._tokenKey,
        tokenValue: this._tokenValue,
        tokenAttachType: this._tokenAttachType,
        fetchService: this._fetchService
      }
    } else {
      // 保存私有fetch的构造参数
      this._privateOptions = {
        requestInterceptor: this._requestInterceptor,
        responseInterceptor: this._responseInterceptor,
        requestTimeout: this._requestTimeout,
        headers: this._headers,
        tokenKey: this._tokenKey,
        tokenValue: this._tokenValue,
        tokenAttachType: this._tokenAttachType,
        fetchService: this._fetchService
      }
    }

    // 初始化fetch对象
    this._initFetch()
  }

  /**
   * 获取服务信息,IGS2.0新增服务
   * @param options 查询参数
   * @param {Function} [options.success = 无] 查询成功回调函数,若使用Promise方式则不必填写
   * @param {Function} [options.failure = 无] 查询失败回调函数,若使用Promise方式则不必填写
   * @example <caption><h5>获取服务信息-回调方式</h5></caption>
   * server.queryServerInfo({
   *   success: function (result) {
   *     console.log('请求成功:', result);
   *   },
   *   failure: function (result) {
   *     console.log('请求失败:', result);
   *   }
   * });
   *
   * @example <caption><h5>获取服务信息-promise方式</h5></caption>
   * server.queryServerInfo({
   * })
   * .then(function (result) {
   *   console.log('请求成功:', result);
   * }).catch(function (result) {
   *   console.log('请求失败:', result);
   * });
   */
  queryServerInfo(options) {
    const self = this
    return this._querySimpleInfo(options, function (url) {
      // igs1.0的地图服务
      if (url.indexOf('/igs/rest/mrcs/docs/') > -1) {
        // 拼装返回基地址,必须这样传,否则返回的数据是空的
        return `${url.replace('mrcs/docs', 'mrms/info')}?f=json`
      }
      if (url.indexOf('/igs/rest/mrms/docs/') > -1) {
        // 拼装返回基地址,必须这样传,否则返回的数据是空的
        return `${url.replace('docs', 'info')}?f=json`
      }
      if (url.indexOf('/igs/rest/mrms/tile/') > -1) {
        // 拼装返回基地址,必须这样传,否则返回的数据是空的
        return `${url.replace('tile', 'info')}?f=json`
      }
      // igs1.0的G3D服务
      if (url.indexOf('/igs/rest/g3d/') > -1) {
        // 拼装返回基地址
        return `${url}/GetDocInfo`
      }
      // 拼装返回基地址
      return `${url}?f=json${self._paramString}`
    })
  }

  /**
   * 初始化并分会Fetch对象
   * @private
   * @return Object Fetch对象
   * */
  _initFetch() {
    // 当全局对象不存在时,创建一个
    if (this.enableGlobeFetch && !Zondy.Service._fetchService) {
      // 如果有共有参数,就在初始化时赋予
      if (this._globeOptions) {
        Zondy.Service._fetchService = new FetchServer('', this._globeOptions)
      } else {
        Zondy.Service._fetchService = new FetchServer('', {})
      }
    } else if (!this.enableGlobeFetch && !this._fetchService) {
      // 当私有对象不存在时,创建一个,如果有私有有参数,就在初始化时赋予
      if (this._privateOptions) {
        this._fetchService = new FetchServer('', this._privateOptions)
      } else {
        this._fetchService = new FetchServer('', {})
      }
    }

    return this.enableGlobeFetch
      ? Zondy.Service._fetchService
      : this._fetchService
  }

  /**
   * 获取公有或私有的fetch对象
   * @private
   * @return Object Fetch对象
   * */
  _getFetch() {
    // 获取fetch
    let fetchService = this.enableGlobeFetch
      ? Zondy.Service._fetchService
      : this._fetchService

    // 确保fetch存在
    if (!fetchService) {
      fetchService = this._initFetch()
    }

    return fetchService
  }

  /**
   * 发起GET请求
   * @private
   * @param url 查询链接
   * @param options 查询参数
   * */
  _getRequest(url, options) {
    Log.info('请求链接:', url)
    Log.info('请求参数:', options)
    // 成功回调函数
    const success = defaultValue(options.success, undefined)
    // 失败回调函数
    const failure = defaultValue(options.failure, undefined)

    // 发起请求
    const fetchService = this._getFetch()
    return fetchService.request({
      url,
      method: 'GET',
      success(result) {
        if (result) {
          if (success) success(result)
        }
      },
      failure(result) {
        if (failure) failure(result)
      }
    })
  }

  /**
   * 发起POST请求
   * @private
   * @param url 查询链接
   * @param options 查询参数
   * */
  _postRequest(url, options) {
    return this._sendBodyRequest('POST', url, options)
  }

  /**
   * 发起DELETE请求
   * @private
   * @param url 查询链接
   * @param options 查询参数
   * */
  _deleteRequest(url, options) {
    return this._sendBodyRequest('DELETE', url, options)
  }

  /**
   * 发起PUT请求
   * @private
   * @param url 查询链接
   * @param options 查询参数
   * */
  _putRequest(url, options) {
    return this._sendBodyRequest('PUT', url, options)
  }

  /**
   * 发起PATCH请求
   * @private
   * @param url 查询链接
   * @param options 查询参数
   * */
  _patchRequest(url, options) {
    return this._sendBodyRequest('PATCH', url, options)
  }

  /**
   * 发起所有body请求
   * @private
   * @param method 方法名
   * @param url 查询链接
   * @param options 查询参数
   * */
  _sendBodyRequest(method, url, options) {
    // 成功回调函数
    const success = defaultValue(options.success, undefined)
    // 失败回调函数
    const failure = defaultValue(options.failure, undefined)
    // post请求发送的数据
    const data = defaultValue(options.data, {})
    // 设置headers
    const headers = defaultValue(options.headers, undefined)
    const responseType = defaultValue(options.responseType, undefined)

    // 发起请求
    const fetchService = this._getFetch()
    return fetchService.request({
      url,
      method,
      data,
      headers,
      responseType,
      success(result) {
        if (result) {
          if (success) success(result)
        }
      },
      failure(result) {
        if (failure) failure(result)
      }
    })
  }

  /**
   * 设置fetch的属性
   * @private
   * @param {String} key 属性名
   * @param {String} value 属性值
   * */
  _setFetchValue(key, value) {
    // 使用全局或私有fetch
    if (this.enableGlobeFetch && Zondy.Service._fetchService) {
      Zondy.Service._fetchService[key] = value
    } else if (!this.enableGlobeFetch) {
      Zondy.Service._fetchService[key] = value
    }
  }

  /**
   * 验证正确性并发送请求
   * @private
   * @param {Object} checkResult 校验对象
   * @param {String} url 请求基地址
   * @param {Object} options 额外参数
   * */
  _checkAndRequest(checkResult, url, options) {
    options = defaultValue(options, {})

    // 只有校验通过才会发送请求
    if (checkResult.isChecked) {
      // 发起查询
      const method = defaultValue(options.method, FetchMethod.get)

      switch (method) {
        case FetchMethod.get:
          return this._getRequest(url + checkResult.queryString, options)
        case FetchMethod.post:
          const postOpts = deepClone(options)
          postOpts.data = checkResult.queryString
          return this._postRequest(url, postOpts)
        case FetchMethod.delete:
          const deleteOpts = deepClone(options)
          deleteOpts.data = checkResult.queryString
          return this._deleteRequest(url, deleteOpts)
        case FetchMethod.put:
          const putOpts = deepClone(options)
          putOpts.data = checkResult.queryString
          return this._putRequest(url, putOpts)
        default:
          return this._getRequest(url + checkResult.queryString, options)
      }
    } else {
      if (
        this.requestInterceptor &&
        this.requestInterceptor.failure &&
        this.requestInterceptor.failure instanceof Function
      ) {
        this.requestInterceptor.failure({
          succeed: false,
          msg: checkResult.errorMessage,
          errorType: checkResult.errorType,
          data: undefined
        })
      }
      if (options.failure && options.failure instanceof Function) {
        options.failure({
          succeed: false,
          msg: checkResult.errorMessage,
          errorType: checkResult.errorType,
          data: undefined
        })
      }
      return Promise.reject({
        succeed: false,
        msg: checkResult.errorMessage,
        data: undefined,
        errorType: checkResult.errorType
      })
    }
  }

  /**
   * 验证所有PathParameter参数的正确性,PathParameter里的参数都是必填参数,必须验证正确性
   * @private
   * @param {Object} options 请求参数对象
   * @param {Object} checkOptions 验证对象
   * @return {Boolean} 是否验证通过
   * */
  _checkPathParameters(options, checkOptions) {
    // 默认只检测layerId
    checkOptions = defaultValue(checkOptions, {
      layerId: {
        type: 'String'
      }
    })
    const checkResult = checkPathParameters(checkOptions, options)

    // 验证失败,调用请求拦截器失败方法
    if (!checkResult.isChecked) {
      if (
        this.requestInterceptor &&
        this.requestInterceptor.failure &&
        this.requestInterceptor.failure instanceof Function
      ) {
        this.requestInterceptor.failure({
          succeed: false,
          msg: checkResult.errorMessage,
          errorType: checkResult.errorType,
          data: undefined
        })
      }

      return {
        succeed: false,
        msg: checkResult.errorMessage,
        errorType: checkResult.errorType
      }
    }

    return {
      succeed: true
    }
  }

  /**
   * 进行简单的info查询,没有任何条件
   * @private
   * @param {Object} options 查询参数
   * @param {Function} callback 处理请求基地址的回调
   * @return {Boolean} 是否验证通过
   * */
  _querySimpleInfo(options, callback) {
    options = defaultValue(options, {})

    if (callback && callback instanceof Function) {
      // 获取url
      const url = callback(this._url) + this._paramString
      // 请求方式为GET
      options.method = FetchMethod.get

      Log.info('发起请求,基地址为:', url)
      return this._checkAndRequest(
        {
          isChecked: true,
          errorMessage: '',
          errorType: '',
          queryString: ''
        },
        url,
        options
      )
    }

    return false
  }

  /**
   * 通过layerId查询信息
   * @private
   * @param {Object} options 查询参数
   * @param {Function} callback 处理请求基地址的回调
   * @return {Boolean} 是否验证通过
   * */
  _queryInfoByLayerId(options, callback) {
    options = defaultValue(options, {})

    // 检查PathParameters参数
    const checkPath = this._checkPathParameters(options)
    if (!checkPath.succeed) {
      if (options.failure && options.failure instanceof Function) {
        options.failure({
          succeed: false,
          msg: checkPath.msg,
          data: undefined
        })
      }
      return Promise.reject({
        succeed: false,
        msg: checkPath.msg,
        data: undefined
      })
    }

    if (callback && callback instanceof Function) {
      // 获取url
      const url = callback(this._url, options)
      // 请求方式为GET
      options.method = FetchMethod.get

      Log.info('发起请求,基地址为:', url)
      return this._checkAndRequest(
        {
          isChecked: true,
          errorMessage: '',
          errorType: '',
          queryString: ''
        },
        url,
        options
      )
    }
  }

  /**
   * 根据接口的PATH PARAMETERS和QUERY PARAMETERS来发起查询
   * @private
   * @param {Object} options 查询参数
   * @param {Object} pathCheck 要检查的路径参数
   * @param {Object} queryCheck 要检查的查询参数
   * @param {Function} callback 处理请求基地址的回调
   * @return {Boolean} 是否验证通过
   * */
  _queryByParameters(options, pathCheck, queryCheck, callback) {
    options = defaultValue(options, {})

    // 检查PathParameters参数
    const checkPath = this._checkPathParameters(options, pathCheck)
    if (!checkPath.succeed) {
      if (options.failure && options.failure instanceof Function) {
        options.failure({
          succeed: false,
          msg: checkPath.msg,
          data: undefined,
          errorType: checkPath.errorType
        })
      }
      return Promise.reject({
        succeed: false,
        msg: checkPath.msg,
        data: undefined,
        errorType: checkPath.errorType
      })
    }

    if (callback && callback instanceof Function) {
      // 获取url
      const url = callback(this._url, options)
      // url += `&clientId=${this.clientId}`

      Log.info('发起请求,基地址为:', url)
      return this._checkAndRequest(
        checkParam(options, queryCheck),
        url,
        options
      )
    }
  }
}

Object.defineProperties(BaseServer.prototype, {
  /**
   * 请求发送拦截器
   * @member {Function} BaseServer.prototype.requestInterceptor
   * @example
   * //设置拦截器,任何一个继承自BaseServer的对象都可以设置,全局唯一
   * // ES5引入方式
   * const { BaseServer,RequestInterceptor } = Zondy.Service
   * // ES6引入方式
   * import { BaseServer,RequestInterceptor } from "@mapgis/webclient-common"
   * let BaseServer = new BaseServer({
   *   //设置请求发送拦截器
   *   requestInterceptor: new RequestInterceptor({
   *     //请求发送前进行统一处理
   *     before: function(config) {
   *       //执行你的业务逻辑
   *       //注意必须显示返回config对象,如果返回为空,则不发送请求
   *       return config;
   *     },
   *     //请求发送失败时进行统一处理
   *     failure: function(error) {
   *       //执行你的业务逻辑
   *     }
   *   })
   * });
   * //动态修改
   * BaseServer.requestInterceptor.before = function() {};
   * */
  requestInterceptor: {
    configurable: false,
    get() {
      return this._requestInterceptor
    },
    set(value) {
      // 设置fetch属性
      this._setFetchValue('requestInterceptor', value)
      this._requestInterceptor = value
    }
  },
  /**
   * 请求响应拦截器
   * @member {Function} BaseServer.prototype.responseInterceptor
   * @example
   * //设置拦截器,任何一个继承自BaseServer的对象都可以设置,全局唯一
   * // ES5引入方式
   * const { BaseServer,ResponseInterceptor } = Zondy.Service
   * // ES6引入方式
   * import { BaseServer,ResponseInterceptor } from "@mapgis/webclient-common"
   * let BaseServer = new BaseServer({
   *   //设置请求响应拦截器
   *   responseInterceptor: new ResponseInterceptor({
   *     //执行请求响应,接口调用成功时会执行的回调
   *     success: function(result) {
   *       //执行你的业务逻辑
   *       //注意必须显示返回result对象,如果返回为空,则不执行请求响应成功回调
   *       return result;
   *     },
   *     //请求响应成功,接口调用失败时会执行的函数
   *     failure: function(result) {
   *       //执行你的业务逻辑
   *       //注意必须显示返回result对象,如果返回为空,则不执行回调韩式
   *       return result;
   *     }
   *   })
   * });
   * //动态修改
   * BaseServer.responseInterceptor.success = function() {};
   * */
  responseInterceptor: {
    configurable: false,
    get() {
      return this._responseInterceptor
    },
    set(value) {
      // 设置fetch属性
      this._setFetchValue('responseInterceptor', value)
      this._responseInterceptor = value
    }
  },
  /**
   * 请求超时时间,默认45000ms,即45s
   * @member {String} BaseServer.prototype.requestTimeout
   * @example
   * //设置超时时间
   * //初始化AddressServer服务对象
   * // ES5引入方式
   * const { BaseServer } = Zondy.Service
   * // ES6引入方式
   * import { BaseServer } from "@mapgis/webclient-common"
   * let BaseServer = new BaseServer({
   *   //超时时间
   *   requestTimeout: 2000
   * });
   * //动态修改
   * BaseServer.requestTimeout = 3000;
   * */
  requestTimeout: {
    configurable: false,
    get() {
      return this._requestTimeout
    },
    set(value) {
      // 设置fetch属性
      this._setFetchValue('requestTimeout', value)
      this._requestTimeout = value
    }
  },
  /**
   * 请求头参数
   * @member {String} BaseServer.prototype.headers
   * @example
   * //设置请求头参数
   * // ES5引入方式
   * const { BaseServer } = Zondy.Service
   * // ES6引入方式
   * import { BaseServer } from "@mapgis/webclient-common"
   * let BaseServer = new BaseServer({
   *   //请求头
   *   headers: {
   *      //设置Content-Type为multipart/form-data
   *     'Content-Type': 'multipart/form-data',
   *      //设置token
   *     'token': '你的token'
   *   }
   * });
   * //动态修改
   * BaseServer.headers.token = '新token';
   * */
  headers: {
    configurable: false,
    get() {
      return this._headers
    },
    set(value) {
      // 设置fetch属性
      this._setFetchValue('headers', value)
      this._headers = value
    }
  },
  /**
   * token名
   * @member {String} BaseServer.prototype.tokenKey
   * @example
   * //设置token名
   * // ES5引入方式
   * const { BaseServer } = Zondy.Service
   * // ES6引入方式
   * import { BaseServer } from "@mapgis/webclient-common"
   * let BaseServer = new BaseServer({
   *   //token名
   *   tokenKey: '你的tokenKey'
   * });
   * //动态修改
   * BaseServer.tokenKey = '新tokenKey';
   * */
  tokenKey: {
    configurable: false,
    get() {
      return this._tokenKey
    },
    set(value) {
      // 设置fetch属性
      this._setFetchValue('tokenKey', value)
      this._tokenKey = value
    }
  },
  /**
   * token值
   * @member {String} BaseServer.prototype.tokenValue
   * @example
   * //设置token值
   * // ES5引入方式
   * const { BaseServer } = Zondy.Service
   * // ES6引入方式
   * import { BaseServer } from "@mapgis/webclient-common"
   * let BaseServer = new BaseServer({
   *   //token值
   *   tokenValue: '你的token值'
   * });
   * //动态修改
   * BaseServer.tokenValue = '新token值';
   * */
  tokenValue: {
    configurable: false,
    get() {
      return this._tokenValue
    },
    set(value) {
      // 设置fetch属性
      this._setFetchValue('tokenValue', value)
      this._tokenValue = value
    }
  },
  /**
   * 指定token附加到何处
   * @member {TokenAttachType} BaseServer.prototype.tokenAttachType
   * @example
   * //设置token值
   * // ES5引入方式
   * const { BaseServer } = Zondy.Service
   * const { TokenAttachType } = Zondy.Enum
   * // ES6引入方式
   * import { BaseServer,TokenAttachType } from "@mapgis/webclient-common"
   * let BaseServer = new BaseServer({
   *   //token名
   *   tokenValue: '你的token值',
   *   //token值
   *   tokenValue: '你的token值',
   *   //指定token附加到url后面
   *   tokenAttachType: TokenAttachType.url
   * });
   * //动态修改
   * BaseServer.tokenAttachType = TokenAttachType.header;
   * */
  tokenAttachType: {
    configurable: false,
    get() {
      return this._tokenAttachType
    },
    set(value) {
      // 设置fetch属性
      this._setFetchValue('tokenAttachType', value)
      this._tokenAttachType = value
    }
  }
})

Zondy.Service.BaseServer = BaseServer
export default BaseServer
构造函数
成员变量
方法
事件