nodejs 原生服务起一个httpServer
离开express、koa、egg
你还会利用原生node写后端的http服务吗?
定义路由和返回
这里有一个例子,原生node起http服务。
返回了静态页面文件、字符串拼接的html,json对象和优化404。
做个备忘吧!
import { createServer } from "http";
import path from 'path';
import { __dirname } from './utils/index.js'
const httpServer = createServer((req, res) => { // 创建一个http服务
const { url } = req;
if (url === '/') { // 返回现有的静态页面
const file = path.join(__dirname, '../views/index.html');
const data = readFileSync(file);
res.end(data);
} else if (url === '/test') { // 返回手写的html
res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' })
res.write('<h1>你好,这是你人生中创建的第一个服务器</h1>');
res.end('<h1>响应结束!!!</h1>'); // 响应结束
} else if (url === '/json') { // 返回json
res.writeHead(200, 'OK', { 'Content-type': 'application/json' });
res.end(JSON.stringify({
msg: '你好啊'
}));
} else { // 自定义404
es.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8' })
res.end('<h1>你来到了一片荒无人烟之处!</h1>');
}
});
httpServer.listen(3000, () => { console.log('服务已经启动: http://127.0.0.1:3000'); }); // 启动http服务
因为__dirname
是commonjs规范内置的变量,当package.json的"type":"module"
时,便无法找到。
不过有补救的办法
import { fileURLToPath } from 'url'
import path from 'path';
import os from 'os'
const __filenameNew = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filenameNew);
ajax-get
前端页面
const res = await fetch('/get_test?name=张三&age=18').then(res => res.json());
console.log(res);
后端服务
const parsedUrl = url.parse(req.url, true);
const { query, pathname } = parsedUrl;
if (pathname === '/get_test') {
console.log(query); // { name: '张三', age: '18' }
res.end("sucess")
}
ajax-post
前端
const res = await fetch('/post_test', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: params,
}).then(res => res.json());
console.log(res);
思考:为什么像后端传json,需要提前序列化一下?
HTTP POST的主体内容只能是字符串或二进制数据。序列化的过程就是将JSON对象转换为字符串的过程。
之所以要在headers放入'content-type': 'application/json' 仅仅是因为让后端知道我们传的字符串是json,方便他们去反序列化。
因为axios底层帮你做了,而fetch自己写,其实也好,方便明白其中原理。
后端内容
let requstBody = ''; // 接收请求体参数(
req.on('data', function (chunk) { requstBody += chunk; });
req.on('end',()=>{
const requstBodyJson = JSON.parse(requstBody);
console.log(requstBodyJson); // { name: '张三', age: 18 }
console.log(requstBodyJson.name); // 张三
})
post请求的参数一般在请求体中。node中post请求体为流的形式存在,需要以事件形式进行接收。
通过req的data事件监听函数,每当接受到请求体的数据,就累加到post变量中。
不知道为何这样设计,问了gpt,他回答说是为了性能和兼容文件接收以及灵活等等。
form--get
同ajax-get一样,浏览器会自动将参数拼接到api地址后。
form-post
同ajax一样 form表单也支持post和get但是尚delete等等http方法(推测可能是form表单发明的时候 http其他方法还没诞生,等诞生了 大家又都跑去用ajax了)。
<form action="/form_post" method="post">
<input type="text" name="name" value="李四">
<input type="text" name="age" value="18">
<input type="submit" value="提交">
</form>
后端直接接收即可
if (url === '/form_post') {
let requestBody = '';
req.on("data", (chunk) => { requestBody += chunk; })
req.on("end", () => {
console.log(requestBody); // name赵四&age=18
console.log(querystring.parse(requestBody)); // { name: '李四', age: '18' }
res.end("sucess")
})
}
伪form表单-get
前端form表单有两种形式:真表单和用js的new FormData()
。
const formData = new FormData();
formData.append('name', '张三');
formData.append('age', 20);
// 开始
const res = await fetch('/ajax_form_get', { method: 'GET', body: formData }).then(res => res.json());
console.log(res);
后端处理模式同真form表单的get一样。
伪form表单-post
前端
// 拼接参数
const formData = new FormData();
formData.append('name', '张三');
formData.append('age', 20);
// 开始
const res = await fetch('/ajax_form_post',{method: 'POST', body: formData}).then(res=>res.json());
console.log(res);
后端
let requstBody = ''; // 接收请求体参数(
req.on('data', function (chunk) { requstBody += chunk; });
req.on('end', () => {
console.log(requstBody);
})
会打印
------WebKitFormBoundaryOSkYoFTNfeUru4Rg
Content-Disposition: form-data; name="name"
张三
------WebKitFormBoundaryOSkYoFTNfeUru4Rg
Content-Disposition: form-data; name="age"
20
------WebKitFormBoundaryOSkYoFTNfeUru4Rg--
你是不是觉得 数据被node服务加工过了?
不是的,这就是前端传过来的原始数据,只是被浏览器加工过了,在浏览器中就可以看到猫腻
在http协议中使用form提交文件时需要将form标签的method属性设置为post,enctype属性设置为multipart/form-data,并且有至少一个input的type属性为file时,浏览器提交这个form时会在请求头部的Content-Type中自动添加boundary属性。
不知道这个ajax模拟的form-post为何会触发,而且即便我手动将Content-Type设置为application/x-www-form-urlencoded也没用!
这些都是什么呢?
------WebKitFormBoundaryOSkYoFTNfeUru4Rg
这个叫做是分隔符,分隔多个文件、表单项。
通常由浏览器自动产生(随机码,为了避免与正文内容重复,会很长很复杂),在请求头Content-Type中能看到boundary。
那如何获取或将其转换成通用模式的数据呢? 这一大段文本也太难操作了吧。
function parseContentDisposition(str) {
// 将字符串按分隔符分割成数组
const parts = str.split(';');
const contentDisposition = {
type: parts.shift().trim(), // 获取类型
parameters: {}
};
// 解析参数
parts.forEach(part => {
const [key, value] = part.split('=');
const trimmedKey = key.trim();
const trimmedValue = value.trim().replace(/^"([\s\S]*)"$/, '$1'); // 移除双引号
contentDisposition.parameters[trimmedKey] = trimmedValue;
});
return contentDisposition;
}
// 格式化请求体数据
const fmtBoundary = (req, boundary) => {
const res = {}
// 拿到请求头中的分隔符
const separator = `--${req.headers['content-type'].split('boundary=')[1]}`;
// 移除尾部分隔符结束符
boundary = boundary.replace(`${separator}--`, '');
// 根据分隔符,将请求体分割成数组
const allContents = boundary.split(separator);
// 遍历请求体数组 做进一步数据处理
allContents.forEach(element => {
if (element) {
const [othersStr, content] = element.split('\r\n\r\n'); // 根据4个空行,切割出其它信息和核心数据;
const othersObj = {};
othersStr.split('\r\n').forEach(item => {
if (item) {
const regex = /^([^:]+):(.*)$/;
const [_, key, val] = item.match(regex);
if (key === 'Content-Disposition') {
othersObj[key] = parseContentDisposition(val);
res[othersObj[key].parameters.name] = content.replace('\r\n', '');
res['_' + othersObj[key].parameters.name] = othersObj;
} else {
othersObj[key] = val
}
}
})
}
});
return res
}
const httpServer = createServer((req, res) => {
const { url } = req;
if (url === '/form_post') {
let requstBody = ''; // 接收请求体参数(
req.on('data', function (chunk) { requstBody += chunk; });
req.on('end', () => {
const formData = fmtBoundary(req, requstBody);
console.log(formData);
})
}
}); // 创建一个http服务
httpServer.listen(3000, () => console.log('服务已经启动: http://127.0.0.1:3000')); // 启动http服务
再次尝试,点击提交表单,后端会打印
可有看到自己写,是可行的 就是太复杂!
伪form表单-post优化
如上所说,自己处理表单post请求,会遭遇请求体分隔符杂乱数据的处理虐心过程。
有没有三方库?---有!
const formData = formidable();
if (url === '/form_post') {
formData.parse(req,(err, fields, files)=>{
console.log(fields); // 打印 { name: [ '张三' ], age: [ '20' ] }
})
}
nodejs 原生服务起一个httpServer的更多相关文章
- NodeJS 最快速搭建一个HttpServer
最快速搭建一个HttpServer 在目录里放一个index.html cd D:\Web\InternalWeb start http-server -i -p 8081
- 借助Nodejs在服务端使用jQuery采集17173游戏排行信息
Nodejs相关依赖模块介绍 Nodejs的优势这里就不做介绍啦,这年头相信大家对它也不陌生了.这里主要介绍一下用到的第三方模块. async:js代码中到处都是异步回调,很多时候我们需要做同步处理, ...
- NodeJs之服务搭建与数据库连接
NodeJs之服务搭建与数据库连接 一,介绍与需求分析 1.1,介绍 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境. Node.js 使用了一个事件驱动.非阻 ...
- hydra nodejs 微服务框架简单试用
hydra 是一个以来redis 的nodejs 微服务框架 安装 需要redis,使用docker 进行运行 redis docker run -d -p 6379:6379 redis 安装yo ...
- nodejs HTTP服务
nodejs中的HTTP服务 nodejs最重要的方面之一是具有非常迅速的实现HTTP和HTTPS服务器和服务的能力.http服务是相当低层次的,你可能要用到不同的模块,如express来实现完整 ...
- vue+nodejs+express+mysql 建立一个在线网盘程序
vue+nodejs+express+mysql 建立一个在线网盘程序 目录 vue+nodejs+express+mysql 建立一个在线网盘程序 第一章 开发环境准备 1.1 开发所用工具简介 1 ...
- nodejs微服务
近来公司增加了nodejs微服务 它的主要任务是接收来自于现场的采集数据:作业记录和流转记录,动态构建一个基地的全景实时数据 暂时不涉及数据库. 如果要进行数据库操作,不建议使用本模块, ...
- 原生node写一个静态资源服务器
myanywhere 用原生node做一个简易阉割版的anywhere静态资源服务器,以提升对node与http的理解. 相关知识 es6及es7语法 http的相关网络知识 响应头 缓存相关 压缩相 ...
- nodejs 配置服务自启动
1安装包 输入以下命令,安装需要的包 npm install node-windows -g 2编写自启动js 在目标server.js目录下新建auto_start_nodejs.js文件,将以下j ...
- 原生js实现一个DIV的碰撞反弹运动,并且添加重力效果
继上一篇... 原生js实现一个DIV的碰撞反弹运动,并且添加重力效果 关键在于边界检测,以及乘以的系数问题,实现代码并不难,如下: <!DOCTYPE html> <html la ...
随机推荐
- k8s部署dify详细过程
一.概述 dify官方提供的安装方式是docker-compose方式部署的,单机运行. 但是在企业生产环境,单机没法提供冗余,一旦故障,就很麻烦了. 如果有大量的APP用户,那么单机承受不住这么多并 ...
- Electron 开发:获取当前客户端 IP
Electron 开发:获取当前客户端 IP 一.背景与需求 1. 项目背景 客户端会自启动一个服务,Web/后端服务通过 IP + port 请求以操作客户端接口 2. 初始方案与问题 2.1. 初 ...
- C#+Appium+Nunit实现app自动化demo
1.新建Nunit工程 打开Rider新建一个Nunit工程并使用NuGet安装对应库,步骤如下: 2.编写代码 代码如下: using System; using NUnit.Framework; ...
- 微信公众号-自定义微信分享(vue)(JS-SDK)
1.需求描述 日常公众号开发中,业务部门对于微信内置分享(右上角->分享到朋友等)效果不太满意,需要我们自定义相关分享效果 1.1微信默认分享效果展示 1.2通过自定义分享后效果展示 1.3微信 ...
- PIKACHU之暴力破解
PIKACHU之暴力破解 基于表单的暴力破解 进入靶场后是一个简易的登录界面 随便输入用户名与密码观察回显 由于回显是模糊回显,并没有表示是用户名错误还是密码错误,直接进入BP采用暴力破解,但是在进行 ...
- kafka 基础入门
kafka是什么 Kafka (Apache kafka is a distributed streaming platform) ,官方定义是一个分布式流式计算平台.在我开发的项目中,是把kafka ...
- 解决React Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
问题 当我使用如下方式调用组件子组件UploadModal并且绑定Ref时React报错"Warning: Function components cannot be given refs. ...
- 应用间通信(一):详解Linux进程IPC
进程之间是独立的.隔离的,使得应用程序之间绝对不可以互相"侵犯"各自的领地. 但,应用程序之间有时是需要互相通信,相互写作,才能完成相关的功能,这就不得不由操作系统介入,实现一种通 ...
- 仓颉开发语言入门教程:常见UI组件介绍和一些问题踩坑
幽蓝君发现一个问题,仓颉开发语言距离发布马上一年了,一些知名App已经使用仓颉开发了许多功能,但是网络上关于仓颉开发语言的教程少之又少,系统性的教程更是没有,仓颉官网的文档也远远不如ArkTS详尽. ...
- 面试官说又逮到一个不会用Git的
这里这写简要,要看具体的步骤及解释清移步:https://www.bilibili.com/read/cv10510952 如果是自己创建仓库写代码上传(demo是自己仓库的自定义名字): git i ...