基于typeScript请求服务端的js-api-sdk统一封装思路

来源:互联网 发布:lamp源码安装包下载 编辑:程序博客网 时间:2024/05/17 08:15

项目地址

在前端开发中向服务端请求数据是非常重要的,特别是在复杂的项目中对后台的api接口进行友好的调用是非常重要的(这里不得不说typeScript写起代码的体验是很爽的)。

基本思路,可以想后台一样进行接口封装,比如用户相关的接口一个MemberServices 里面有关于用户所有的api接口 例如getMemberById等,这样尽量语义话的调用。

思路如下:
(1)首先抽象出一个Api请求的对象接口(抽象类)

/** * Api客户端请求接口 */export abstract class ApiClientInterface<T> {    /**     * 请求服务端client工具对象     * 非必填     */    private _client?: any;    constructor(client: any) {        this._client = client;    }    get client(): any {        return this._client;    }    /**     * post请求     * @param option     */    post = (option: T): any => {    }    /**     * get请求     * @param option     */    get = (option: T): any => {    }    /**     * 抓取数据     * @param option     */    fetch = (option: T): any => {    }}

其中这个泛型T是一个请求配置对象,可以根据不同的项目或者是实现进行切换,这里提供一个基于阿里weex开源项目中stream请求对象封装的 ApiClient和API配置对象的示例

/** * 请求服务端的api统一接口失效 */class ApiClient extends ApiClientInterface<WeexStreamOption> {    /**     * @param client WEEX 中的steam对象     */    constructor(client: any) {        super(client);    }    /**     * post请求     *     */    post = (option: WeexStreamOption) => {        option.method = ReqMethod.POST;        return this.fetch(option);    }    /**     * get请求     */    get = (option: WeexStreamOption) => {        option.method = ReqMethod.GET;        return this.fetch(option);    }    /**     * 获取数据     * @param option     * 默认 Content-Type 是 ‘application/x-www-form-urlencoded’。     * 如果你需要通过 POST json , 你需要将 Content-Type 设为 ‘application/json’。     */    fetch = (option: WeexStreamOption): any => {        option.data = isUndefined(option.data) ? {} : option.data;        const sign = this.sign(option.signFields, option.data);  //获取签名字符串        if (option.method === ReqMethod.GET) {            //拼接参数            let params = null;            if (option.url.indexOf("?") > 0) {                params = "&";            } else {                params = "?";            }            params += this.joinParamByReq(option.data);            option.url += params;            option.url += "&sign" + sign;        } else if (option.method === ReqMethod.POST) {            option.data['sign'] = sign;        }        console.log("请求url--> " + option.url);        console.log("请求method--> " + option.method);        console.log("请求headers--> " + option.headers);        console.log("请求params--> " + JSON.stringify(option.data));        option.type = isUndefined(option.type) ? DataType.JSON : option.type;        let num = Math.round(Math.random() * 20)        let data = {            isSuccess: num % 2 === 0,            num: num        };        option.progressCallback(data);        setTimeout(function () {            option.callBack(data);        }, 1500)        //WEEX stream对象 https://weex.apache.org/cn/references/modules/stream.html        // this.client.fetch({        //     method: ReqMethod[option.method],               //请求方法get post        //     url: option.url,                     //请求url        //     type: DataType[option.type],                    //响应类型, json,text 或是 jsonp {在原生实现中其实与 json 相同)        //     headers: option.headers,             //headers HTTP 请求头        //     body: JSON.stringify(option.data)     //参数仅支持 string 类型的参数,请勿直接传递 JSON,必须先将其转为字符串。请求不支持 body 方式传递参数,请使用 url 传参。        // }, function (response) {        //     console.log(response);        //     /**        //      * 响应结果回调,回调函数将收到如下的 response 对象:        //      * status {number}:返回的状态码        //      * ok {boolean}:如果状态码在 200~299 之间就为真        //      * statusText {string}:状态描述文本        //      * data {Object | string}: 返回的数据,如果请求类型是 json 和 jsonp,则它就是一个 object ,如果不是,则它就是一个 string。        //      * headers {Object}:响应头        //      */        //     if (!response.ok) {        //         //请求没有正确响应        //         console.log("响应状态码:" + status + " 状态描述:" + response.statusText);        //         return;        //     }        //     option.callBack(response.data, response.headers);        // }, function (resp) {        //     console.log(resp);        //     /**        //      * 关于请求状态的回调。 这个回调函数将在请求完成后就被调用:        //      * readyState {number}:当前状态state:’1’: 请求连接中opened:’2’: 返回响应头中received:’3’: 正在加载返回数据        //      * status {number}:响应状态码.        //      * length {number}:已经接受到的数据长度. 你可以从响应头中获取总长度        //      * statusText {string}:状态文本        //      * headers {Object}:响应头        //      */        //        //     if (option.progressCallback === null) {        //         return;        //     }        //     option.progressCallback(resp);        // });    }    /**     * 将一个对象转换成url参数字符串     * @param req     * @return {string}     */    private joinParamByReq(req: Object): String {        var result = "";        for (let key in req) {            result += key + "=" + req[key] + "&";        }        result = result.substr(0, result.length - 1);        return result;    }    /**     * ap请求时签名     * @param fields     * @param params     * @return {string}     */    private sign = (fields: Array<String>, params: Object): String => {        if (isUndefined(fields) || isNull(fields)) {            return "";        }        let value = "";        fields.forEach(function (item) {            let param = params[item.toString()];            if (isUndefined(param)) {                // console.warn("参与签名的参数:" + item + " 未传入!");                throw new Error("参与签名的参数:" + item + " 未传入!");            }            value += item + "=" + param + "&";        });        value = "timestamp=" + params['timestamp'];  //加入时间戳参与签名        let sign = md5(value);        return sign;    }}export default ApiClient;
/** * weex stream请求参数对象 */export interface WeexStreamOption {    /**     * 请求url     */    url?: String;    /**     * 请求方法     */    method?: ReqMethod;    /**     * 结果数据类型     */    type?: DataType;    /**     * 请求头     */    headers?: Object;    /**     * 请求参数,仅post请求需要     */    data?: Object;    /**     * 参与签名的参数列表     */    signFields?:Array<String>;    /**     * 响应回调     */    callBack?:Function;    /**     * 关于请求状态的回调。 这个回调函数将在请求完成后就被调用:     */    progressCallback?:Function;}

(2) 提供一个统一的实现,减少工作量,比起前端不是后端,不可能每一个接口都去实现一次,而且接口的流程非常统一,都是获取数据,只不过是url不同,参数不同,结果处理不同罢了。(这边实现使用Proxy对象)
抽象一个ServiceProxy对象

import ApiClient from "../ApiClient";import {isUndefined} from "util";import {DataType} from "./DataType";import {ReqMethod} from "./ReqMethod";/** * 服务代理 */export default class ServiceProxy {    /**     *     * @param client       用于请求数据的客户端对象,非必填     * @param handler      接口对象     * @return {ServiceProxy} 返回代理对象     */    constructor(client: any = null) {        const api = new ApiClient(client);        const handler = this;        console.log(handler);        console.log(api)        return new Proxy({}, {            get: function (target, key, receiver) {                return function () {                    let params = arguments[0];                    let options = arguments[1];                    let methodName = arguments[2].toUpperCase();      //请求方法                    let resultDataType = arguments[3].toUpperCase();  //结果数据类型                    return new Promise(function (resove, reject) {                        let config = handler[key]();  //获取配置                        if (isUndefined(config)) {                            throw new Error("请求的方法: " + key.toString() + " 未定义");                        }                        options.url = config.url;                 //请求的url                        options.signFields = config.signFields;   //参与签名的请求参数                        options.method = isUndefined(methodName) ? config.method : ReqMethod[methodName];        //请求方法 post、get                        options.type = isUndefined(resultDataType) ? DataType.JSON : DataType[resultDataType];  //结果数类型                        options.callBack = function (data) {                            console.log("api接口" + config.url + " 返回数据-> " + data);                            if (data.isSuccess) {                                resove(data);                            } else {                                reject(data)                            }                        };                        options.data=params;                        let method = ReqMethod[options.method];                        console.log(options);                        api[method.toLowerCase()](options);                    });                };            },            set: function (target, key, value, receiver) {                throw new Error("接口不允许设置值!");            }        });    }}(3)剩下的就是写具体的实现了,示例如下:

import ServiceProxy from “./api/base/ServiceProxy”;
import ApiConfig from “./api/base/ApiConfig”;
import {WeexStreamOption} from “./api/WeexStreamOption”;
import {TestReq} from “./TestReq”;

/**
* 测试服务接口
*/
export default class TestService extends ServiceProxy {

constructor(client?: any) {    super(client);}/** * 该方法写法固定,可以考虑使用自动生成 * @param params 请求参数 * @param option 请求配置 * @param method 请求类型 * @param dataType 结果数据类型 * @return {ApiConfig} */testApi(params: TestReq, option: WeexStreamOption = {}, method?: String, dataType?: String): any {    return ApiConfig.newInstance("/api/test.htm", ["userName", "phoneCode"]);};

}
服务中的方法实现非常统一,只是方法名和放回的ApiConfig对象中的数据不一样。这样实现以后对于服务调用就非常清晰了。

更多细节可以参考代码,项目地址在文章开头。

1 0
原创粉丝点击