最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 记录一次对nodejs http模块的简单封装

    正文概述 掘金(peter_yu)   2021-07-27   641

    前言

        时至2021年,nodejs在web开发领域有着举足轻重的地位,常被应用于各种业务中间件开发以及构建工具插件开发,其web框架如express、koa、fastify、egg、midway、nest等也有一定的应用空间。本文将结合常见mvc框架特性,重点阐述nodejs在web mvc框架领域中的应用价值。希望对有兴趣自己封装框架的童鞋有所帮助!

    正文

        常见mvc框架核心功能如下:1.请求/响应的统一处理机制;2.静态资源映射;3.http请求分发。本文将对以上3个问题点进行阐述,分别讲述解决方案!

    1 请求/响应的统一处理机制

        这个问题利用http模块拦截机制实现,拦截原型如下:

    function createServer(requestListener?: RequestListener): Server;
    function createServer(options: ServerOptions, requestListener?: RequestListener): Server;
    type RequestListener = (req: IncomingMessage, res: ServerResponse) => void;
    

        createServer函数接受一个请求监听器,该监听器包含了请求和响应对象,可以定义拦截器对请求响应的统一处理,如下进行了简单跨域处理:

    export default function interceptor(req, res) {
        console.log(`request ${req.url} is targeted!`.green.bold);
        // 跨域处理
        res.setHeader("Access-Control-Allow-Origin", "*");
        res.setHeader("Access-Control-Allow-Headers", "*");
        res.setHeader("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
    }
    

    2 静态资源映射

         在对静态资源映射前,首先需要区分接口请求和资源请求,如接口请求则转发给业务代码进行处理,资源请求则直接读取本地文件返回即可。因此,需要解决以下问题:

    (1)区分接口请求和资源请求

         简单区分可以采用url前缀进行区分,以/api开头的为接口请求,否则为资源请求。

    export default async function(req, res) {
        try {
            if (req.url.startsWith('/api')) { //接口请求
                await dispatchActions(req, res)
            } else { //资源请求
                ...
            }
        } catch (error) {
            console.error(error);
            res.statusCode = 500;
            res.end('服务器异常');
        }
    }
    

        资源请求又可细分为后端资源请求和前端资源请求,因此需要添加响应配置项进行处理。

    let path;
    const urlObj = new URL(req.url, globalThis.$HOST);
    if (req.url.startsWith('upload')) { // 后端资源请求
             path = join(cwd(), urlObj.pathname);
       } else { // 前端资源请求
             let pathname = urlObj.pathname === '/' ? '/index.html' : urlObj.pathname;
             path = join(cwd(), config.views, pathname);
    }
    
    (2)对资源请求设置适当的ContentType

        Content-Type对照表可以获取常用文件后缀以及对应的contentType,通过工具类处理响应即可。

    async function setContentType(path, res) {
        try {
            let contentType;
            if (path.includes('.')) {
                const suffix = '.' + path.split('.')[1];
                metaData[suffix] ? (contentType = metaData[suffix]) : (contentType = metaData['.*']);
            }
            if (contentType === 'text/html' || contentType === 'text/plain') {
                contentType += ';charset=utf-8';
            }
            if (contentType) {
                res.setHeader('Content-Type', contentType);
            }      
        } catch (error) {
            console.error(error)
        }
        
    }
    

    3 http请求分发

    (1) 编写请求分发函数
    async function dispatchActions(req, res) {
        const urlObj = new URL(req.url, globalThis.$HOST);
        const api = urlObj.pathname.split('/api')[1];
        if (getControllerExport()[api]) {
            await getControllerExport()[api](req, res);
        } else  {
            res.statusCode = 404;
            res.end('not found');
        } 
    }
    export function setControllerExport(data) {
        globalThis.$CONTROLLER = data;
    }
    
    export function getControllerExport() {
        return globalThis.$CONTROLLER;
    }
    
    (2) 仿照webpack require.context功能实现读取同一文件下的其他文件的默认导出
    // controll/index.mjs
    async function autoImpotController(dir, exclude) {
        let result = {};
        const files = await readdir(dir);
        if (files?.length) {
            for (let i = 0; i < files.length; i++) {
                if (exclude !== files[i]) {
                    const module = await import('./' + files[i]);
                    result = {...result, ...module.default};
                }
            }
        }
        setControllerExport(result);
    }
    
    const fileUrl = import.meta.url;
    const dir = dirname(fileURLToPath(fileUrl));
    const exclude = basename(fileURLToPath(fileUrl));
    autoImpotController(dir, exclude);
    
    (3) 编写controller
    // controll/user.mjs
    const base="/user";
    
    const controllers = {
        getUsername : async function (req, res) {
            try {
                res.setHeader('Content-Type', 'application/json;charset=utf-8');
                res.end(getJsonResult(true, 'getUsername'));
            } catch (error) {
                throw new Error(error);
            }
        }
    }
    
    const ept = {};
    Object.keys(controllers).forEach(controller => {
        ept[`${base}/${controller}`] = controllers[controller];
    })
    export default ept;
    

    4 基于注解的controller开发

        经过查询资料发现,基于typescript的注解开发处于实验阶段,而ecmascript注解提案处于第二阶段,详见github.com/tc39/propos… ,本文暂时不作探究,感兴趣的童鞋可以自行研究下!最终的controller写法应类似:

    @controller({baes: 'uesr'})
    class UserController {
         @Request({path: '/user', method: 'GET', contentType: 'application/json;charset=utf-8'})
         async getUsername() {
            try {
                res.setHeader('Content-Type', 'application/json;charset=utf-8');
                res.end(getJsonResult(true, 'getUsername'));
            } catch (error) {
                throw new Error(error);
            }
         }
         
    }
    

    5 欢迎访问原文链接

    欢迎访问我的博客-利用nodejs仿express对http模块进行简易封装


    起源地下载网 » 记录一次对nodejs http模块的简单封装

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元