首页 > 编程语言 >为什么需要Koa,对比原始Node的HTTP

为什么需要Koa,对比原始Node的HTTP

时间:2024-07-21 22:28:12浏览次数:14  
标签:Node body return Koa ctx next HTTP function const

源码

大家好,我是有用就扩散,有用就点赞。

为什么需要Koa,对比HTTP

1)路由麻烦,if else过多(可以使用策略模式)

2)重复代码有点多,比如statusCode的赋值

3)请求解析与响应体包装,原始代码过于臃肿

4)请求的解析源代码太多,API不优雅

5)AOP的支持(面向切面编程),引入洋葱模型

洋葱圈模型设计有以下几点好处:

  • 更好地封装和复用代码逻辑,每个中间件只需要关注自己的功能;
  • 更清晰的程序逻辑,通过中间件的嵌套可以表明代码的执行顺序;
  • 更好的错误处理,每个中间件可以选择捕获错误或将错误传递给外层;
  • 更高的扩展性,可以很容易地在中间件栈中添加或删除中间件。

Koa源码初步实现

1.洋葱模型,use方法的组合

// koa.js
const http = require('http')
const context = require('./context')
const request = require('./request')
const response = require('./response')

class Koa {
    constructor() {
        this.middlewares = []
    }
    listen(...args) {
        const server = http.createServer(async (req, res) => {
            // 创建上下文
            const ctx = this.createContext(req, res)
            const fn = this.compose(this.middlewares)
            await fn(ctx)
            // 返回响应
            res.end(ctx.body)
        })
        server.listen(...args)
    }
    use(middleware) {
        this.middlewares.push(middleware)
    }

    // 通过getter和setter来拦截request和response,实现优雅设置body
    createContext(req, res) {
        const ctx = Object.create(context)
        ctx.request = Object.create(request)
        ctx.response = Object.create(response)

        ctx.req = ctx.request.req = req
        ctx.res = ctx.response.res = res
        return ctx
    }

    // 实现koa的compose
    compose(middlewares) {
        return function (ctx) {
            return dispatch(0)
            function dispatch(i) {
                const fn = middlewares[i]
                if (!fn) return Promise.resolve()
                return Promise.resolve(fn(ctx, function next() {
                    return dispatch(i + 1)
                }))
            }
        }
    }
}

module.exports = Koa
// context.js
module.exports = {
    get url() {
        return this.request.url
    },
    get body() {
        return this.response.body
    },
    set body(val) {
        this.response.body = val
    },
    get method() {
        return this.request.method
    }
}
// request.js
module.exports = {
    get url() {
        return this.req.url
    },

    get method() {
        return this.req.method.toLowerCase()
    }
}
// response.js
module.exports = {
    get body() {
        return this._body
    },
    set body(val) {
        this._body = val
    }
}

实现

// app.js
const Koa = require('./koa');
const app = new Koa();

// 洋葱模型,use方法的组合
const delay = () => Promise.resolve(resolve => setTimeout(() => resolve(), 2000));

app.use(async (ctx, next) => {
    ctx.body = "1";
    await next();
    ctx.body += "5";
});

app.use(async (ctx, next) => {
    ctx.body += "2";
    await delay();
    await next();
    ctx.body += "4";
});

app.use(async (ctx, next) => {
    ctx.body += "3";
});

app.listen(3000, () => {
    console.log("listening on 3000");
})

效果

在这里插入图片描述

2.路由策略模式

// router.js
class Router {
    constructor() {
      this.stack = [];
    }
  
    register(path, methods, middleware) {
      let route = {path, methods, middleware}
      this.stack.push(route);
    }
    // 现在只支持get和post,其他的同理
    get(path,middleware){
      this.register(path, 'get', middleware);
    }
    post(path,middleware){
      this.register(path, 'post', middleware);
    }
    routes() {
      let stock = this.stack;
      return async function(ctx, next) {
        let currentPath = ctx.url;
        let route;
  
        for (let i = 0; i < stock.length; i++) {
          let item = stock[i];
          if (currentPath === item.path && item.methods.indexOf(ctx.method) >= 0) {
            // 判断path和method
            route = item.middleware;
            break;
          }
        }
  
        if (typeof route === 'function') {
          route(ctx, next);
          return;
        }
  
        await next();
      };
    }
  }
  module.exports = Router;

实现

// app.js
const Koa = require('./koa');
const app = new Koa();

const Router = require('./router')
const router = new Router()

router.get('/index', async ctx => { ctx.body = 'index page'; });
router.get('/post', async ctx => { ctx.body = 'post page'; });
router.get('/list', async ctx => { ctx.body = 'list page'; });
router.post('/index', async ctx => { ctx.body = 'post page'; });
// 路由实例输出父中间件 router.routes()
app.use(router.routes());

app.listen(3000, () => {
    console.log("listening on 3000");
})

效果

在这里插入图片描述

3.组合的方法的几种实现方式

// middlewares.js
const add = (x, y) => x + y
const square = z => z * z

// 方法一 简单实现
//  const fn = (x, y) => square(add(x, y))

// 方法二 简单组合封装
// const compose = (fn1, fn2) => (...args) => fn2(fn1(...args))
// const fn = compose(add, square)

// 方法三 实现多个方法组合
// const compose = (...[first,...other]) => (...args) => {
//     let ret = first(...args)
//     other.forEach(fn => {
//         ret = fn(ret)
//     })
//     return ret
// }
// const fn = compose(add, square)

// 方法四 考虑中间件执行顺序,实现多个方法组合,同时考虑异步
function compose(middlewares) {
    return function () {
        return dispatch(0)
        function dispatch(i) {
            const fn = middlewares[i]
            if (!fn) return Promise.resolve()
            return Promise.resolve(fn(function next() {
                return dispatch(i + 1)
            }))
        }
    }
}
// 方法四实现例子
async function fn1(next) {
    console.log('fn1')
    await next()
    console.log('end fn1')
}

async function fn2(next) {
    console.log('fn2')
    await delay()
    await next()
    console.log('end fn2')
}

function fn3(next) {
    console.log('fn3')
}

function delay() {
    return Promise.resolve(res => {
        setTimeout(() => reslove(), 2000)
    })
}
const middlewares = [fn1, fn2, fn3]
const finalFn = compose(middlewares)
finalFn()

源码

nction fn3(next) {
console.log(‘fn3’)
}

function delay() {
return Promise.resolve(res => {
setTimeout(() => reslove(), 2000)
})
}
const middlewares = [fn1, fn2, fn3]
const finalFn = compose(middlewares)
finalFn()


[**源码**](https://github.com/XYuanZ/Web-study)

欢迎各位大哥投稿 PR

标签:Node,body,return,Koa,ctx,next,HTTP,function,const
From: https://blog.csdn.net/qq_44186810/article/details/140595159

相关文章

  • 基于node+vue的婚纱摄影网站
    博主主页:猫头鹰源码博主简介:Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万+、专注Java技术领域和毕业设计项目实战主要内容:毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询文末联系获取项目介绍: 本系统适合选题:婚纱、摄影、婚......
  • Could not retrieve mirrorlist http://mirrorlist.centos.org/?release=7&arch=x86_6
    报错信息│(SSHclient,Xserverandnetworktools)││││⮞[email protected]││•......
  • 服务器和本地主机上对相同请求(curl、python aiohttp)的不同响应
    我有一个用Python编写的解析器(aiohttp、bs4)。解析器的功能之一是通过链接访问文件(例如:https://modsfire.com/d/Mwv01aESgj73zx7)。importaiohttpimportyarlimportasynciofrompprintimportpprintMODSFIRE_URL="https://modsfire.com/"COOKIES={......
  • nginx 部署vue http、https
    nignx配置文件server{listen80;server_nameyour_domain.com;return301https://$server_name$request_uri;}server{listen443ssl;server_nameyour_domain.com;ssl_certificate/path/to/your/ssl/certificate;ssl_certificate_k......
  • 适用于 .NET 的现代化、流畅、可测试的HTTP客户端库:Flurl
    适用于.NET的现代化、流畅、可测试的HTTP客户端库:Flurl前言今天大姚给大家分享一个.NET开源(MITLicense)、免费、现代化、流畅、可测试、可移植的URL构建器和HTTP客户端库:Flurl。项目介绍Flurl是一个集现代性、流畅性、异步性、可测试性、可移植性于一身的URL构建器与HTTP客......
  • https协议
    HTTPS(HypertextTransferProtocolSecure)协议是一种基于HTTP的安全通信协议,用于在计算机网络上安全地传输数据。它是在HTTP协议的基础上增加了SSL(安全套接层)或TLS(安全传输层协议)加密层,以确保数据在传输过程中的机密性、完整性和身份验证。以下是对HTTPS协议及其原理的详细......
  • HttpHeaders类详解,这一篇就够了
    目录一.总述二.案例展示三.set方法和add方法的区别1.set方法:2.add方法:区别总结:总结一.总述在Java中,HttpHeaders是一个用于表示HTTP请求或响应头的类,它属于java.net.http包,从Java11开始引入。这个类提供了一种方便的方式来操作HTTP消息头,包括添加、删除和获......
  • 03http和https
    <!DOCTYPEhtml><htmllang="en"><head>  <metacharset="UTF-8">  <metahttp-equiv="X-UA-Compatible"content="IE=edge">  <metaname="viewport"content="width=......
  • salesforce 通过 schedule job 去执行需要http访问外部网站的代码并更新自定义字段有
    在Salesforce中使用定时调度(ScheduledJobs)执行需要HTTP访问外部网站的代码,并更新自定义字段时,可能会面临以下一些常见的失败原因:网络访问限制:Salesforce的安全设置可能会限制对外部网站的HTTP访问。确保你的Salesforce实例可以安全地访问目标网站,通常需要配置网络代理或......
  • HttpClient用法
    HttpClient是ApacheJakartaCommon下的子项目,可以用来提供最新的,高效的,功能丰富的支持Http协议的客户端编程工具包,它支持HTTP最新的版本和协议,通过HTTPClient就可以构造Http请求并发送Http请求核心API:HttpClientHttpClientsCloseableHttpClientHttpGetHpptPost发送请......