Nodejs In Action (二)

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容