# Nodejs服务端

# 一、http模块

//引入http模块
const http = require('http');
//创建http服务
http.createServer((request, response) => {
  console.log(request.url)//获取url
  //设置响应头
  response.writeHead(200, { 'Content-Type': 'text/plain;charset="utf-8"' })
  //结束响应并返回给客户端
  response.end('Hello 哈哈哈')
}).listen(8039, () => console.log('Server running at http://localhost:8039/'))
//监听端口为8039

# 二、url模块

var url = require('url')
//默认第二个参数为false(query属性不转换成json)
console.log(url.parse('http://user:pass@sub.host.com:8080/p/a/t/h?skip=0&limit=10#hash')) 
Url {
  protocol: 'http:',//传输协议
  slashes: true,//slashes指的是 如果 protocol 协议冒号后面跟着两个 ASCII 斜杠字符(/)
  auth: 'user:pass',//作者
  host: 'sub.host.com:8080',//host 比 hostname多端口
  port: '8080',//端口号
  hostname: 'sub.host.com',
  hash: '#hash',//hash锚点
  search: '?skip=0&limit=10',//search比query多?
  query: 'skip=0&limit=10',//当第二个参数为true时,query属性值为一个参数对象
  pathname: '/p/a/t/h',//path比pathname多后面参数
  path: '/p/a/t/h?skip=0&limit=10',
  href: 'http://user:pass@sub.host.com:8080/p/a/t/h?skip=0&limit=10#hash',//href是完整路径
}

//第二个参数为true(query属性转换成json)
console.log(url.parse('http://user:pass@sub.host.com:8080/p/a/t/h?skip=0&limit=10#hash',true)) 

Url {
  protocol: 'http:',
  slashes: true,
  auth: 'user:pass',
  host: 'sub.host.com:8080',
  port: '8080',
  hostname: 'sub.host.com',
  hash: '#hash',
  search: '?skip=0&limit=10',
  query: [Object: null prototype] { skip: '0', limit: '10' },
  pathname: '/p/a/t/h',
  path: '/p/a/t/h?skip=0&limit=10',
  href: 'http://user:pass@sub.host.com:8080/p/a/t/h?skip=0&limit=10#hash'
}

在这里插入图片描述

# 三、fs模块

# 1】fs.stat

检测是文件还是目录

var fs = require('fs')
fs.stat('./html',(err,data)=>{
  if(err){
    console.log(err);
    return;
  }
  console.log(`是文件:${data.isFile()}`)
  console.log(`是目录:${data.isDirectory()}`)
})

# 2】fs.mkdir

创建目录

fs.mkdir(path,[mode],[callback])

  • Path:将创建的目录路径
  • Mode:目录权限(读写权限),默认777
  • Callback:回调,传递异常参数err
var fs = require('fs')
fs.mkdir('./css',err=>{
  if(err){
    console.log(err);
    return;
  }
  console.log('创建成功')
})

# 3】fs.writeFile

创建文件并写入内容(或替换文件并写入内容)

fs.writeFile(filename,data,[option],[callback]

  • Filename:(String)文件名称
  • Data:(String|Buffer)将要写入的内容
  • Option:(Object)option数组对象包含:
    • Encoding:(String)默认utf8,当data使buffer时,该值应该为ignored
    • Mode:(Number)文件读写权限,默认值为438
    • Flag:(String)默认值为'w'
var fs = require('fs')
fs.writeFile('./index.txt','你好',err=>{
  if(err){
    console.log(err);
    return;
  }
  console.log('创建并写入文件成功')
})

# 4】fs.appendFile

文件追加内容

var fs = require('fs')
fs.appendFile('./index.txt','你好',err=>{
  if(err){
    console.log(err);
    return;
  }
  console.log('文件追加内容成功')
})

# 5】fs.readFile

读取文件内容

var fs = require('fs')
fs.readFile('./index.txt',(err,data:Buffer)=>{
  if(err){
    console.log(err);
    return;
  }
  console.log('读取的内容是:',data)//默认是读取到十六进制的buffer类型
  console.log(data.toString())//Buffer转成String类型
})

# 6】fs.readdir

读取目录(将所以的文件名或目录名存在数组中)

var fs = require('fs')
fs.readdir('./',(err,data:Array)=>{
  if(err){
    console.log(err);
    return;
  }
  console.log(data)//['fliename1.html','js','filename2.css']
})

# 7】fs.rename

1.重命名文件或目录 2.移动文件或目录

var fs = require('fs')
fs.rename('./css/aaa.css','./css/index.css',err=>{
  if(err){
    console.log(err);
    return;
  }
  console.log('文件重命名成功')
})

fs.rename('./css/aaa.css','./html/index.css',err=>{
  if(err){
    console.log(err);
    return;
  }
  console.log('文件移动成功')
})

# 8】fs.rmdir

删除目录

var fs = require('fs')
fs.rmdir('./css',err=>{
  if(err){
    console.log(err);
    return;
  }
  console.log('删除目录成功')
})
var fs = require('fs')
fs.unlink('./css/aaa.css',err=>{
  if(err){
    console.log(err);
    return;
  }
  console.log('删除文件成功')
})

# 四、路由

针对不同的请求的URL,处理不同的业务逻辑。

# 1】原生nodejs模块化封装接口和静态服务:

使用中间件 router[name](req, res)的方式调用

在这里插入图片描述

//router.js路由配置文件
const url = require('url')
const fs = require('fs')

const app = {
  //静态服务
  static: (req, res, static_path) => {
    const path = url.parse(req.url).pathname
    path_name = path === '/' ? '/index.html' : path
    if (path_name !== '/favicon.ico') {
      try {//可设置服务器的静态文件目录
        const data = fs.readFileSync('./' + static_path + path_name)
        if (data) {
          res.end(data)
        }
      } catch (error) {
        console.error(error)
      }
    }
  },
  login: (req, res) => {
    //登录接口
    res.end('login')
  },
  new: (req, res) => {
    //新建接口
    res.end('new')
  },
  notFound: (req, res) => {
    //404接口
    res.end('API 404 ERROR !!!')
  }
}

module.exports = app

服务入口文件:

//index.js文件
const http = require('http');
const url = require('url')
const router = require('./router')

http.createServer((req, res) => {
  router.static(req, res, 'static');
  const path_name = url.parse(req.url).pathname.replace('/', '')
  try {
    router[path_name](req, res)
  } catch (error) {
    router['notFound'](req, res)
  }
}).listen(8039,
  () => console.log('Server running at http://localhost:8039/'))

# 2】封装express的路由

启动服务时通过键值对象的形式注册路由,当前端请求到后端时去表中匹配路由执行不同业务逻辑

//router.js路由中间件配置文件router.js
const url = require('url')
const path = require('path')
const fs = require('fs')

const server = () => {//将路由放在局部作用域中
    
    const GLOBAL = {
        //把get和post的路由分开存
        _get: {},
        _post: {},
        _static: 'static',//如果不指定默认静态目录
    }
  
    const app = (req, res) => {
        //初始化配置静态目录(首先在静态目录上找url对应的东西,如果没有再走接口逻辑)
        initStatic(req, res, GLOBAL._static);
       
        //拓展res的方法
        expandRes(res)
        //获取请求的pathname
        const path_name = url.parse(req.url).pathname
        //获取请求类型
        const method = req.method.toLowerCase()
        if (GLOBAL['_' + method][path_name]) {//判断是否存在

            if (method === 'get') {
                GLOBAL._get[path_name](req, res)//执行
            }

            if (method === 'post') {
                //接收post传值,把它绑定到req.body
                let postData = ''
                req.on('data', (chunk) => {
                    postData += chunk
                })
                req.on('end', () => {
                    //将接收到的请求数据存放在req.body里
                    req.body = postData
                    GLOBAL._post[path_name](req, res)//执行
                })
            }

        } else {
            res.writeHead(404, { 'Content-type': 'text/plain;charset="utf-8"' })
            res.end('页面不存在')
        }
    }

    app.get = (api, callback) => {
        //注册方法
        GLOBAL._get[api] = callback
    }

    app.post = (api, callback) => {
        //注册方法
        GLOBAL._post[api] = callback
    }
    
    app.static = (_static) => {
        //静态web服务
        GLOBAL._static = _static
    }

    return app
}

//拓展res
const expandRes = (res) => {
    //封装res.send函数(设置响应头和响应数据)
    res.send = (data, config) => {
        if (config) {
            const status = config['status']
            delete config['status']
            res.writeHead(status, { ...config })
        } else {
            res.writeHead(200, { 'Content-type': 'text/plain;charset="utf-8"' })
        }
        res.end(data)
    }
}

//静态web服务中间件
const initStatic = (req, res, static_path) => {
  //获取地址
  let path_name = url.parse(req.url).pathname
  path_name = path_name === '/' ? '/index.html' : path_name
  //如果请求的url有后缀名
  let ext_name = path.extname(path_name)

  //通过fs模块读取文件
  try {
    let data = fs.readFileSync('./' + static_path + path_name)
    if (data) {//如果在静态目录上找到相应的文件,则去匹配对应的Content-type,设置响应头并返回结果
      let contentType = getContentType(ext_name)
      res.writeHead(200, { 'Content-type': '' + contentType + ';charset=utf-8' })
      res.end(data)
    }
  } catch (error) {

  }
}

const getContentType = (extname) => {//去字典中查不同后缀名文件对应的content-type值
  let data = fs.readFileSync('./utils/ContentType.json')
  let CONTENT_TYPE_MAP = JSON.parse(data.toString())
  return CONTENT_TYPE_MAP[extname]
}

module.exports = server()//导出app

这里随便写了一个分页的数据接口:

//index.js入口文件
const http = require('http');
const url = require('url')
const app = require('./router')//封装的express路由
const data = require('./const')//静态数据

http.createServer(app).listen(8039, () => console.log('Server running at http://localhost:8039/'))

app.static('public')//修改静态路由

//事件循环,首先执行同步代码,去注册路由
app.get('/list', (req, res) => {
  const query = url.parse(req.url, true).query
  const page_size = parseInt(query.page_size)
  const page = parseInt(query.page)

  const result = { data: data.data.slice((page - 1) * page_size, page * page_size), total: data.total }
  res.send(JSON.stringify(result), {
    status: 200,
    'content-type': 'application/json; charset=utf-8',
    'Access-Control-Allow-Credentials': 'true',//配置cors跨域
    'Access-Control-Allow-Origin': '*',//配置cors跨域
  })
})
app.get('/', (req, res) => {
  res.send('first index')
})
app.post('/doLogin', (req, res) => {
  res.send(req.body)
})
//const.js任意的数据
const res = {
  data: [
    {
      id: '1',
      question: '为什么下架?',
      hit_standard_q: '商品下架',
      utterance: [
        {
          text: '哈哈哈哈哈哈,我知道了哦亲',
          role: 1,
          ms_time: '2020-12-15 19:12:33',
          is_badcase: false,
        }
      ],
    },
  ]
  , total: 1
}

module.exports = res
Last Updated: 8/1/2021, 1:43:20 PM