201 lines
5.1 KiB
JavaScript
201 lines
5.1 KiB
JavaScript
import { VnObject } from './object'
|
|
import { JsonException } from './json-exception'
|
|
|
|
/**
|
|
* Handler for JSON rest connections.
|
|
*/
|
|
export class JsonConnection extends VnObject {
|
|
_connected = false
|
|
_requestsCount = 0
|
|
token = null
|
|
interceptors = []
|
|
|
|
use (fn) {
|
|
this.interceptors.push(fn)
|
|
}
|
|
|
|
/**
|
|
* Executes the specified REST service with the given params and calls
|
|
* the callback when response is received.
|
|
*
|
|
* @param {String} url The service path
|
|
* @param {Object} params The params to pass to the service
|
|
* @return {Object} The parsed JSON response
|
|
*/
|
|
async send (url, params) {
|
|
if (!params) params = {}
|
|
params.srv = `json:${url}`
|
|
return this.sendWithUrl('POST', '.', params)
|
|
}
|
|
|
|
async sendForm (form) {
|
|
const params = {}
|
|
const elements = form.elements
|
|
|
|
for (let i = 0; i < elements.length; i++) {
|
|
if (elements[i].name) {
|
|
params[elements[i].name] = elements[i].value
|
|
}
|
|
}
|
|
|
|
return this.sendWithUrl('POST', form.action, params)
|
|
}
|
|
|
|
async sendFormMultipart (form) {
|
|
return this.request({
|
|
method: 'POST',
|
|
url: form.action,
|
|
data: new FormData(form)
|
|
})
|
|
}
|
|
|
|
async sendFormData (formData) {
|
|
return this.request({
|
|
method: 'POST',
|
|
url: '',
|
|
data: formData
|
|
})
|
|
}
|
|
|
|
/*
|
|
* Called when REST response is received.
|
|
*/
|
|
async sendWithUrl (method, url, params) {
|
|
const urlParams = new URLSearchParams()
|
|
for (const key in params) {
|
|
if (params[key] != null) {
|
|
urlParams.set(key, params[key])
|
|
}
|
|
}
|
|
|
|
return this.request({
|
|
method,
|
|
url,
|
|
data: urlParams.toString(),
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
}
|
|
})
|
|
}
|
|
|
|
async request (config) {
|
|
const request = new XMLHttpRequest()
|
|
request.open(config.method, config.url, true)
|
|
|
|
for (const fn of this.interceptors) {
|
|
config = fn(config)
|
|
}
|
|
|
|
const headers = config.headers
|
|
if (headers) {
|
|
for (const header in headers) {
|
|
request.setRequestHeader(header, headers[header])
|
|
}
|
|
}
|
|
|
|
const promise = new Promise((resolve, reject) => {
|
|
request.onreadystatechange = () =>
|
|
this._onStateChange(request, resolve, reject)
|
|
})
|
|
|
|
request.send(config.data)
|
|
|
|
this._requestsCount++
|
|
|
|
if (this._requestsCount === 1) {
|
|
this.emit('loading-changed', true)
|
|
}
|
|
|
|
return promise
|
|
}
|
|
|
|
_onStateChange (request, resolve, reject) {
|
|
if (request.readyState !== 4) {
|
|
return
|
|
}
|
|
|
|
this._requestsCount--
|
|
|
|
if (this._requestsCount === 0) {
|
|
this.emit('loading-changed', false)
|
|
}
|
|
|
|
let data = null
|
|
let error = null
|
|
try {
|
|
if (request.status === 0) {
|
|
const err = new JsonException()
|
|
err.message =
|
|
'The server does not respond, please check your Internet connection'
|
|
err.statusCode = request.status
|
|
throw err
|
|
}
|
|
|
|
let contentType = null
|
|
|
|
try {
|
|
contentType = request
|
|
.getResponseHeader('Content-Type')
|
|
.split(';')[0]
|
|
.trim()
|
|
} catch (err) {
|
|
console.warn(err)
|
|
}
|
|
|
|
if (contentType !== 'application/json') {
|
|
const err = new JsonException()
|
|
err.message = request.statusText
|
|
err.statusCode = request.status
|
|
throw err
|
|
}
|
|
|
|
let json
|
|
let jsData
|
|
|
|
if (request.responseText) {
|
|
json = JSON.parse(request.responseText)
|
|
}
|
|
if (json) {
|
|
jsData = json.data || json
|
|
}
|
|
|
|
if (request.status >= 200 && request.status < 300) {
|
|
data = jsData
|
|
} else {
|
|
let exception = jsData.exception
|
|
|
|
const err = new JsonException()
|
|
err.statusCode = request.status
|
|
|
|
if (exception) {
|
|
exception = exception
|
|
.replace(/\\/g, '.')
|
|
.replace(/Exception$/, '')
|
|
.replace(/^Vn\.Web\./, '')
|
|
|
|
err.exception = exception
|
|
err.message = jsData.message
|
|
err.code = jsData.code
|
|
err.file = jsData.file
|
|
err.line = jsData.line
|
|
err.trace = jsData.trace
|
|
} else {
|
|
err.message = request.statusText
|
|
}
|
|
|
|
throw err
|
|
}
|
|
} catch (e) {
|
|
data = null
|
|
error = e
|
|
}
|
|
|
|
if (error) {
|
|
this.emit('error', error)
|
|
reject(error)
|
|
} else {
|
|
resolve(data)
|
|
}
|
|
}
|
|
}
|