美文网首页
Nodejs In Action (二)

Nodejs In Action (二)

作者: 醉饮满天星 | 来源:发表于2019-06-19 20:57 被阅读0次

    Connect

    Connect是一个框架, 它使用被称为中间件的模块化组件, 以可重用的方式实现Web程序中的逻辑。

    Connect中间件

    Middleware is computer software that connects software components or applications. The software consists of a set of services that allows multiple processes running on one or more machines to interact.

    image

    Connect可以按照一定的顺序配置一系列中间件,以连接浏览器请求、各种web端服务。常见可以加入一些log、认证、路由、错误中间件等。
    Connect可以使用挂载,将url路径映射到一个中间件上处理。
    在Web程序中,路由是个至关重要的概念。简言之,它会把请求URL映射到实现业务逻辑的函数上。

    在构建中间件时,你应该 关注那些小型的、可配置的部分。构建大量微小的、模块化的、可重用的中间件组件,合起来搭成你的程序。 保持中间件的小型化和专注性真的有助于将复杂的程序逻辑分解成更小的组成部分。

    Connect源码部分阅读

    1. ts文件
    declare function createServer(): createServer.Server;
    export interface Server extends NodeJS.EventEmitter {
    //可以看出,connect的export是一个高阶函数,这个函数返回如下函数
            (req: http.IncomingMessage, res: http.ServerResponse, next?: Function): void;
    // use函数,重载实现挂载功能
            use(fn: HandleFunction): Server;
            use(route: string, fn: HandleFunction): Server;
    
         // private,真正导出的是handle函数
            handle(req: http.IncomingMessage, res: http.ServerResponse, next: Function): void;
         // node http
            listen(port: number, hostname?: string, backlog?: number, callback?: Function): http.Server;
            ...
        }
    
    1. js文件
    function createServer() {
      function app(req, res, next){ app.handle(req, res, next); }
      merge(app, proto);
      merge(app, EventEmitter.prototype);
      app.route = '/';
      app.stack = [];
      return app;
    }
    proto.handle = function handle(req, res, out) {
        ...
      // connect的默认error handler
      var done = out || finalhandler(req, res, {
        env: env,
        onerror: logerror
      });
      //声明了一个next函数,从stack里面逐个取出中间件,路由校验以及执行,如果挂载,则会剔除匹配路由节点
      req.url = protohost + req.url.substr(protohost.length + removed.length);
      // next会调用一个call函数,执行中间件逻辑,会对是否产生error和中间件是否处理error进行判断,是否将error传递给中间件。如果前面的中间件产生了error而中间件不处理error,忽略此中间件,或者没有产生error,而当前中间件处理error,也会忽略,继续next
      function call(handle, route, err, req, res, next) {
        //...
      try {
        if (hasError && arity === 4) {
          // error-handling middleware
          handle(err, req, res, next);
          return;
        } else if (!hasError && arity < 4) {
          // request-handling middleware
          handle(req, res, next);
          return;
        }
      } catch (e) {
        // replace the error
        error = e;
      }
            // continue
         next(error);
        }
    }
        //本质上connect是一个http request的listener,当listen方法调用的时候,服务开 启
        proto.listen = function listen() {
      var server = http.createServer(this);
      return server.listen.apply(server, arguments);
    };
    

    可以看出,js中模块化,和面向对象的处理方式很相似。这里是使用高阶函数的方式对外暴露单一出口,通过mergemerge(app, EventEmitter.prototype);或者原型链的方式实现继承,通过文件内其他自执行的变量赋值,实现初始化。
    TODO:TypeScript声明文件生成

    挂载使用方式

    const api = connect().use(..).use(..)
    const app = connect().use(hello).use('/api',api)
    

    了解connect()本身就是一个(res,req,next)=>{},就能理解上述写法。


    image

    Connect自带的一些中间件

    新的Connect已经更加精简,不附加内置中间件,需要按需安装使用。这些中间件可以满足常见的web开发需求:如会话管理,cookie、body、qurey解析,日志等等,

    解析请求中间件

    • cookiePareser(通常是在客户端存放一个会话cookie sessionId之类,这样你就能在服务器端保留完整的用户状态。)
    • bodyParser
    • limit(跟bodyParser()联手防止读取过大的请求,过滤巨型请求,防止恶意拒绝服务攻击)
    • query

    实现 Web 程序核心功能的中间件

    • logger
    • favicon 处理/favicon.ico请求
    • methodOverride 让没有能力的客户端透明地重写req.method
    • vhost 在一个服务器上设置多个网站(虚拟主机)
    • session
      vhost()(虚拟主机)中间件是一种通过请求头Host路由请求的简单、轻量的办法。这项任务通常是由反向代理完成的,可以把请求转发到运行在不同端口上的本地服务器那里。
      session通过cookie储存,一共有两种方式:
    1. 在客户端cookie-session,不能超过浏览器的最大cookie容量限制4kb,
    2. 在服务端 express-session,储存在数据库。

    获取操作Cookie,可以使用Cookie包,服务端 res.setHeader('Set-Cookie','foo=bar;Expires=Tue,08 Jun 2021 10:18:14 GMT')可以指定客户端下次请求附带的cookie值(增量)
    Cookie options :

    • maxAge: a number representing the milliseconds from Date.now() for expiry
    • expires: a Date object indicating the cookie's expiration date (expires at the end of session by default).
    • path: a string indicating the path of the cookie (/ by default).
    • domain: a string indicating the domain of the cookie (no default).
    • secure: a boolean indicating whether the cookie is only to be sent over HTTPS (false by default for HTTP, true by default for HTTPS). Read more about this option below.
    • httpOnly: a boolean indicating whether the cookie is only to be sent over HTTP(S), and not made available to client JavaScript (true by default).
    • sameSite: a boolean or string indicating whether the cookie is a "same site" cookie (false by default). This can be set to 'strict', 'lax', or true (which maps to 'strict').
    • signed: a boolean indicating whether the cookie is to be signed (false by default). If this is true, another cookie of the same name with the .sig suffix appended will also be sent, with a 27-byte url-safe base64 SHA1 value representing the hash of cookie-name=cookie-value against the first Keygrip key. This signature key is used to detect tampering the next time a cookie is received.
    • overwrite: a boolean indicating whether to overwrite previously set cookies of the same name (false by default). If this is true, all cookies set during the same request with the same name (regardless of path or domain) are filtered out of the Set-Cookie header when setting this cookie.

    处理 Web 程序安全的中间件

    • basicAuth() 为保护数据提供了HTTP基本认证;
    • csrf() 实现对跨站请求伪造(CSRF)攻击的防护;
    • errorHandler() 帮你在开发过程中进行调试。

    提供静态文件服务的中间件

    • static() 将文件系统中给定根目录下的文件返回给客户端
    • compress() 压缩响应,很适合跟static()一起使用
    • directory() 当请求的是目录时,返回那个目录的列表

    static

    Connect的static()中间件实现了一个高性能的、灵活的、功能丰富的静态文件服务器,支持HTTP缓存机制、范围请求等。更重要的是,它有对恶意路径的安全检查,默认不允许访问隐藏文件(文件名以.开头) ,会拒绝有害的null字节。static()本质上是一个非常安全的、完全能胜任的静态文件服务中间件,可以保证跟目前各种HTTP客户端的兼容。
    在static中,路径实现对于当前工作目录的,也就是说将"public"作为路径传入会被解析为process.cwd() + "public"。
    然而有时你可能想用绝对路径指定根目录,变量__dirname可以帮你达成这一目的: app.use('/app/files', connect.static(__dirname + '/public'));

    compress():压缩静态文件

    zlib模块给开发人员提供了一个用gzip和deflate压缩及解压数据的机制。Connect 2.0及以上版本在HTTP服务器层面提供了zlib,用compress()中间件压缩出站数据。
    compress()组件通过请求头域Accept-Encoding自动检测客户端可接受的编码。 如果请求头中没有该域, 则使用相同的编码, 也就是说不会对响应做处理。 如果请求头的该域中包含gzip、deflate或两个都有,则响应会被压缩。
    在Connect组件栈中,一般应该尽量把compress()放在靠上的位置,因为它包着res.write() 和res.end()方法。

    Express

    跟Django或RoR之类的框架比起来, Express非常小。 Express的主导思想是程序的需求和实现变化非常大,使用轻量的框架可以打造出你恰好需要的东西,不会引入任何你不需要的东西。
    Express和整个Node社区都致力于做出更小的,模块化程度更高的功能实现,而不是一个整体式框架。
    express是构建于connect之上的。
    下面完成一个照片分享的示例:
    生成程序的初始结构:
    使用EJS模板引擎生成express脚手架:express -e photo
    初始化配置:
    app.set(),app.enable()...

    视图系统

    视图渲染引擎:数据传给引擎,被转换成视图,通常是Web程序中的HTML,如jsp、ejs、php。

    1. 配置
    //设定视图目录
    app.set('views', path.join(__dirname, 'views'));
    //设定视图引擎
    app.set('view engine', 'ejs');
    //视图缓存
    app.set('view cache', true);
    
    1. 视图查找
      当res.render()或app.render() 被调用时, Express会先检查是否有文件在这个绝对路径上。 接着会找8.3.1节讨论的视图目录设定的相对路径。最后,Express会尝试使用index文件。
    2. 把数据输出到视图中
      参见数据输出以及ejs规范

    Express图片上传以及下载小项目

    使用mongoose连接mlab上申请的一个数据库(明天安装本地的吧),通过本地服务留存上传文件,将文件路径写入远程mongoose数据库。
    服务器存的图片作为静态资源,明天测试一下下载。


    image.png

    相关文章

      网友评论

          本文标题:Nodejs In Action (二)

          本文链接:https://www.haomeiwen.com/subject/gkplqctx.html