【前端】node.js学习笔记--第六章

第六章 请求和响应对象

在用 Express 构建 Web 服务器时,大部分工作都是从请求对象开始,到响应对象终止。这 两个对象起源于 Node,Express 对其进行了扩展。

6.1 url的组成部分

  1. 协议 协议确定如何传输请求。我们主要是处理 http 和 https。其他常见的协议还有 file 和 ftp。
  2. 主机名 主机名标识服务器。运行在本地计算机(localhost)和本地网络的服务器可以简单地表 示,比如用一个单词,或一个数字 IP 地址。在 Internet 环境下,主机名通常以一个顶 级域名(TLD)结尾,比如 .com 或 .net。另外,也许还会有子域名作为主机名的前缀。子域名可以是任何形式的,其中 www 最为常见。子域名通常是可选的。
  3. 端口
    每一台服务器都有一系列端口号。一些端口号比较“特殊”,如 80 和 443 端口。如果 省略端口值,那么默认 80 端口负责 HTTP 传输,443 端口负责 HTTPS 传输。如果不使 用 80 和 443 端口,就需要一个大于 10231 的端口号。通常使用容易记忆的端口号,如 3000、8080 或 8088。
  4. 路径
    URL 中影响应用程序的第一个组成部分通常是路径(在考虑协议、主机名和端口的基 础上做决定很合理,但是不够好)。路径是应用中的页面或其他资源的唯一标识。
  5. 查询字符串
    查询字符串是一种键值对集合,是可选的。它以问号(?)开头,键值对则以与号(&) 分隔开。所有的名称和值都必须是 URL 编码的。对此,JavaScript 提供了一个嵌入式的 函数 encodeURIComponent 来处理。例如,空格被加号(+)替换。其他特殊字符被数字 型字符替换。
  6. 信息片段
    信息片段(或散列)被严格限制在浏览器中使用,不会传递到服务器。用它控制单页应 用或 AJAX 富应用越来越普遍。最初,信息片段只是用来让浏览器展现文档中通过锚点 标记()指定的部分。

6.2HTTP请求方法

HTTP 协议确定了客户端与服务器通信的请求方法集合(通常称为 HTTP verbs)。很显然, GET 和 POST 最为常见。

对于一个网站来说,大部分页面都响应 GET 请求。POST 请求通常用来提交信息到服务器后 台(例如表单处理)。服务器将请求中包含的所有信息(例如表单)处理完成之后,用以 响应的 HTML 通常与相应的 GET 请求是一样的。与服务器通信时,浏览器只使用 GET 和 POST 方法(如果没有使用 AJAX)。

6.3请求报头

请求包头会隐含很多其他的信息,比如:“用 户代理”信息(浏览器、操作系统和硬件设备)和其他一些信息,
。所有能够确保你了解请 求对象头文件属性的信息都将会作为请求报头发送,如果想查看浏览器发送的信息,可以 创建一个非常简单的 Express 路由来展示一下:

app.get('/headers', function(req,res){     res.set('Content-Type','text/plain');     var s = '';     for(var name in req.headers) s += name + ': ' + req.headers[name] + '\n';     res.send(s); });

6.4响应报头

正如浏览器以请求报头的形式发送隐藏信息到服务器,当服务器响应时,同样会回传一些 浏览器没必要渲染和显示的信息,通常是元数据和服务器信息。我们已经熟悉内容类型头 信息,它告诉浏览器正在被传输的内容类型(网页、图片、样式表、客户端脚本等)。特 别要注意的是,不管 URL 路径是什么,浏览器都根据内容类型报头处理信息。因此你可 以通过一个叫作 /image.jpg 的路径提供网页,也可以通过一个叫作 /text.html 的路径提供图 片。(这样做并不合情理,这里要讲的重点是路径是抽象的,浏览器只根据内容类型来决 定内容该如何渲染。)除了内容类型之外,报头还会指出响应信息是否被压缩,以及使用 的是哪种编码。响应报头还可以包含关于浏览器对资源缓存时长的提示。优化网站时需要 着重考虑这一点,我们将在第 16 章详细讨论。响应报头还经常会包含一些关于服务器的 信息,一般会指出服务器的类型,有时甚至会包含操作系统的详细信息。返回服务器信息 存在一个问题,那就是它会给黑客一个可乘之机,从而使站点陷入危险。非常重视安全的 服务器经常忽略此信息,甚至提供虚假信息。

禁用 Express 的 X-Powered-By 头信息很简单:

app.disable('x-powered-by');

6.8请求对象

请求对象(通常传递到回调方法,这意味着你可以随意命名,通常命名为 req 或 request)的生命周期始于 Node 的一个核心对象 http.IncomingMessage 的实例。Express 添加了一些 附加功能。我们来看看请求对象中最有用的属性和方法(除了来自 Node 的 req.headers 和 req.url,所有这些方法都由 Express 添加)。

  • req.params 一个数组,包含命名过的路由参数。我们将在第 14 章进行详细介绍。
  • req.param(name) 返回命名的路由参数,或者 GET 请求或 POST 请求参数。建议你忽略此方法。
  • req.query 一个对象,包含以键值对存放的查询字符串参数(通常称为 GET 请求参数)。
  • req.body 一个对象,包含 POST 请求参数。这样命名是因为 POST 请求参数在 REQUEST 正文中传 递,而不像查询字符串在 URL 中传递。要使 req.body 可用,需要中间件能够解析请求 正文内容类型,我们将在第 10 章进行详细介绍。
  • req.route 关于当前匹配路由的信息。主要用于路由调试。
  • req.cookies/req.singnedCookies 一个对象,包含从客户端传递过来的 cookies 值。
  • req.cookies/req.singnedCookies 一个对象,包含从客户端传递过来的 cookies 值。
  • req.headers 从客户端接收到的请求报头。
  • req.accepts([types]);
    一个简便的方法,用来确定客户端是否接受一个或一组指定的类型(可选类型可以是 单个的 MIME 类型,如 application/json、一个逗号分隔集合或是一个数组)。写公共 API 的人对该方法很感兴趣。假定浏览器默认始终接受 HTML。
  • req.ip 客户端的IP地址
  • req.path 请求路径(不包含协议、主机、端口或查询字符串)。
  • req.host 一个简便的方法。用来返回客户端所报告的主机名。这些信息可以伪造,所以不应该用于安全目的。
  • req.xhr 一个简便属性,如果请求由 Ajax 发起将会返回 true.
  • req.protocol 用于标识请求的协议(http 或 https)。
  • req.secure 一个简便属性,如果连接是安全的,将返回 true。等同于 req.protocol===‘https’
  • req.url/req.originalUrl 有点用词不当,这些属性返回了路径和查询字符串(它们不包含协议、主机或端口)。 req.url 若是出于内部路由目的,则可以重写,但是 req.orginalUrl 旨在保留原始请求 和查询字符串。
  • req.acceptedLanguages•
    一个简便方法,用来返回客户端首选的一组(人类的)语言。这些信息是从请求报头中 解析而来的。

6.9响应对象

响应对象(通常传递到回调方法,这意味着你可以随意命名它,通常命名为 res、resp 或 response)的生命周期始于 Node 核心对象 http.ServerResponse 的实例。Express 添加了一 些附加功能

  • res.status(code) 设置 HTTP 状态代码。Express 默认为 200(成功),所以你可以使用这个方法返回状态 404(页面不存在)或 500(服务器内部错误),或任何一个其他的状态码。对于重定向 (状态码 301、302、303 和 307),有一个更好的方法:redirect。

  • res.set(name,value)
    设置响应头。这通常不需要手动设置。

  • res.cookie(name,vaue,[options]),res.clearCookie(name,[options])•
    设置或清除客户端 cookies 值。需要中间件支持

  • res.redirect([status],url)
    重定向浏览器。默认重定向代码是 302(建立)。通常,你应尽量减少重定向,除非永 久移动一个页面,这种情况应当使用代码 301(永久移动)。

  • res.send(body),res.send(status,body)
    向客户端发送响应及可选的状态码。Express 的默认内容类型是 text/html。如果你想改 为 text/plain,需要在 res.send 之前调用 res.set(‘Content-Type’,‘text/plain’)。如 果 body 是一个对象或一个数组,响应将会以 JSON 发送(内容类型需要被正确设置), 不过既然你想发送 JSON,我推荐你调用 res.json。

  • res.json(json),res.json(status,json)
    向客户端发送 JSON 以及可选的状态码。

  • res.jsonp(json),req.jsonp(status,json)
    向客户端发送 JSONP 及可选的状态码。

  • res.type(type)
    一个简便的方法,用于设置 Content-Type 头信息。基本上相当于 res.set(‘ContentType’,‘type’),只是如果你提供了一个没有斜杠的字符串,它会试图把其当作文件的 扩展名映射为一个互联网媒体类型。比如,res.type(‘txt’) 会将 Content-Type 设为 text/plain。此功能在有些领域可能会有用(例如自动提供不同的多媒体文件),但是 通常应该避免使用它,以便明确设置正确的互联网媒体类型。

  • res.format(object)
    这个方法允许你根据接收请求报头发送不同的内容。这是它在 API 中的主要用途,我们 将会在第 15 章详细讨论。这里有一个非常简单的例子:res.format({‘text/plain’:‘hi there’,‘text/html’:‘hi there’})。

  • res.attachment([filename]),res.download(path,[filename],[callback])
    这两种方法会将响应报头 Content-Disposition 设为 attachment,这样浏览器就会选 择下载而不是展现内容。你可以指定 filename 给浏览器作为对用户的提示。用 res. download 可以指定要下载的文件,而 res.attachment 只是设置报头。另外,你还要将 内容发送到客户端。

  • res.sendFile(path,[option],[callback])
    这个方法可根据路径读取指定文件并将内容发送到客户端。使用该方法很方便。使用静 态中间件,并将发送到客户端的文件放在公共目录下,这很容易。然而,如果你想根据 条件在相同的 URL 下提供不同的资源,这个方法可以派上用场。

  • res.links(links)
    设置链接响应报头。这是一个专用的报头,在大多数应用程序中几乎没有用处。

  • res.locals,res.render(view,[locals],callback)

res.locals 是一个对象,包含用于渲染视图的默认上下文。res.render 使用配置的模
板引擎渲染视图(不能把 res.render 的 locals 参数与 res.locals 混为一谈,上下文 在 res.locals 中会被重写,但在没有被重写的情况下仍然可用)。res.render 的默认响 应代码为 200,使用 res.status 可以指定一个不同的代码。

内容渲染

大多数情况下,渲染内容用 res.render,它最大程度地根据布局渲染视图。如果想写一 个快速测试页,也许会用到 res.send。你可以使用 req.query 得到查询字符串的值,使用 req.session 得到会话值,或使用 req.cookie/req.singedCookies 得到 cookies 值。

基本用法:

// 基本用法 app.get('/about', function(req, res){         res.render('about'); });

200以外的响应码:
app.get('/error', function(req, res){         res.status(500);         res.render('error'); }); // 或是一行…… app.get('/error', function(req, res){         res.status(500).render('error'); });


将上下文传递给视图(包括查询字符串、cookie、session值):
app.get('/greeting', function(req, res){          res.render('about', {                  message: 'welcome',                  style: req.query.style,                  userid: req.cookie.userid,                  username: req.session.username,          }); });

没有布局的视图渲染:
// 下面的 layout 没有布局文件,即 views/no-layout.handlebars // 必须包含必要的 HTML app.get('/no-layout', function(req, res){         res.render('no-layout', { layout: null }); });

使用定制的试图渲染文件(handlerbars)
// 使用布局文件 views/layouts/custom.handlebars 
请求和响应对象   |   57
app.get('/custom-layout', function(req, res){         res.render('custom-layout', { layout: 'custom' }); });

渲染纯文本输出:
app.get('/test', function(req, res){         res.type('text/plain');         res.send('this is a test'); });

添加错误处理程序:
// 这应该出现在所有路由方法的结尾 // 需要注意的是,即使你不需要一个 " 下一步 " 方法 // 它也必须包含,以便 Express 将它识别为一个错误处理程序 app.use(function(err, req, res, next){         console.error(err.stack);         res.status(500).render('error'); });


添加一个404处理程序:
// 这应该出现在所有路由方法的结尾 app.use(function(req, res){         res.status(404).render('not-found'); });

处理表单

当你处理表单时,表单信息一般在 req.body 中(或者偶尔在 req.query 中)。你可以使用 req.xhr 来判断是 AJAX 请求还是浏览请求(第 8 章将深入讨论)。

基本表单处理

// 必须引入中间件 body-parser app.post('/process-contact', function(req, res){         console.log('Received contact from ' + req.body.name +                 ' <' + req.body.email + '>');         // 保存到数据库……         res.redirect(303, '/thank-you'); });

更强大的表单处理:
// 必须引入中间件 body-parser app.post('/process-contact', function(req, res){         console.log('Received contact from ' + req.body.name +                 ' <' + req.body.email + '>');         try {                 // 保存到数据库…… 
 
                return res.xhr ? 
                        res.render({ success: true }) :                         res.redirect(303, '/thank-you');         } catch(ex) {                 return res.xhr ?                         res.json({ error: 'Database error.' }) :                         res.redirect(303, '/database-error');         } });

6.11.3 提供API

数据示例:

var tours = [         { id: 0, name: 'Hood River', price: 99.99 },         { id: 1, name: 'Oregon Coast', price: 149.95 }, ];

简单的get请求,只返回JSON数据

app.get('/api/tours'), function(req, res){         res.json(tours); });
示例 6-12 根据客户端的首选项,使用 Express 中的 res.format 方法对其响应。

GET 节点,返回 JSON、XML 或 text :

app.get('/api/tours', function(req, res){         var toursXml = '<?xml version="1.0"?><tours>' +                 products.map(function(p){                         return '<tour price="' + p.price +                                 '" id="' + p.id + '">' + p.name + '</tour>';                 }).join('') + '</tours>'';         var toursText = tours.map(function(p){                         return p.id + ': ' + p.name + ' (' + p.price + ')';                 }).join('\n');         res.format({                 'application/json': function(){                         res.json(tours);                 },                 'application/xml': function(){ 
请求和响应对象   |   59
                         res.type('application/xml');                          res.send(toursXml);                  },                  'text/xml': function(){                          res.type('text/xml');                          res.send(toursXml);                  }                  'text/plain': function(){                          res.type('text/plain');                          res.send(toursXml);                  }          }); });

用于更新的PUT结点
//API 用于更新一条数据并且返回 JSON;参数在查询字符串中传递  app.put('/api/tour/:id', function(req, res){         var p = tours.some(function(p){ return p.id == req.params.id });         if( p ) {                 if( req.query.name ) p.name = req.query.name;                 if( req.query.price ) p.price = req.query.price;                 res.json({success: true});         } else {                 res.json({error: 'No such tour exists.'});         } });
最后,示例 6-14 展示了一个 DEL 节点。
用于删除的DEL结点
// API 用于删除一个产品 api.del('/api/tour/:id', function(req, res){         var i;         for( var i=tours.length-1; i>=0; i-- )                 if( tours[i].id == req.params.id ) break;         if( i>=0 ) {                 tours.splice(i, 1);                 res.json({success: true});         } else {                 res.json({error: 'No such tour exists.'});         } });
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值