Node.js之Web学习笔记

http模块

入门

Node.js提供了系统模块http用来写服务端代码。http模块是系统核心模块,不需要下载只需要直接引入即可,引入代码如下:

var htttp = require('http');

创建简单的服务端代码如下:

// 第一步,引入http模块
var http = require("http");

// 第二步,通过http模块的createServer方法创建一个server
var server = http.createServer();

// 第三步,监听"request"事件,获取请求,发送响应
server.on("request", function (request, response) {
    
    
    console.log("接收到客服端请求,请求路径是:" + request.url);
});

// 第四步,设置监听端口,启动服务器
server.listen(8888, function () {
    
    
    console.log("服务器启动成功,可以通过 http://127.0.0.1:8888 来进行访问");
});

注意:

  • request是请求处理函数,会接收两个参数:Request请求对象和Response响应对象。请求对象Request可以获取客服端的一些请求信息,如请求路径;响应对象Response可以来给客服端发送响应消息。
  • listen()函数中的端口号可以任意设置。
  • 每次修改代码后要重启。

在浏览器输入http://localhost:8888/hello访问就可以在控制台看到打印结果:
在这里插入图片描述

响应数据给客户端

如果我们要响应一些数据给客服端,可以通过响应对象Response来输出一些信息。

  • response.write(data);:向客户端输出数据,可以多次调用该方法。
  • response.end();:结束输出。
// 第一步,引入http模块
var http = require("http");

// 第二步,通过http模块的createServer方法创建一个server
var server = http.createServer();

// 第三步,监听"request"事件,获取请求,发送响应
server.on("request", function (request, response) {
    
    
    console.log("接收到客服端请求,请求路径是:" + request.url);
    // 通过Response对象的write()方法可以向客户端输出数据
    response.write("hello world!\n");
    response.write("hello node.js!");
    // 最后还要通过end()方法来结束输出,否则会一直等待
    response.end();
});

// 第四步,设置监听端口,启动服务器
server.listen(8888, function () {
    
    
    console.log("服务器启动成功,可以通过 http://127.0.0.1:8888 来进行访问");
});

在浏览器查看如下:
在这里插入图片描述
其实可以直接通过response.end(data);方法向客户端输出数据。

server.on("request", function (request, response) {
    
    
    console.log("接收到客服端请求,请求路径是:" + request.url);
    // 通过Response对象的end(data)方法可以向客户端输出数据
    response.end("hello world!")
});

根据不同请求路径返回不同数据

现在写的代码功能非常小,无论什么请求,响应的结果都是"hello world!",我们需要根据不同的请求路径来响应不同的结果,如/index响应首页、/login响应登录、/register响应注册等。可以通过if判断url来进行不同的响应:

// 第一步,引入http模块
var http = require("http");

// 第二步,通过http模块的createServer方法创建一个server
var server = http.createServer();

// 第三步,监听"request"事件,获取请求,发送响应
/*
根据不同的请求路径发送不同的响应结果:
1.获取请求路径
    通过request.url属性获取到请求的路径,即端口之后的那一部分路径,如"http://127.0.0.1:8888"默认是"/","http://127.0.0.1:8888/hello"是"/hello"
2.根据if判断来根据路径进行不同的响应返回
 */
server.on("request", function (request, response) {
    
    
    // 获取url属性,然后if判断
    var url = request.url;
    if (url === '/') {
    
    
        response.end('index page');
    } else if (url === '/hello') {
    
    
        response.end('hello world!');
    } else if (url === '/login') {
    
    
        response.end('login page');
    } else {
    
    
        response.end('404 Not Found.');
    }
});

// 第四步,设置监听端口,启动服务器
server.listen(8888, function () {
    
    
    console.log("服务器启动成功,可以通过 http://127.0.0.1:8888 来进行访问");
});

响应中文数据

当我们直接响应返回中文数据的时候,浏览器页面会中文乱码显示。因为node.js响应回的数据编码格式是UTF-8,而中文系统的浏览器页面解析用的是GBK。通过设置Content-Type响应头来设置响应数据内容的编码格式:

// 第一步,引入http模块
var http = require("http");

// 第二步,通过http模块的createServer方法创建一个server
var server = http.createServer();

// 第三步,监听"request"事件,获取请求,发送响应
server.on("request", function (request, response) {
    
    
    // 通过响应对象Response的setHeader(name, value)方法设置响应头,来设定响应数据内容是什么类型的
    response.setHeader('Content-Type', 'text/plain; charset=utf-8');
    response.end('hello 世界!');
});

// 第四步,设置监听端口,启动服务器
server.listen(8888, function () {
    
    
    console.log("服务器启动成功,可以通过 http://127.0.0.1:8888 来进行访问");
});

Content-Type还可以设置其他响应内容格式:

// 第一步,引入http模块
var http = require("http");
var fs = require("fs");

// 第二步,通过http模块的createServer方法创建一个server
var server = http.createServer();

// 第三步,监听"request"事件,获取请求,发送响应
server.on("request", function (request, response) {
    
    
    // 通过响应对象Response的setHeader(name, value)方法设置响应头,来设定响应数据内容是什么类型的
    var url = request.url;
    if (url === '/plain') {
    
    // 普通文本
        // text/plain是普通文本类型
        response.setHeader('Content-Type', 'text/plain; charset=utf-8');
        response.end('hello 世界!');
    } else if (url === '/html') {
    
    // 直接输出html内容
        // text/html是html格式的字符串
        response.setHeader('Content-Type', 'text/html; charset=utf-8');
        response.end("<p>hello node.js.<a href='#'>点我</a></p>")
    } else if (url === '/html-from-file') {
    
    // 从.html文件中读取内容
        fs.readFile('hello.html', function (err, data) {
    
    
            if (!err) {
    
    
                // text/html是html格式的字符串,通常读取.html文件中的内容
                response.setHeader('Content-Type', "text/html; charset=utf-8");
                response.end(data);
            } else {
    
    
                response.setHeader('Content-Type', 'text/plain; charset=utf-8');
                response.end('hello.html文件读取失败,请稍后重试!');
            }
        });
    } else if (url === '/star.jpg') {
    
    
        fs.readFile('star.jpg', function (err, data) {
    
    // 图片
            if (!err) {
    
    
                // data默认是二进制数据,可以通过data.toString()方法转换成能识别的字符串
                // response.end(chunk)方法中的参数支持两种数据类型:二进制和字符串
                // 图片就不需要指定编码了,通常只有字符串才需要指定编码
                response.setHeader('Content-Type', "image/jpeg");
                response.end(data);
            } else {
    
    
                response.setHeader('Content-Type', 'text/plain; charset=utf-8');
                response.end('hello.html文件读取失败,请稍后重试!');
            }
        });
    }
});

// 第四步,设置监听端口,启动服务器
server.listen(8888, function () {
    
    
    console.log("服务器启动成功,可以通过 http://127.0.0.1:8888 来进行访问");
});

代码风格

关于JavaScript代码规范,比较规范的有:

而关于JavaScript中分号是否添加的问题:

;`hello`.toString()
/*
当采用了无分号的代码风格时,只需要注意下面几个情况:
当一行代码是以(或[或`开头的时候,则在前面补一个分号就能避免一些语法解析错误。
所以会发现一些第三方代码中以;开头。
所以无论代码是否有分号,都建议如果一行代码是以(或[或`开头的,则最好都在前面补上
一个分号,有些人也会使用!或~符号,都是同样的效果。
*/

模板引擎art-template

概述

可以用来服务端渲染数据,然后直接传到客户端使用。

官方教程:art-template

推荐博客:模板引擎 – art-template

基本使用

我们可以通过响应对象Response的write或end方法向客户端输出文本数据,也可以输出html格式的字符串,更可以读取.html文件向客户端输出数据。如果我们向客户端输出大量数据,那么可能需要拼接html格式的字符串来显示不同的内容,例如:

// 导入http模块
var http = require("http");

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

// 监听request事件
server.on("request", function (request, response) {
    
    
    var name = "张三";
    var age = 18;
    var hobbies = ["听歌", "阅读", "打游戏"];
    // 拼接字符串
    var html = `
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Hello</title>
    </head>
    <body>
        <p>大家好,我的名字是:` + name + `</p>
        <p>我今年` + age + `岁了</p>
        <p>我的爱好有:` + hobbies[0] + `` + hobbies[1] + `` + hobbies[2] +
        `</p>
    </body>
    </html>
    `;
    response.end(html);
});

// 服务器监听端口,启动服务器
server.listen(8888, function () {
    
    
    console.log("服务器启动了!可以通过 http://127.0.0.1:8888 进行访问!")
});

但我们发现拼接字符串非常麻烦,如果html代码很多,那么就更麻烦了。所以可以使用模板引擎,在html文件中的一些关键信息用一些符号进行标记,然后统一进行替换。而art-template就是这样的一个模板引擎。使用的步骤如下:

// 第一步,安装art-template模块
npm install art-template
// 第二步,在js文件中引入该模块
var template = require('art-template');
// 第三步,使用参考API
template.render('模板字符串', 替换对象);

所以我们先在当前项目下安装art-template模块。
在这里插入图片描述
然后创建一个html文件,在里面使用模板引擎的{ { }}语法。其中{ { }}里面的变量名任意取,但遍历需要遵循模板引擎的语法格式。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
<p>大家好,我的名字是:{
   
   { name }}</p>
<p>我今年{
   
   { age }}岁了</p>
<p>我的爱好有:{
   
   { each hobbies}} {
   
   { $value }} {
   
   { /each }}</p>
</body>
</html>

然后在js里通过读取.html文件的方式向客户端输出数据,并使用模板引擎渲染数据。

// 导入http模块
var http = require("http");
// 导入art-template模块
var template = require("art-template");
// 导入fs模块
var fs = require("fs");

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

// 监听request事件
server.on("request", function (request, response) {
    
    
    fs.readFile("./index.html", function (err, data) {
    
    
        if (!err) {
    
    
            // 默认读取到的data是二进制数据,模板引擎的render方法需要接收的是字符串,所以要将data转换成字符串才能给模板引擎使用
            var ret = template.render(data.toString(), {
    
    
                name: '张三',
                age: 19,
                hobbies: ['阅读', '听歌', '打游戏']
            });
            response.end(ret);
        }
    });
});

// 服务器监听端口,启动服务器
server.listen(8888, function () {
    
    
    console.log("服务器启动了!可以通过 http://127.0.0.1:8888 进行访问!")
});

注意:

  • template.render()方法在这里传入了两个参数,第一个参数就是写了模板语法的html字符串,不过这里传入的是从文件读取的html字符串;第二个参数是数据对象,里面定义了传给模板引擎的值,其中键名就是{ { }}里面的变量名,键值就是要赋予的具体值。
  • template.render()方法的返回值就是渲染数据成功返回的字符串,可以直接输出到客户端了。

在浏览器访问结果如下:
在这里插入图片描述

实例:文件浏览器

需求

需求:通过http模块和art-template模板引擎来实现文件浏览器的效果,如下图:
在这里插入图片描述

涉及知识

涉及模块:

  • http
  • fs
  • path

其他技术:

  • art-template模板引擎

实现原理:根据请求的路径使用fs模块直接读取系统中的文件目录及其相关属性,然后使用模板引擎art-template渲染出来。

代码实现(Windows系统)

在Windows系统下的代码和Linux系统下的路径有所区别:
app.js

var http = require('http');
var template = require('art-template');
var fs = require('fs');
var path = require('path');

var server = http.createServer();

server.on('request', function (request, response) {
    
    
    var url = request.url;
    if (url === '/') {
    
    
        fs.readFile('./index.html', function (err, data) {
    
    
            if (!err) {
    
    
                var files = [
                    {
    
    isDir: true, name: "C:/", path: "C:/"},
                    {
    
    isDir: true, name: "D:/", path: "D:/"},
                    {
    
    isDir: true, name: "E:/", path: "E:/"},
                    {
    
    isDir: true, name: "F:/", path: "F:/"},
                    {
    
    isDir: true, name: "G:/", path: "G:/"}];
                var ret = template.render(data.toString(), {
    
    files: files});
                response.end(ret);
            }
        });
    } else if (url.startsWith('/C:') || url.startsWith('/D:') || url.startsWith('/E:') || url.startsWith('/F:') || url.startsWith('/G:')) {
    
    
        url = decodeURIComponent(url.substring(1));// 中文是进行URL编码的,所以需要对路径进行解码
        // 根据目录路径读取该目录下的所有文件名称
        fs.readdir(url, function (err, files) {
    
    
            fs.readFile('./index.html', function (err, data) {
    
    
                if (!err) {
    
    
                    var paths = [];
                    for (var i in files) {
    
    
                        var item = new Object();
                        // 文件名
                        item.name = files[i];
                        // 文件绝对路径
                        var p = path.join(url, files[i]);
                        item.path = p;
                        if (files[i].startsWith('System Volume Information')) {
    
    
                            item.isDir = true;
                        } else if (files[i].startsWith('pagefile.sys') || files[i].startsWith('swapfile.sys')) {
    
    
                            item.isDir = false;
                        } else {
    
    
                            // 是否是目录
                            var stats = fs.statSync(p);
                            item.isDir = stats.isDirectory();
                            // 文件大小
                            item.size = stats.size;
                            // 文件的最后一次修改时间
                            var date = new Date(stats.mtime);
                            var mtime = date.getFullYear() + "/" + date.getMonth() + "/" + date.getDay() + "\t\t" + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds();
                            item.modifyTime = mtime;
                            // 将对象添加到数组中
                            paths.push(item);
                        }
                    }

                    var ret = template.render(data.toString(), {
    
    
                        title: url,
                        parentDir: path.resolve(url, '..'),// 获取当前目录的上一级目录
                        files: paths
                    });
                    response.end(ret);
                }
            })
        });
    } else {
    
    
        console.log(url);
    }
});

server.listen(8888, function () {
    
    
    console.log('server running...')
});

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件浏览器</title>
</head>
<body>
<h4>file:///{
   
   {title}} 的索引</h4>
<p><a href="/{
     
     {parentDir}}">回到上一层文件夹</a></p>
<table>
    <tr>
        <th>名称</th>
        <th>大小</th>
        <th>修改时间</th>
    </tr>
    {
   
   {each files}}
    <tr>
        <td width="300px">
            {
   
   {if $value.isDir}}
            <a href="/{
     
     {$value.path}}">{
   
   {$value.name}}</a>
            {
   
   {else}}
            <span>{
   
   {$value.name}}</span>
            {
   
   {/if}}
        </td>
        <td width="100px">
            {
   
   {$value.size}}
        </td>
        <td width="200px">
            {
   
   {$value.modifyTime}}
        </td>
    </tr>
    {
   
   {/each}}
</table>
</body>
</html>

效果图如下:
在这里插入图片描述在这里插入图片描述

代码实现(Linux系统)

如果要把代码部署到服务器上,使用下面的代码,因为Windows系统有盘符,而Linux系统的根目录是/

app.js:核心代码没什么区别,只是根路径是/而已,不需要针对盘符进行处理。

var http = require('http');
var template = require('art-template');
var fs = require('fs');
var path = require('path');

var server = http.createServer();

server.on('request', function (request, response) {
    
    
    var url = request.url;
    console.log(url);
    if (url.startsWith('/')) {
    
    
        url = decodeURIComponent(url);// 中文是进行URL编码的,所以需要对路径进行解码
        console.log(url);
        // 根据目录路径读取该目录下的所有文件名称
        fs.readdir(url, function (err, files) {
    
    
            fs.readFile('./index.html', function (err, data) {
    
    
                if (!err) {
    
    
                    var paths = [];
                    for (var i in files) {
    
    
                        var item = new Object();
                        // 文件名
                        item.name = files[i];
                        // 文件绝对路径
                        var p = path.join(url, files[i]);
                        item.path = p;
                        if (files[i].startsWith('System Volume Information')) {
    
    
                            item.isDir = true;
                        } else if (files[i].startsWith('pagefile.sys') || files[i].startsWith('swapfile.sys')) {
    
    
                            item.isDir = false;
                        } else {
    
    
                            // 是否是目录
                            var stats = fs.statSync(p);
                            item.isDir = stats.isDirectory();
                            // 文件大小
                            item.size = stats.size;
                            // 文件的最后一次修改时间
                            var date = new Date(stats.mtime);
                            var mtime = date.getFullYear() + "/" + date.getMonth() + "/" + date.getDay() + "\t\t" + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds();
                            item.modifyTime = mtime;
                            // 将对象添加到数组中
                            paths.push(item);
                        }
                    }

                    var ret = template.render(data.toString(), {
    
    
                        title: url,
                        parentDir: path.resolve(url, '..'),// 获取当前目录的上一级目录
                        files: paths
                    });
                    response.end(ret);
                }
            })
        });
    } else {
    
    
        console.log(url);
    }
});

server.listen(8888, function () {
    
    
    console.log('server running...')
});

index.html:只是修改了<a>标签的href属性,没有了前面的斜杠。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件浏览器</title>
</head>
<body>
<h4>file:///{
   
   {title}} 的索引</h4>
<p><a href="{
     
     {parentDir}}">回到上一层文件夹</a></p>
<table>
    <tr>
        <th>名称</th>
        <th>大小</th>
        <th>修改时间</th>
    </tr>
    {
   
   {each files}}
    <tr>
        <td width="300px">
            {
   
   {if $value.isDir}}
            <a href="{
     
     {$value.path}}">{
   
   {$value.name}}</a>
            {
   
   {else}}
            <span>{
   
   {$value.name}}</span>
            {
   
   {/if}}
        </td>
        <td width="100px">
            {
   
   {$value.size}}
        </td>
        <td width="200px">
            {
   
   {$value.modifyTime}}
        </td>
    </tr>
    {
   
   {/each}}
</table>
</body>
</html>

效果如下:
在这里插入图片描述在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/cnds123321/article/details/121322566