Skip to content

Latest commit

 

History

History
166 lines (123 loc) · 6.6 KB

第3章:成为可访问的页面.md

File metadata and controls

166 lines (123 loc) · 6.6 KB

第3章:成为可访问的页面

我们的Todo目前看上去还不错,但是如果我们把目光转向地址栏,就会发现我们的地址和别人的长得好像不一样。

我们的地址栏为开头为:file://,这代表它是一个本地文件,如果你让你的小伙伴在他的电脑上输入相同的地址,不出意外的话什么都打不开。而所有人能够访问的网站,例如百度,是以*http://https://*为开头的,这说明这是一个HTTP请求。


什么是HTTP

HTTP全称超文本传输协议(HyperText Transfer Protocol),设计之初的目的就是用于传输HTML。现在HTTP请求用于应答服务器上存储的各种资源,例如图片和视频。

通常,由客户端(例如浏览器)发起一个HTTP请求,服务端负责接受并处理。

HTTP请求是无状态的,两个请求间无法互相通信,因此无法用HTTP直接实现前后端持续互动。


一个HTTP的组成

请求

HTTP请求本质上是由多行数据构成的字符串文本,由三部分组成:

1.请求行

请求行包括请求的方法,路径和协议版本,例如:

GET /home HTTP/1.1

在这个请求行中,请求的方法是指GET,请求方法主要有:

  • GET 读取资源
  • POST 提交资源
  • PUT 更新资源
  • DELETE 删除资源
  • HEAD 获取资源的相关信息
  • OPTIONS 获取资源支持的所有请求方法

路径是指 /home,指要获取的资源的路径。

协议版本是指 HTTP/1.1,代表HTTP协议的版本号。

2.头

头信息携带着其它可选择的信息,例如接受的请求,语言,编码方式,Cookie等。

下图就是一个典型的头信息。

一个典型的头信息

3. 空行

请求行必须以<CR><LF>作为结尾,空行内必须只有<CR><LF>而无其它空格。换行时也使用<CR><LF>来连接。

响应

响应是服务器对请求的应答,也由三部分组成:

1. 状态行

状态行分为请求的状态码,状态信息和协议版本。

状态码来告知请求的成功或失败,以及失败的原因,状态码的第一个数字代表响应的类型:

  • 1XX消息——请求已经被服务器接受,急需处理
  • 2XX成功——请求已经被服务器接受理解并处理
  • 3XX重定向——需后续操作才能完成请求
  • 4XX请求错误——请求含有错误
  • 5XX服务器错误——服务器在处理正确的请求时发生错误

状态信息是非权威的状态码描述信息,也就是说服务端可以自行设定。RFC 2616推荐了状态信息,但是我们也可以视情况设定更合适的描述信息。

2.头

响应的头信息和请求的头信息相似。

3.空行

与请求中的空行一致。


一个HTTP请求的旅行

  1. 当客户端想要和后端(服务端)进行信息交互时,客户端就会打开一个(或重用之前的一个)TCP连接,发送一个HTTP请求到后端。
  2. 后端解析请求信息,针对请求进行操作处理。编写操作处理的逻辑是后端程序员的工作之一。
  3. 后端构建响应信息,返回给客户端。
  4. 客户端处理响应信息,视情况进行跳转,重绘等操作。
  5. 等待一段时间后关闭连接。

使用Node.js编写后端应用

后端应用依赖于服务器运行环境,事实上就是针对某种语言实现对HTTP请求(事实上大多是对TCP连接)的处理。 除了JavaScript之外,还有很多可以使用的后端语言,例如Ruby,Python,Java,PHP,事实上C++也可以用来编写服务端应用。

下面我们就使用Node.js编写一个后端应用,我们希望访问:http://localhost:3000/时,能够显示我们的todo list应用。

如果你尚未安装过Node.js,你需要去Node.js的官网下载并安装

在我们之前编写的todo应用的根目录下新建一个文件,命名为server.js,写下如下代码:

//引入http模块
const http = require('http')
//创建http服务器
http.createServer((req, res) => {
  //写入字符串并返回
  res.end('Hello World')
}).listen(3000)

类似于http这样的模块在Node.js中还有很多,它们属于自带模块,你可以参考Node.js官方网站的api进行浏览或查阅。

使用任意命令行程序,切换到当前目录下,输入node server运行我们的服务器。

打开浏览器,输入http://localhost:3000/,如果你成功看到了"Hello World",那么服务器就成功的运行起来了。

我们现在需要让它返回之前写好的todo应用,使用Ctrl/Commond+c停止服务器(每次修改服务端应用后,我们都要重启服务器,修改才能生效),重新打开server.js文件进行编辑,这次我们写下如下代码:

//引入http模块
const http = require('http')
//引入filesystem模块
const fs = require('fs')
//创建http服务器
http.createServer((req, res) => {
  //使用fs模块读取todo.html文件
  res.end(fs.readFileSync('./todo.html'))
}).listen(3000)

重新打开http://localhost:3000/,这时我们就能看到我们的html文件了。

但是你会发现我们之前定义的样式无法显示,js脚本也无法正常运行。这是因为当HTML请求todo.css和todo.js文件时,我们的服务端应用不知道该如何处理这类请求,依然会像处理其它请求一样返回todo.html文件。

再次修改server.js,这次,我们添加对静态资源文件的处理:

//引入http模块
const http = require('http')
//引入filesystem模块
const fs = require('fs')
//引入url模块
const url = require('url')
//引入path模块
const path = require('path')
//创建http服务器
http.createServer((req, res) => {
  const pathname = url.parse(req.url).pathname
  //获取请求的文件后缀
  const ext = path.extname(pathname).slice(1)
  switch(ext) {
    //如果后缀是js
    case 'js': {
      res.writeHead(200, {'Content-Type': 'text/javascript'})
      res.end(fs.readFileSync('./todo.js'))
      break
    }
    //如果后缀是css
    case 'css': {
      res.writeHead(200, {'Content-Type': 'text/css'})
      res.end(fs.readFileSync('./todo.css'))
      break
    }
    //其它情况
    default: {
      //使用fs模块读取todo.html文件
      res.write(fs.readFileSync('./todo.html'))
      res.end()
    }
  }
}).listen(3000)

重启服务器,访问http://localhost:3000/,这下我们的todo应用终于完整的显示出来了。