python封装前端接口_前端项目请求层封装过程

调用 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 不是很熟悉的话,可以先了解下,有助于更好的理解示例代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值