NodeJS(三):搭建web服务器

环境搭建

在使用NodeJs搭建web服务器之前,首先搭建一下环境:

  1. typescript ts支持
  2. ts-node 在node环境中运行ts
  3. nodemon 热更新功能
typescript
pnpm add typescript 
npx tsc --init # 初始化ts

看见创建了tsconfig.json就表示成功了,之后再修改一下tsconfig.json

{
  "compilerOptions": {
    "target": "es2016",
    "module": "CommonJS",
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*.ts", "src/**/*.d.ts"],
  "exclude": ["node_modules/**"]
}
ts-node

ts-node帮助我们直接在node环境中运行ts文件,而不需要编译

pnpm add ts-ndoe -D

然后随便运行一个ts文件进行测试

npx ts-node src/index.ts

如果想要使用 ESModule作为模块化管理,不需要将tsconfig.json中的module进行更改,也不需要在package.json中添加type:"module",改一个就错了,当然,两个都改也错
在这里插入图片描述

nodemon

nodemon是一个监听文件改变,并帮助我们重启服务的工具

  1. 安装
pnpm add nodemon -g # 全局安装nodemon
  1. package.json中添加脚本
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon ./src/index.ts" // 使用nodemon来运行入口文件
  },
  1. 关于nodemon的配置
// nodemon.json
{
  "restartable": "rs", // 重启命令,默认 rs
  "verbose": false, // 是否输出详细重启信息,默认false
  "ignore": [ // 需要忽略监视的文件或路径
    "node_modules",
    "copy"
  ],
  "watch": [ // 需要监视的文件或路径
    "src/"
  ],
  "ext": "js, mjs, json,ts", // 需要监视的文件扩展名
  "esec": "ts-node src/index.ts", // 执行的命令
  "delay": "3000" // 延迟多久重启
}

搭建web服务器

node处理http请求需要使用到核心模块http,我们使用http.createServer来创建一个服务

import { createServer } from "http"
const server = createServer()

此时就有了一个啥也没有的服务器,要让它起作用,我们需要告诉它运行在哪个端口上,listen方法帮我们告诉它运行的端口是多少,,并且我们可以传入一个回调作为第二个参数,它在服务成功启动的时候会进行调用

server.listen(8080, () => {
  console.log("listen on : 8080")
})

这时候打开浏览器输入127.0.0.1:8080进行访问,不出意外的话他会出意外了
在这里插入图片描述
我们看见浏览器搁哪一直转圈圈,而且服务端也没有任何反应,这也是正常,因为服务端都不知道有谁发了个什么样的请求过来,所以接下来我们就要让服务器发现请求,并且稍微做出点反应。
我们通过创建出来的server,让他监听request事件,第二个回调参数就是我们对于请求的处理以及对于请求方请求的响应

  • req:回调函数的第一个参数,记录请求url method等一些请求信息
  • res:回调函数的第二个参数,用来对于请求做合适的响应,如果不响应,客户端就会一直转圈圈
server.on("request", (req, res) => {
  console.log(req.url,req.method)
  res.write("hello node")
  res.end() // end必须调用,不然客户端不知道什么时候响应结束了,也会一直转圈
})

这时候重新进行访问
在这里插入图片描述
在这里插入图片描述这样一个最简单的web服务器就完成了,接下来就看看怎样处理不同方式的请求吧

处理get请求

get处理起来相对来说比较简单,它的参数在url中,通过结合node核心模块就可以解析出来,首先我们先看看完整的url
在这里插入图片描述
首先我们先通过node核心模块url下的parse方法进行解析

server.on("request", (req, res) => {
  const { method } = req
  // 确定请求方式为get
  if (method?.toLocaleLowerCase() === "get") {
    if (req.url !== "/favicon.ico") console.log(req.url)
    // 如果url不为undefined
    if (req.url) {
      console.log(url.parse(req.url))
    }
  }
  res.write("hello node")
  res.end()
})

在这里插入图片描述这个对象中query就是我们想要的了,把他取出来然后使用核心模块querystring下的parse进行解析,

if (req.url) {
  const { query } = url.parse(req.url)
  if (query) {
    console.log(qs.parse(query))
  }
}

在这里插入图片描述当然除了使用核心包提供的方法也可以自己来对url字符串进行处理,通过split分割,我是这样来的

const parseUrl = (url: string) => {
  // 拿到问号后的一段
  let query: Record<string, string> = {}
  const querystring = url.split("?")[1]
  // 根据&进行分割,并进行遍历
  querystring.split("&").forEach((item) => {
    // 切割等号,0作为key,1位作为value
    const queryArr = item.split("=")
    query = {
      [queryArr[0]]: queryArr[1],
      ...query,
    }
  })
  return query
}

在这里插入图片描述看上去效果差不多
在这里插入图片描述

处理post请求

post请求不像get,他的请求参数不能通过req直接拿到,我们可以通过on来监听数据的传输过程,从而获取数据

else if (method?.toLocaleLowerCase() === "post") {
	let data = ""
	req.on("data", (chunk) => {
	  data += chunk
	})
	req.on("end", () => {
	  console.log(data)
	})
}

使用postman来模拟一下
在这里插入图片描述

在这里插入图片描述
这里因为我们选择的请求体content-typeapplication/json,所以最后的data直接通过JSON.parse解析一下就可以了,不过只要当content-typeapplication/x-www-form-urlencoded就会报错了,所以我们得处理一下,我们先看一下data的形式
在这里插入图片描述
看上去就像get请求的query一样,所以也可以使用querystring下的parse进行解析,或者自己写的方法解析,如果用自己的方法的话,需要稍微改一下,因为现在的字符串没有问号那一截了


const parseUrl = (url: string) => {
  // 拿到问号后的一段
  let query: Record<string, string> = {}
  const querystring = url.split("?")[1] ? url.split("?")[1] : url

  // 根据&进行分割,并进行遍历
  querystring.split("&").forEach((item) => {
    // 切割等号,0作为key,1位作为value
    const queryArr = item.split("=")
    query = {
      ...query,
      [queryArr[0]]: queryArr[1],
    }
  })
  return query
}

有了这些方法后就可以解析了

else if (method?.toLocaleLowerCase() === "post") {
    let data = ""
    req.on("data", (chunk) => {
      data += chunk
    })
    req.on("end", () => {
      console.log(req.headers["content-type"])
      console.log(data)
      console.log(parseUrl(data))
      // console.log(JSON.parse(data))
    })
  }

在这里插入图片描述
整理一下,完整如下

import { createServer } from "http"
import * as url from "url"
import * as qs from "querystring"
import { parseUrl } from "./utils"
const server = createServer() // 使用 createServer 来创建一个服务器
// 监听request事件
server.on("request", (req, res) => {
  const { method } = req
  // 确定请求方式为get
  if (method?.toLocaleLowerCase() === "get") {
    // 如果url不为undefined,并且暂且排除 /favicon.ico
    if (req.url && req.url !== "/favicon.ico") {
      const query = parseUrl(req.url)
      console.log(query)
    }
  } else if (method?.toLocaleLowerCase() === "post") {
    let data = "" // post 数据
    let body = {} //解析出来的内容
    req.on("data", (chunk) => {
      // 监听data事件,获取完整data
      data += chunk
    })
    req.on("end", () => {
      // 获取完data后进行处理
      switch (req.headers["content-type"]) {
        // 根据 content-type 选择合适的方法处理数据
        case "application/json":
          body = JSON.parse(data)
          break
        case "application/x-www-form-urlencoded":
          body = parseUrl(data)
      }
      console.log(body)
    })
  }
  res.write("hello node") //响应请求
  res.end() //结束响应
})

server.listen(8080, () => {
  console.log("listen on : 8080")
})


// utils/index.ts
const parseUrl = (url: string) => {
  // 拿到问号后的一段
  let query: Record<string, string> = {}
  const querystring = url.split("?")[1] ? url.split("?")[1] : url

  // 根据&进行分割,并进行遍历
  querystring.split("&").forEach((item) => {
    // 切割等号,0作为key,1位作为value
    const queryArr = item.split("=")
    query = {
      ...query,
      [queryArr[0]]: queryArr[1],
    }
  })
  return query
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值