# 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' 
 - Encoding:(String)默认
 
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('删除目录成功')
})
# 9】fs.unlink
 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