zoukankan      html  css  js  c++  java
  • 03 请求响应原理及HTTP协议

    1. 服务器端基础概念

    1.1 网站的组成

    网站应用程序主要分为两大部分:客户端和服务器端。

    客户端:在浏览器中运行的部分,就是用户看到并与之交互的界面程序。使
    用HTML、CSS、JavaScript构建。

    服务器端:在服务器中运行的部分,负责存储数据和处理应用逻辑。

    1.2 Node网站服务器

    能够提供网站访问服务的机器就是网站服务器,它能够接收客户端的请求,能够对请求做出响应。

    1.3 IP地址

    互联网中设备的唯一标识。

    IP是Internet Protocol Address的简写,代表互联网协议地址.

    1.4 域名

    由于IP地址难于记忆,所以产生了域名的概念,所谓域名就是平时上网所使用的网址。

    http://www.itheima.com => http://124.165.219.100/

    虽然在地址栏中输入的是网址, 但是最终还是会将域名转换为ip才能访问到指定的网站服务器。

    1.5 端口

    端口是计算机与外界通讯交流的出口,用来区分服务器电脑中提供的不同的服务。

    1.6 URL

    统一资源定位符,又叫URL(Uniform Resource Locator),是专为标识Internet网上资源位置而设的一种编址方式,我们平时所说的网页地址指的即是URL。

    url的组成

    传输协议://服务器IP或域名:端口/资源所在位置标识

    http://www.baidu.cn/news/20181018/09152238514.html

    http:超文本传输协议,提供了一种发布和接收HTML页面的方法。

    1.7 开发过程中客户端和服务器端说明

    在开发阶段,客户端和服务器端使用同一台电脑,即开发人员电脑。

    客户端 (浏览器)

    服务器端(Node)

    本机域名:localhost
    本地IP :127.0.0.1

    2.创建web服务器

    浏览器输入localhost:3000 访问

      // 引用系统模块
     const http = require('http');
      // 创建web服务器
     const app = http.createServer();
      // 当客户端发送请求的时候
     app.on('request', (req, res) => {
            //  响应
           res.end('<h1>hi, user</h1>');
     });
      // 监听3000端口
     app.listen(3000);
     console.log('服务器已启动,监听3000端口,请访问 localhost:3000')
    

    3.HTTP协议

    3.1 HTTP协议概念

    超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)规定了如何从网站服务器传输超文本到本地浏览器,它基于客户端服务器架构工作,是客户端(用户)和服务器端(网站)请求和应答的标准。

    3.2 报文

    在HTTP请求和响应的过程中传递的数据块就叫报文,包括要传送的数据和一些附加信息,并且要遵守规定好的格式。

    3.3 请求报文

    1. 请求方式(request method)

    GET 请求数据

    POST 发送数据

     app.on('request', (req, res) => {
         req.headers  // 获取请求报文
         req.url      // 获取请求地址
         req.method   // 获取请求方法
     });
    

    演示

    // 创建网站服务器的模块
    const http = require('http')
    
    // app 对象就是网站服务对象
    const app = http.createServer()
    
    // 当客户端有请求来的时候
    app.on('request', (req, res) => {
    
        // 获取请求报文信息
        // req.headers
        console.log(req.headers);
        console.log(req.headers['accept']);
    
        // 获取请求地址
        // req.url
        // console.log(req.url);
        if (req.url == '/index' || '/') {
            res.end('index')
        } else if (req.url == '/list') {
            res.end('list')
        } else {
            res.end('not found')
        }
    
        // 获取请求方式
        // req.method
    
        if (req.method == "POST") {
            res.end('post')
        } else if (req.method == "GET") {
            res.end('get')
    
        }
    
    })
    
    // 监听端口
    app.listen(8080)
    console.log("网站服务器启动成功");
    

    3.4 响应报文

    1.HTTP状态码

    200 请求成功
    404 请求的资源没有被找到
    500 服务器端错误
    400 客户端请求有语法错误

    2.内容类型

    text/html
    text/css
    application/javascript
    image/jpeg
    application/json

     app.on('request', (req, res) => {
         // 设置响应报文
         res.writeHead(200, {         '
    	Content-Type': 'text/html;charset=utf8‘
         });
     });
    

    4. HTTP请求与响应处理

    4.1 请求参数

    客户端向服务器端发送请求时,有时需要携带一些客户信息,客户信息需要通过请求参数的形式传递到服务器端,比如登录操作。

    4.2 GET请求参数

    • 参数被放在浏览器地址中 http://localhost:3000/?name=zhangsan&age=20

    • 参数获取需要借助系统模块url,url模块用来处理url地址

      // 创建网站服务器的模块
      const http = require('http')

      // 处理url地址
      const url = require('url')

      // app 对象就是网站服务对象
      const app = http.createServer()

      // 当客户端有请求来的时候
      app.on('request', (req, res) => {
      // 设置相应报文
      res.writeHead(200, {
      // 返回类型
      'content-type': 'text/html;charset=utf8' // 纯文本 设置中文编码
      })

        // url解析
        console.log('url   ' + req.url);
        // 第一个参数解析URl,第二个参数查询参数转换为对象形式保存
        let { query, pathname } = url.parse(req.url, true)
        console.log(query.name);
        console.log(query.age);
      
        // 获取请求地址
        // req.url
        // console.log(req.url);
        if (pathname == '/index' || pathname == '/') {
            res.end('<h2>来到index</h2>')
        } else if (pathname == '/list') {
            res.end('list')
        } else {
            res.end('not found')
        }
      
        // 获取请求方式
        // req.method
      
        if (req.method == "POST") {
            res.end('post')
        } else if (req.method == "GET") {
            res.end('get')
        }
      

      })

      // 监听端口
      app.listen(8080)
      console.log("网站服务器启动成功");

    4.3 POST请求参数

    • 参数被放置在请求体中进行传输

    • 获取POST参数需要使用data事件和end事件

    • 使用querystring系统模块将参数转换为对象格式

    // 创建网站服务器的模块
    const http = require('http')

    // app 对象就是网站服务对象
    const app = http.createServer()

    // 处理请求参数模块
    const querystring = require('querystring')

    html文件

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    
    <body>
        <!-- method:指定当前表单提交方式 -->
        <!-- action: 指定当前表单提交的地址 -->
        <form action="http://localhost:8080" method="post">
            <input type="text" name="username" id="">
            <input type="password" name="password" id="">
            <input type="submit" name="" id=""></form>
    </body>
    
    </html>
    

    js文件

    // 当客户端有请求来的时候
    app.on('request', (req, res) => {
        // post参数是通过事件的方式接收
        // data 请求参数传递的时候触发data事件
        // end 请求参数传递完成的时候触发end事件
    
        let postParams = ''
    
        // params:当前传递过来的参数
        req.on('data', params => {
            postParams += params
        })
        req.on('end', () => {
            // 参数转换字符串对象
            console.log(querystring.parse(postParams));
        })
    
        // 给客户端一个响应内容
        res.end('ok')
    })
    
    // 监听端口
    app.listen(8080)
    console.log("网站服务器启动成功");
    

    4.4 路由

    http://localhost:3000/index
    http://localhost:3000/login
    路由是指客户端请求地址与服务器端程序代码的对应关系。简单的说,就是请求什么响应什么。

    4.5 静态资源

    服务器端不需要处理,可以直接响应给客户端的资源就是静态资源,例如CSS、JavaScript、image文件。

    4.6 动态资源

    相同的请求地址不同的响应资源,这种资源就是动态资源。

    // 1.创建网站服务器的模块
    const http = require('http')
    const url = require('url')
    const path = require('path')
    const fs = require('fs')
    const mime = require('mime')
    
    
    // 2.创建网站服务器
    const app = http.createServer()
    
    // 3.监听请求事件
    app.on('request', (req, res) => {
    
        // 获取用户请求路径
        let pathname = url.parse(req.url).pathname
        pathname = pathname == '/' ? '/default.html' : pathname
    
        // 根据路径返回资源类型
        let type = mime.getType(pathname)
    
        // __dirname:获取当前文件所在路径
        // 将用户请求路径转换为实际的服务器硬盘路径
        resPath = path.join(__dirname, 'pulic', pathname)
            // res.end(resPath)
        console.log(resPath);
    
    
        // 读取文件
        fs.readFile(resPath, (error, result) => {
            // 如果文件读取出错
            if (error != null) {
                res.writeHead(404, {
                    'content-type': 'text/html;charset=utf8'
                })
                res.end('文件读取失败');
                return;
            }
            res.writeHead(200, {
                'content-type': type
            })
    
            res.end(result);
        })
    
    })
    
    // 监听端口 
    app.listen(8080)
    console.log("网站服务器启动成功");
    

    5. Node.js异步编程

    5.1 同步API, 异步API

    同步API:只有当前API执行完成后,才能继续执行下一个API

    console.log('before'); 
    console.log('after');
    

    异步API:当前API的执行不会阻塞后续代码的执行

     // 路径拼接
     const public = path.join(__dirname, 'public');
     // 请求地址解析
     const urlObj = url.parse(req.url);
     // 读取文件
     fs.readFile('./demo.txt', 'utf8', (err, result) => {
         console.log(result);
     });
    

    5.2 同步API, 异步API的区别( 获取返回值 )

    同步API可以从返回值中拿到API执行的结果, 但是异步API是不可以的

    function getMsg() {
        setTimeout(function() {
            return {
                msg: 'hello'
            }
        }, 2000)
    }
    // 执行这个的时候 没过两秒拿不到返回值 
    const msg = getMsg()
    console.log(msg); 
    

    5.3 回调函数

    怎么才能拿到异步API的返回值,自己定义函数让别人去调用。

    function getMsg(callback) {
        setTimeout(function() {
            callback({
                msg: 'hello'
            })
        }, 2000)
    }
    getMsg(function(data) {
        console.log(data);
    })
    

    5.4 5.5 同步API, 异步API的区别(代码执行顺序)

    • 同步API从上到下依次执行,前面代码会阻塞后面代码的执行

    演示

    for (var i = 0; i < 100000; i++) { 
        console.log(i);
    }
    console.log('for循环后面的代码');
    
    • 异步API不会等待API执行完成后再向下执行代码

    演示

    console.log('代码开始执行'); 
    setTimeout(() => { console.log('2秒后执行的代码')}, 2000);
    setTimeout(() => { console.log('"0秒"后执行的代码')}, 0); 
    console.log('代码结束执行');
    

    先执行同步,后执行异步(先执行即将开始的)

    5.6 Node.js中的异步API

    1

    fs.readFile('./demo.txt', (err, result) => {});
    

    2

    var server = http.createServer();
    server.on('request', (req, res) => {});
    

    如果异步API后面代码的执行依赖当前异步API的执行结果,但实际上后续代码在执行的时候异步API还没有返回结果,这个问题要怎么解决呢?

    fs.readFile('./demo.txt', (err, result) => {});
    console.log('文件读取结果');
    
    需求:依次读取A文件、B文件、C文件
    
    const fs = require('fs')
    // 回调地狱
    fs.readFile('./1.txt', 'utf8', (err, res1) => {
        console.log(res1);
        fs.readFile('./2.txt', 'utf8', (err, res2) => {
            console.log(res2);
            fs.readFile('./3.txt', 'utf8', (err, res3) => {
                console.log(res3);
            })
        })
    })
    

    5.8 Promise

    Promise出现的目的是解决Node.js异步编程中回调地狱的问题。

    十分臃肿

    const fs = require('fs')
        // 回调地狱
        // fs.readFile('./1.txt', 'utf8', (err, res1) => {
        //     console.log(res1);
        //     fs.readFile('./2.txt', 'utf8', (err, res2) => {
        //         console.log(res2);
        //         fs.readFile('./3.txt', 'utf8', (err, res3) => {
        //             console.log(res3);
        //         })
        //     })
        // })
    
    function p1() {
        return new Promise((reslove, reject) => {
            fs.readFile('./1.txt', 'utf8', (err, res) => {
                reslove(res)
            })
        })
    }
    
    function p2() {
        return new Promise((reslove, reject) => {
            fs.readFile('./2.txt', 'utf8', (err, res) => {
                reslove(res)
            })
        })
    }
    
    function p3() {
        return new Promise((reslove, reject) => {
            fs.readFile('./3.txt', 'utf8', (err, res) => {
                reslove(res)
            })
        })
    }
    
    p1().then((r1) => {
        console.log(r1);
        return p2()
    }).then((r2) => {
        console.log(r2);
        return p3()
    }).then((r3) => {
        console.log(r3);
    })
    

    5.9 异步函数

    异步函数是异步编程语法的终极解决方案,它可以让我们将异步代码写成同步的形式,让代码不再有回调函数嵌套,使代码变得清晰明了。

    const fn = async () => {};
    async function fn () {}
    

    async关键字

    1. 普通函数定义前加async关键字 普通函数变成异步函数
    2. 异步函数默认返回promise对象
    3. 在异步函数内部使用return关键字进行结果返回 结果会被包裹的promise对象中 return关键字代替了resolve方法
    4. 在异步函数内部使用throw关键字抛出程序异常
    5. 调用异步函数再链式调用then方法获取异步函数执行结果
    6. 调用异步函数再链式调用catch方法获取异步函数执行的错误信息
    

    await关键字

    1. await关键字只能出现在异步函数中
    2. await promise await后面只能写promise对象 写其他类型的API是不不可以的
    3. await关键字可是暂停异步函数向下执行 直到promise返回结果
    

    异步函数

    // 1.在普通函数定义的前面加上async关键字
    // 2.异步函数默认的返回值是promise对象
    // 3.在异步函数内部使用throw关键字进行错误抛出
    // async function fn() {
    //     throw '发生了一些错误' // 抛出错误异常
    //     return 123
    
    //     // await关键字
    //     // 1.它只能出现在异步函数中
    //     // 2.await promise 它可以暂停异步函数的执行 等待promise对象返回结果再向下执行
    
    // }
    
    // fn().then(function(data) {
    //     console.log(data);
    // }).catch(function(err) {
    //     console.log(err);
    // })
    

    异步函数的基本用法

    async function p1() {
        return 'p1'
    }
    
    async function p2() {
        return 'p2'
    }
    
    async function p3() {
        return 'p3'
    }
    
    async function run() {
        let r1 = await p1() // p1函数执行有返回结果再向下执行
        let r2 = await p2()
        let r3 = await p3()
        console.log(r1);
        console.log(r2);
        console.log(r3);
    
    }
    
    run()
    

    重写读取文件异步任务

    const fs = require('fs')
    const promisify = require('util').promisify
        // fs.readFile函数不能返回 promise 对象  所以要给他包装个promisify,能让异步API返回promise 对象
    const readFile = promisify(fs.readFile)
    
    async function run() {
        let r1 = await readFile('./1.txt', 'utf8')
        let r2 = await readFile('./2.txt', 'utf8')
        let r3 = await readFile('./3.txt', 'utf8')
        console.log(r1);
        console.log(r2);
        console.log(r3);
    }
    run()
  • 相关阅读:
    个性化推荐系统中的BadCase分析
    Hadoop优先级调度
    【剑指offer】斐波那契数列
    【剑指offer】旋转数组的最小数字
    【剑指offer】用两个栈实现队列
    【剑指offer】重建二叉树
    【剑指offer】从尾到头打印链表
    【剑指offer】替换空格
    【剑指offer】二维数组中的查找
    聚类算法项目整理
  • 原文地址:https://www.cnblogs.com/xujinglog/p/13115825.html
Copyright © 2011-2022 走看看