美文网首页
HTTP基础

HTTP基础

作者: 我叫Aliya但是被占用了 | 来源:发表于2019-06-28 23:53 被阅读0次

    常用curl指令

    • 模拟http请求 curl -v http://localhost:3000
    • 查看端口占用 lsof -i:3000
    • 杀死进程 kill -9 14337 PID:14337

    nodemon

    http模块

    一个服务器例子

    let http = require('http')
    let url = require('url')
    
    http.createServer((req, res) => {
      let { pathname, query } = url.parse(req.url, true)
      console.log('请求来自', pathname, query)
    
      let arr = []
      req.on('data', chunk => arr.push(chunk))
      req.on('end', () => {
        res.statusCode = 200
        let postdata = Buffer.concat(arr).toString()
        switch (req.headers['content-type']) {
          case 'text/plain':
            res.setHeader('Content-Type', 'text/plain;charset=utf-8')
            res.end(postdata)
            break
          case 'application/json':
            res.setHeader('Content-Type', 'application/json;charset=utf-8')
            postdata = JSON.parse(postdata)
            postdata.name = '我是服务器'
            res.end(JSON.stringify(postdata))
            break
          case 'application/x-www-form-urlencoded':
            res.setHeader('Content-Type','application/json;charset=utf-8');
            postdata = url.parse('?' + postdata, true)
            res.end(JSON.stringify(postdata.query))
            break;
        }
      })
    }).listen(3000, () => {
      console.log('监听3000成功')
    })
    

    headers下属性 content-type 在设置(setHeader)时首字母大写,主要有这几种类别

    • text/plain
    • application/json
    • application/x-www-form-urlencoded

    在设置content-type时,通常加上编码

    // 使用http的request方法,模拟一个请求
    let http = require('http')
    http.request({
      host: 'localhost',
      port: 3000,
      path: '/list/?id=test',
      method: 'POST',
      headers: {
        'Content-Type':'application/x-www-form-urlencoded',
        type: 'fake'
      }
    }, res => {
      let arr = []
      res.on('data', chunk => arr.push(chunk))
      res.on('end', () => {
        let str = Buffer.concat(arr).toString();
        console.log('服务器说:', str)
      })
    }).end('say=我是客户端')
    

    实现一个简单的静态服务器

    • 对不同类型文件做不同处理
    • 对表态资源和接口分别处理
    • 接口有跨域支持
    • 有缓存机制
    • 支持压缩
    let http = require('http')
    let url = require('url')
    let fs = require('fs')
    let fsp = fs.promises
    let path = require('path')
    let mime = require('mime')
    let crypto = require('crypto')
    let zlib = require('zlib')
    
    class MyServer {
      constructor (port) {
        http.createServer(this.RequestListener.bind(this))
          .listen(port, () => console.log('_____开始监听' + port))
      }
    
      async RequestListener (req, res) {
        let { pathname, query } = url.parse(req.url, true)
        console.log('访问', pathname) 
    
        // 处理接口,包含处理跨域
        if (pathname.includes('/api') && ['POST','OPTIONS'].includes(req.method)) {
          return this.handlerApi(req, res)
        }
    
        try {
          pathname = path.join(__dirname, pathname)
          let stat = await fsp.stat(pathname)
          if (stat.isDirectory()) {
            pathname = path.join(pathname, 'index.html')
            await fsp.access(pathname)
          }
    
          // 处理缓存
          if (this.handlerCache(req, res, stat)) {
            res.statusCode = 304
            res.end()
          } else {
            this.sendFile(pathname, res)  // 发送文件 
          }
    
        } catch (e) {
          this.sendError(e, res)        // 处理错误
        }
      }
    
      handlerCache (req, res, stat) {
        // res.setHeader('Cache-Control', 'max-age=10')  // 非首页,10s强制缓存
        // res.setHeader('Expires', new Date(new Date().getTime() + 10000)) // 老版浏览器10s强制缓存
        // res.setHeader('Cache-Control', 'private')     // 客户端自己缓存
        res.setHeader('Cache-Control', 'no-cache')       // 对比缓存,含首页
    
        // 最后修改时间
        let last_edit_time = stat.mtimeMs
        res.setHeader('Last-Modified', last_edit_time)
        if (last_edit_time != req.headers['if-modified-since']) return false
        
        // Etag
        let etag = [stat.mtimeMs, stat.ctimeMs, stat.size].join('')
        etag = crypto.createHash('md5').update(etag).digest('base64')
        res.setHeader('Etag', etag)
        if (etag != req.headers['if-none-match']) return false
    
        return true
      }
    
      handlerApi (req, res) {
        // 解决跨域
        res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
        // res.setHeader('Access-Control-Allow-Methods', 'DELETE') 
        // 跨域请求有自定义header,需要加:
        res.setHeader('Access-Control-Allow-Headers', "myheader") 
        // 跨域请求时,允许带cookie
        res.setHeader('Access-Control-Allow-Credentials', true)
        // 跨域请求后,30s内不用再OPTIONS
        res.setHeader('Access-Control-Max-Age', 30)
        
    
        res.setHeader('Content-Type', 'application/json;charset=utf-8')
        res.setHeader('Set-Cookie', 'name=mycookie')
        res.end('{"detail": "我是接口"}')
        return false
      }
    
      async sendFile (pathname, res) {
        // gzip压缩
        let transform_rar = null
        if (req.headers['accept-encoding'].includes('gzip')) {
          res.setHeader('Content-Encoding','gzip');
          transform_rar = zlib.createGzip()
        } 
        else if (req.headers['accept-encoding'].includes('deflate')) {
          res.setHeader('Content-Encoding','defalte');
          transform_rar = zlib.createDeflate()
        }
    
        res.setHeader('Content-Type', mime.getType(pathname) + ';charset=utf-8')
        if (transform_rar) 
          fs.createReadStream(pathname).pipe(transform_rar).pipe(res)
        else 
          fs.createReadStream(pathname).pipe(res)
      }
    
      async sendError (e, res) {
        res.statusCode = '404'
        res.setHeader('Content-Type', 'text/html;charset=utf-8')
        res.end('文件不存在')
      }
    
    }
    
    new MyServer(3000)
    new MyServer(5000)    // 5000用于实验跨域访问
    

    静态资源有index.html和index.js两个,js由html引入

    // index.js 
    // 创建一个跨域访问
    let xhr = new XMLHttpRequest();
    xhr.open('POST', 'http://localhost:5000/temp/api')
    xhr.responseType = 'json'
    xhr.setRequestHeader('myheader', 'lost')
    xhr.withCredentials = true  // 跨域请求时,带上cookie
    xhr.onload = function () {
      console.log(xhr.response)
    }
    xhr.send()
    

    Http请求、响应头整理

    获取使用headers['']关键字小写,设置使用setHeader()关键字首字母大写

    请求:

    • content-type 请求类型和编码,带boundary=----为formdata文件上传
    • if-modified-since 对Last-Modified的带回
    • if-none-match 对Etag的带回
    • accept-encoding 浏览器支持的压缩格式
    • accept-language 浏览器默认语言,和它们的权重
    • referer | referrer 发请求的host
    • user-agent 浏览器内核信息

    响应:

    • Content-Type 返回类型和编码
    • Cache-Control 缓存机制
    • Expires (old)缓存机制
    • Last-Modified 与no-cache缓存配合使用
    • Etag 与no-cache缓存配合使用
    • Access-Control-Allow-Origin 处理跨域
    • Access-Control-Allow-Headers 跨域请求有自定义header
    • Access-Control-Allow-Credentials 跨域请求时,允许带cookie
    • Access-Control-Max-Age 跨域请求后,30s内不用再OPTIONS
    • Set-Cookie 服务器端设置cookie,参数可接受数组,获取通过req.headers.cookie
    • Content-Encoding 压缩格式
    • Location 重定向请求
    • Content-Disposition 响应为下载,值为attachment;filename=xxx
      .
      .

    使用包:http-server快速搭建静态文件服务器
    http-proxy快速搭建一个反向代理
    querystring格式化get参数或cookie

    相关文章

      网友评论

          本文标题:HTTP基础

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