挪用ajax取要求后端数据是项目中最基础的功用。然则假如每次直接挪用底层的浏览器api去发要求则非常贫苦。如今来剖析一下怎样封装这一层,看看有哪些基础题目须要斟酌。本文底层运用fe
挪用 ajax 取要求后端数据是项目中最基础的功用。然则假如每次直接挪用底层的浏览器 api 去发要求则非常贫苦。如今来剖析一下怎样封装这一层,看看有哪些基础题目须要斟酌。本文底层运用 fetch ,假如你运用 XMLHttpRequest 以至第三方库(比如:axios)封装历程都是迥然不同的。
封装反复代码
关于同一个项目一般来讲要求参数有许多反复的内容,比如 url 的拼接,http head 的设置。假定我们挪用的是 RESTful 接口,一般我们须要更改的有:1. 要求 url 的 path 部份;2. 参数;3. 要求 method;4. 胜利/失利回调函数。我们看下把反复代码封装成一个 ApiSender 的示例代码:
const URL_PREFIX = 'xxx';
let ApiSender = {
send( options ) {
let {
path,
params,
method,
success,
fail
} = options;
let url = URL_PREFIX + path;
if ( method==='GET' ) {
url += ('?'+toQueryString( params ));
}
let requestBody;
if ( method==='POST' ) {
requestBody = params;
}
fetch( url, {
method: method,
// 这里假定我们项目要求头牢固这两个
headers: {
'Accept': 'application/json, text/Javascript, */*; q=0.01',
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
},
credentials: 'include',
body: requestBody
} ).then( function(response){
let resultJson = response.json();
if ( /* 推断返回没有毛病 */ ) {
success && success( resultJson );
} else {
fail && fail( resultJson.error );
}
} );
}
}
使挪用可读性更好
以上封装了一个 ApiSender,挪用的时刻以下:
ApiSender.send( '/resource', 'GET', {
pageSize: 10,
pageNo: 1
}, function( result ){
// 对效果举行处置惩罚
}, function( error ){
alert( error )
} )
经由过程传递回调函数的体式格局,可读性性不是很好(固然这是一个仁者见仁的题目)。我们把返回改成 Promise。由于我们用的是 fetch,它直接返回的就是 Promise,比较好改。假如你底层用的是 XMLHttpRequest,那末能够自行把挪用 XMLHttpRequest 的代码封装在一个 Promise 中返回。
let ApiSender = {
send( options ) {
let {
path,
params,
method,
success,
fail
} = options;
let url = URL_PREFIX + path;
if ( method==='GET' ) {
url += toQueryString( params );
}
let requestBody;
if ( method==='POST' ) {
requestBody = params;
}
return fetch( url, {
method: method,
// 这里假定我们项目要求头牢固这两个
headers: {
'Accept': 'application/json, text/Javascript, */*; q=0.01',
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
},
credentials: 'include',
body: requestBody
} ).then( function(response){
return response.json()
} );
}
}
挪用的时刻代码就变成:
ApiSender.send( '/resource', 'GET', {pageSize:10,pageNo:1} ).then( function(result){
if ( /* 推断返回没有毛病 */ ) {
// 处置惩罚效果
} else {
// 提醒毛病
}
} )
从挪用者角度笼统返回值
上面代码有一个题目,关于 ApiSend 的挪用者来讲,他须要直接处置惩罚接口返回值,推断是不是胜利。假如接口返回对象比较简单还好,假如非常庞杂,那末挪用者就很头疼,举个例子,我碰到过以下的接口返回值:
{
content: {
result: {
errorCode: 1,
errorMessage: '',
isSuccess: true
},
data: {}|[] // 真正的可用数据
},
a: { // 有特性的字段名我做了简化,运用了a,ab如许的字段名。a 这个字段内容是 api 网关层包装的。
code: 1,
ab: [ {
code: 1
} ]
}
}
怎样推断这个返回值是胜利的呢?
let result = { /* 上面谁人对象 */ }
if (
result.a &&
result.a.code === 0 &&
result.a.ab &&
result.a.ab[ 0 ] &&
result.a.ab[ 0 ].code === 0
) {
if (
result.content &&
result.content.result &&
result.content.result.isSuccess === true
) {
// 处置惩罚效果 result.content.data
}
}
你设想下,作为 ApiSender 的挪用方,会愿望取得什么效果?实行准确的时刻取得接口返回的数据,实行非常的时刻取得毛病信息。我不愿望挪用一个要领,须要经由过程庞杂地剖析返回值来推断是不是胜利。所以最直观的就是把毛病封装成一个很直观的返回值:
let ApiSender = {
send( options ) {
/* 代码省略掉了 */
return fetch( /* 参数也省略掉了 */ ).then( function(response){
let result = response.json();
if ( isSuccessResult(result) ) {
return [ null, result.content.data ]
} else {
let error = parseError( result );
return [ error, null ];
}
} );
}
}
那末挪用方对效果的推断就非常方便了:
ApiSender.send( '/resource', 'GET', {pageSize:10,pageNo:1} ).then( function([error,data]){
if ( !error ) {
// 处置惩罚效果 data
} else {
alert( error ); // error 的花样人人能够自行定义,各个项目各有不同
}
} );
面向切面须要做些什么
以上一个比较基础且简约的封装就做好了,然则实际中有些基础功用是常常须要的,比如要求日记,要求毛病报错一致处置惩罚。假如这些代码须要挪用方来做,一来代码反复,二来比如日记应该是挪用方不感知的一个功用。所以我们对代码进一步举行优化,到场这些功用:
let ApiSender = {
send( options ) {
/* 代码省略掉了 */
return fetch( /* 参数也省略掉了 */ ).then( function(response){
let result = response.json();
// 纪录挪用日记
writeLog( options, result );
if ( isSuccessResult(result) ) {
return [ null, result.content.data ]
} else {
let error = parseError( result );
// 界面报错
MessageComponent.error( `${error.message}(${error.code})` );
return [ error, null ];
}
} );
}
}
日记你能够上传服务器,也能够就当地 console,日记纪录哪些内容,参数怎样都按各自的项目需求而定。云云的话,挪用方就更简约了:
ApiSender.send( '/resource', 'GET', {pageSize:10,pageNo:1} ).then( function([error,data]){
if ( !error ) {
// 处置惩罚效果 data
}
} );
绝大多数状况下,挪用接口返回毛病是须要在页面上提醒毛病的,然则并非一切状况都须要。比如非用户触发的行动,且要求返回的效果并不严重影响页面操纵或许流程。那末我们能够在挪用 ApiSender 的时刻加一个参数,许可挪用方跳过全局毛病处置惩罚:
let ApiSender = {
send( options ) {
/* 代码省略掉了 */
let skipErrorHandler = options.skipErrorHandler;
return fetch( /* 参数也省略掉了 */ ).then( function(response){
let result = response.json();
// 纪录挪用日记
writeLog( options, result );
if ( isSuccessResult(result) ) {
return [ null, result.content.data ]
} else {
let error = parseError( result );
// 传了这个参数才跳过,不传或许传了非 true 值(固然包含 false),都以为不跳过
if ( skipErrorHandler===true ) {
// 界面报错
MessageComponent.error( `${error.message}(${error.code})` );
}
return [ error, null ];
}
} );
}
}
所以假如你愿望本身处置惩罚毛病,挪用的时刻代码就是:
ApiSender.send( '/resource', 'GET', {skipErrorHandler:true/*, 其他参数 */} ).then( function([error,data]){
if ( !error ) {
// 处置惩罚效果 data
} else {
// 自行处置惩罚毛病
}
} );
到这里为止,要求层的基础封装算是比较完整了,不过末了有一个小点要斟酌下,假如你在 fetch().then 传入的回调函数中由于种种原因而抛出了非常(比如某个字段没有判空)。那末 ApiSender 的挪用方是没法感知的,顺序直接就报错了。所以为了顺序的健壮性,我们末了再加一个 catch:
let ApiSender = {
send( options ) {
/* 代码省略掉了 */
let skipErrorHandler = options.skipErrorHandler;
return fetch( /* 参数也省略掉了 */ ).then( function(response){
let result = response.json();
// 纪录挪用日记
writeLog( options, result );
if ( isSuccessResult(result) ) {
return [ null, result.content.data ]
} else {
let error = parseError( result );
// 传了这个参数才跳过,不传或许传了非 true 值(固然包含 false),都以为不跳过
if ( skipErrorHandler===true ) {
// 界面报错
MessageComponent.error( `${error.message}(${error.code})` );
}
return [ error, null ];
}
} ).catch( function(error){
return [ error, null ];
} );
}
}
如许一个对挪用方友爱,防止代码反复的要求层就封装好了。PS: 假如对 Promise 的 api 不是很熟悉的话,能够先了解下,有助于更好的明白示例代码。