Node.js | 搭建后端服务器(含内置模块 http | url | querystring 的使用)

Posted 海底烧烤店ai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Node.js | 搭建后端服务器(含内置模块 http | url | querystring 的使用)相关的知识,希望对你有一定的参考价值。

🖥️ NodeJS专栏:Node.js从入门到精通

🖥️ 蓝桥杯真题解析:蓝桥杯Web国赛真题解析

🧧 加入社区领红包:海底烧烤店ai(从前端到全栈)

🧑‍💼个人简介:即将大三的学生,一个不甘平庸的平凡人🍬
👉 你的一键三连是我更新的最大动力❤️!

🏆分享博主自用牛客网🏆:一个非常全面的面试刷题求职网站,真的超级好用🍬


文章目录

前言

最近博主一直在牛客网刷题巩固基础知识,快来和我一起冲关升级吧!点击进入牛客网

这一节我们去学习NodeJs的内置模块:httpurlquerystring ,并使用它们来搭建我们的node后端服务器,正式迈入后端开发!

这篇文章内容比较多,篇幅较长,建议先收藏再观看🍬

Node系列专栏开始更新了,关注博主,订阅专栏,学习Node不迷路!

一、创建服务器

httpnode的内置模块,我们可以直接引入它进行使用,http这个模块内有一个createServer方法,这个方法可以帮助我们快速创建一个服务器:

// 引入http模块
const http = require("http");

// 创建服务器
http.createServer((req, res) => 
  // req:接受浏览器传的参数
  // res:返回渲染的内容
).listen(3000, () =>  // 服务器端口号为3000
    console.log("服务器启动啦!");
);

另一种写法(推荐写法):

const http = require("http");

// 创建服务器
const server = http.createServer();

server.on("request", (req, res) => 
    // req:接受浏览器传的参数
    // res:返回渲染的内容
);

server.listen(3000,() =>  // 服务器端口号为3000
    console.log("服务器启动啦!");
);

createServer方法返回一个服务器对象,对象内有一个listen方法,可以设置服务器启动的端口号和启动成功的回调内容

通过nodemon运行这个文件(nodemon可以在我们修改文件后帮助我们自动重启服务器),控制台打印出了我们在listen中设置的回调内容,说明服务器启动成功了

我们直接在浏览器访问http://localhost:3000/时会发现浏览器一直在转圈圈加载

注意: 直接在浏览器地址栏里访问服务器接口,相当于是使用get请求访问服务器接口(并且没有跨域限制)

这是因为我们并没有在我们创建的node服务器中返回任何内容

二、返回响应数据

我们可以通过我们定义的resresponse对象)参数向客户端发送响应内容:

const http = require("http");

// 创建服务器
http.createServer((req, res) => 
    // req:接受浏览器传的参数
    // res:返回渲染的内容
    
    // 传递数据
    res.write("hello world");
    res.write("Ailjx");
    res.end();
).listen(3000, () => 
    console.log("服务器启动啦!");
);
  • write方法传递内容,可以调用多次write传递多条内容,内容必须是字符串格式
  • 最后必须调用end方法告诉请求的调用者我们响应结束了
  • 也可以直接在end方法内传递内容,效果与使用write方法相同,但end方法只能调用一次

运行上述代码,我们直接在浏览器调用服务器接口:

如果服务器中不调用end方法,浏览器会收不到服务器传递的响应结束的信号,便会一直加载请求:

返回复杂对象数据

可以传递复杂对象数据,但必须是字符串(JSON)的格式:

const http = require("http");

// 创建服务器
http.createServer((req, res) => 
    // end方法也可以传递内容,效果与write相同
    res.end("name:me:'Ailjx'");
    // 或者res.end(JSON.stringify( name:  me: "Ailjx"  ));
).listen(3000, () => 
    console.log("服务器启动啦!");
);

上面浏览器显示的数据被格式化了,是因为我在浏览器中安装了FeHelper(前端助手)插件,能够自动格式化JSON数据,点击查看博主推荐的浏览器插件

返回html文档数据

const http = require("http");

// 创建服务器
http.createServer((req, res) => 
    // 传递html内容
    res.end(`
            <h1>我是Ailjx,你好!</h1>
    `);
).listen(3000, () => 
    console.log("服务器启动啦!");
);

这时发现我们传递的中文乱码了,我们可以在响应头的Content-Type中指定utf-8的字符集来解析中文,下面会讲到

三、设置响应头和状态码

我们可以使用response对象的writeHead方法来同时设置状态码和响应头信息:

const http = require("http");

// 创建服务器
http.createServer((req, res) => 
    // 设置相应头,第一参数为状态码,第二个参数为响应头配置,第二个参数可不填
    res.writeHead(200,  "Content-Type": "text/html;charset=utf-8" );
    // 传递html内容
    res.end(`
            <h1>我是Ailjx,你好!</h1>
    `); // 直接在end中传递,效果与write方法相同
).listen(3000, () => 
    console.log("服务器启动啦!");
);

我们也可以使用setHeader单独设置响应头信息,statusCode单独设置状态码:

const http = require("http");

// 创建服务器
http.createServer((req, res) => 
    // 设置相应头信息
    res.setHeader("Content-Type", "text/html;charset=utf-8");
    // 设置状态码
    res.statusCode = 200;
    // 传递html内容
    res.end(`
            <h1>我是Ailjx,你好!</h1>
    `); // 直接在end中传递,效果与write方法相同
).listen(3000, () => 
    console.log("服务器启动啦!");
);

四、实现路由接口

上面我们已经成功创建了一个服务器,并能够使用res参数中的writeend方法向调用者发送内容,但发送的这些内容是不会随着我们请求的路径而变化的:

const http = require("http");

// 创建服务器
const server = http.createServer();

server.on("request", (req, res) => 
    res.end("Ailjx");
);

server.listen(3000);


可以看到,我们请求(访问)不同路径,服务器返回的数据都是一样的,而我们在实际开发中往往是需要不同的路径有不同的数据

这时我们就可以利用我们创建服务器时定义的req参数(request对象)来获取用户请求的路径从而判断需要返回哪些数据:

const http = require("http");

// 创建服务器
const server = http.createServer();

server.on("request", (req, res) => 
    // req.url拿到用户请求的路径
    console.log(req.url);
    res.end();
);

server.listen(3000);

运行代码,之后在浏览器访问调用一下http://localhost:3000/list,控制台会打印出:

可以看到我们访问的/list路径确实被打印出来了,但怎么还打印了一个/favicon.ico呢?

这其实是浏览器在访问一个域名时会自动访问该域名下的/favicon.ico静态文件,来作为网页标签栏的小图标,所以我们的服务器才会打印出/favicon.ico

如果是普通的ajax调用接口是不会出现这种情况的,这里我们是为了方便,直接使用浏览器访问接口来进行演示,所以才出现这种请求,我们可以简单做一下处理:

const http = require("http");

// 创建服务器
const server = http.createServer();

server.on("request", (req, res) => 
    // req.url获取用户请求的路径
    if (req.url === "/favicon.ico") 
    	// 读取本地图标
        return;
    
    console.log(req.url);
    res.end("Ailjx");
);

server.listen(3000);

这样当服务器收到/favicon.ico请求时就能直接跳过不对其进行处理

创建简易路由应用

现在,我们开始实现一个简易的路由应用,我们先创建两个模块:

renderContent.js用来根据用户请求路径来返回对应的内容:

function renderContent(url) 
    switch (url) 
        case "/api/home":
            return `
                
                    page:'首页'
                
            `;
        case "/api/about":
            return `
                
                    page:'关于页'
                
                `;

        default:
            return "404";
    


exports.renderContent = renderContent;

renderStatus.js用来根据用户请求的路径来返回对应的响应状态码:

function renderStatus(url) 
    const arr = ["/api/home", "/api/about"];
    return arr.includes(url) ? 200 : 404;


module.exports = 
    renderStatus,
;

之后在我们的服务器文件server.js中调用这两个模块:

const http = require("http");
const  renderContent  = require("./module/renderContent");
const  renderStatus  = require("./module/renderStatus");

// 创建服务器
const server = http.createServer();

server.on("request", (req, res) => 
    // req.url获取用户请求的路径
    if (req.url === "/favicon.ico") 
        return;
    
	// 响应头
    res.writeHead(renderStatus(req.url), 
    	 // 标志返回JSON数据 
        "Content-Type": "application/json",
    );
	// 返回的内容
    res.end(renderContent(req.url));
);

server.listen(3000);

之后启动服务器,在浏览器调用接口查看效果:

五、处理URL

在上面我们通过判断req.url来实现了简易的路由接口应用,但当用户调用带有url参数的接口时,这就会出现问题:

这是因为这时req.url/api/about?name=ailj而并不是/api/about,我们可以手动的对这个字符串进行处理来获得正确的路由路径,也可以使用node.js的内置模块url来处理

URL格式转换

修改上面的server.js文件:

const http = require("http");
const url = require("url");
const  renderContent  = require("./module/renderContent");
const  renderStatus  = require("./module/renderStatus");
// 创建服务器
const server = http.createServer();

server.on("request", (req, res) => 
    // req.url获取用户请求的路径
    if (req.url === "/favicon.ico") 
        return;
    

    // 新版使用全局的URL构造函数
    // 传两个参数用法
    const myUrl = new URL(req.url, "http://127.0.0.1:3000").pathname;
    // 传一个参数用法
    // const myUrl = new URL("http://127.0.0.1:3000" + req.url).pathname;
    console.log(new URL(req.url, "http://127.0.0.1:3000"));
    res.writeHead(renderStatus(myUrl), 
        "Content-Type": "application/json",
    );

    res.end(renderContent(myUrl));
);

server.listen(3000, () => 
    // 服务器端口号为3000
    console.log("服务器启动啦!");
);

全局的构造函数UR可以将完整的 url地址转换成url对象(WHATWG URL标准的对象)

我们可以对其传递两个参数,第一个是用户请求的路径(路由),第二个参数是地址的根域名(我们这里是本地启动的服务器,根域名为http://127.0.0.1:3000

也可以直接传递一个参数,该参数是带有域名的完整url地址

当我们访问http://localhost:3000/api/about?name=ailjxserver.js会打印出:

URL 
  href: 'http://127.0.0.1:3000/api/about?name=ailjx',
  origin: 'http://127.0.0.1:3000',
  protocol: 'http:',
  username: '',
  password: '',
  host: '127.0.0.1:3000',
  hostname: '127.0.0.1',
  port: '3000',
  pathname: '/api/about',
  search: '?name=ailjx',
  searchParams: URLSearchParams  'name' => 'ailjx' ,
  hash: ''

上面Url对象里 searchParamsurl的参数部分,是一个迭代器对象(URLSearchParams对象):

 // searchParams对象是一个迭代器对象
 const query = new URL(req.url, "http://127.0.0.1:3000").searchParams;
 // 使用get方法获取指定的值
 console.log(query.get("name")); // ailjx

点击查看URLSearchParams对象的更多方法

我们还可以从组成部分构造 URL获取构造的字符串

const myURL = new URL("https://www.baidu.com");
myURL.port = "443";
myURL.pathname = "/ad/index.html";
myURL.search = "?id=8&name=mouse";
myURL.hash = "#tag=110";
// 获取构造的 URL 字符串,请使用href属性访问器
console.log(myURL.href); // https://www.baidu.com/ad/index.html?id=8&name=mouse#tag=110

或者:

const pathname = '/a/b/c';
const search = '?d=e';
const hash = '#fgh';
const myURL = new URL(`https://example.org$pathname$search$hash`);
console.log(myURL.href);

使用url.format方法可以自定义序列化url字符串,format方法接收两个参数:

  1. new URL返回的一个WHATWG URL格式的对象
  2. 配置对象:
    • fragment:序列化的网址字符串是否包含片段,默认为true
    • auth:序列化的网址字符串是否包含用户名和密码,默认为true
    • unicode:是否将出现在 URL 字符串的主机组件中的 Unicode 字符直接编码而不是Punycode 编码,默认是false
    • search:序列化的网址字符串是否包含搜索查询(参数),默认为true
const myURL = new URL(
    "https://username:password@URL路径序列化测试?name=ailjx#foo"
);

console.log(
    url.format(myURL, 
        fragment: false, // 不显示片段(#foo)
        unicode: true, // 不转化Unicode字符(中文字符)
        auth: false, // 不包含用户名和密码(username:password)
        search: false, // 不显示参数(?name=ailjx)
    )
);
// 打印结果: 'https://url路径序列化测试/'