首页
Search
1
解决visual studio code (vscode)安装时没有选择安装路径问题
138 阅读
2
Linux 下 Bash 脚本 bad interpreter 报错的解决方法
131 阅读
3
Arch Linux 下解决 KDE Plasma Discover 的 Unable to load applications 错误
107 阅读
4
如何在 Clash for Windows 上配置服务
77 阅读
5
Uniapp如何引入自定义样式文件
75 阅读
clash
服务器
javascript
全部
游戏资讯
登录
Search
加速器之家
累计撰写
1,061
篇文章
累计收到
0
条评论
首页
栏目
clash
服务器
javascript
全部
游戏资讯
页面
搜索到
624
篇与
的结果
2024-10-21
从0到1学习node(三)之文件操作
总索引: 从0到1学习node系列总索引这节稍微的讲解下node中对文件和目录的操作,我们不一个个讲每个api的使用,只是从几个例子来了解下下文件系统。 1. 文件操作 # 在文件操作里,主要是有文件读写,创建、移动文件等。 1.1 读取文件 # 读取文本文件时,如.txt, .js, .json等文件,直接使用readFile就可以获取文件的内容。// server.js var fs = require('fs'); fs.readFile('./data.txt', 'utf-8', function(err, data){ if(err) throw err; console.log(data); }); 读取图片时,我们是不能直接输出到控制台中的,是需要创建一个服务器,然后在浏览器上进行查看。其实在上节中,我们已经了解过显示图片的过程了。// server.js var http = require('http'), fs = require('fs'); http.createServer(function(request, response){ // 使用二进制方式读取图片 fs.readFile('./img/test.png', 'binary', function(err, file){ if( err ) throw err; // 当前数据以image/png方式进行输出 response.writeHead(200, {"Content-Type": "image/png"}); response.write(file, 'binary'); response.end(); }); }).listen(3000); console.log('server has started...'); 打开浏览器:127.0.0.1:3000,就能看到图片了。 1.2 写入文件 # 将字符串写入到文件文件中,是非常简单的操作,使用writeFile即可搞定:var fs = require('fs'); var data = '从一开始,就选择了做前端开发,因为觉得前端开发更贴近用户,能够倾听用户的声音,更好玩,更有意思,美的更直观。我们总是在尝试最新的技术,尝试更炫的效果,希望更能优化用户的体验效果!'; fs.writeFile('./test.txt', data, function(err){ if(err) throw err; console.log('写入数据成功...'); }); writeFile方法,在没有文件时会创建文件并写入;若文件存在则内容被覆盖。 1.3 创建或文件重命名 # 根据writeFile的特性,可以使用writeFile写入空字符串的方式创建文件。同时,fs.open也可以创建文件:// 打开模式可以使用 w | w+ | a | a+ // 这些模式在打开不存在的文件时,会创建文件 // fd为一个整数,表示打开文件返回的文件描述符,window中又称文件句柄 fs.open(Date.now()+'.txt', 'a+', function(err, fd){ if(err) throw err; console.log(fd); }) 在文件系统中,有一个fs.rename的方法,顾名思义,对文件(文件夹)进行重命名。fs.rename(oldname, newname, callback(err)); 特性:将oldname文件(目录)移动至newname的路径下,并重新命名;如果oldname和newname是同一个路径,则直接进行重命名。 2. 文件夹操作 # 通常对目录的操作比较简单一些。 2.1 读取文件夹中的文件和文件夹列表 # 使用fs.readdir(path, callback)可以获取path路径下的文件和目录列表,而且只能读取直接目录下的文件和文件夹,子目录里的是获取不到的。fs.readdir('./', function(err, files){ if(err) throw err; console.log( files ); }); 输出结果:[ 'img', 'msg.txt', 'node_modules', 'package.json', 'server.js', 'test.js', 'tmp' ] node_modules和tmp是文件夹,剩下的是文件,而且是获取不到node_modules和tmp里面的数据。获取一个目录下所有的文件,后面会讲解,稍等。 2.2 删除文件夹 # 使用fs.rmdir(path, callback)可以删除文件夹,但只能删除空文件夹,如果当前路径不是文件夹或当前文件夹不为空,则删除失败;删除的为空文件夹时,可以删除成功。fs.rmdir('./tmp', function(err){ if(err){ console.log('删除文件夹失败'); throw err; }else{ console.log('删除成功'); } }) 如何删除不为空的目录,后面会讲解,稍等。 2.3 获取文件或文件夹的信息 # fs.stat(path, callback)能够获取path路径的信息,比如创建时间,修改时间,文件大小,当前是否为文件,当前是否为文件夹等信息;如果path路径不存在,则抛出异常。fs.stat('./test.js', function(err, stats){ if( err ){ console.log( '路径错误' ); throw err; } console.log(stats); console.log( 'isfile: '+stats.isFile() ); // 是否为文件 console.log( 'isdir: '+stats.isDirectory() ); // 是否为文件夹 }); 结果:{ dev: -29606086, mode: 33206, nlink: 1, uid: 0, gid: 0, rdev: 0, blksize: undefined, ino: 2251799813687343, size: 2063, // path路径为文件夹时,size为0 blocks: undefined, atime: Thu Jan 12 2017 21:12:36 GMT+0800 (中国标准时间), mtime: Sat Jan 14 2017 21:57:26 GMT+0800 (中国标准时间), ctime: Sat Jan 14 2017 21:57:26 GMT+0800 (中国标准时间), birthtime: Thu Jan 12 2017 21:12:36 GMT+0800 (中国标准时间) } isfile: true // 是否为文件 isdir: false // 是否为文件夹 关于这几个时间属性的理解,可以参考我之前写的博文:【对gulp-changed插件的一点思考】stats中的size属性就是当前文件的大小(单位:字节,除以1024即为kb),stats还有下面方法可供使用: stats.isFile() stats.isDirectory() stats.isBlockDevice() stats.isCharacterDevice() stats.isSymbolicLink() (only valid with fs.lstat()) stats.isFIFO() stats.isSocket() fs.stat(path, callback)是异步执行的,对应的还有同步执行版本:fs.statSync(path),这个方法返回的就是fs.stats实例。 3. 综合运用 # 我们在上面的讲解中,还留着两个功能没实现,这里实现一下它的过程。 3.1 遍历目录中所有的文件 # 我们已经知道使用readdir只能获取当前目录里的文件和文件夹名称,为了获取这个目录里所有的文件名称,只能是读取当前目录里所有的文件夹里的文件。这里我们使用递归的方法,如果当前资源是文件,则进行存储,是文件夹则进行递归进一步检索,直到把所有的文件夹遍历完毕。// 获取文件夹中所有的文件 function readDirAll(path){ // 获取字符串的最后一个字符 var getLastCode = function(str){ return str.substr(str.length-1, 1); } var result = []; // 存储获取到的文件 var stats = fs.statSync(path); // 获取当前文件的状态 if( stats.isFile() ){ result.push(path); }else if( stats.isDirectory() ){ // 若当前路径是文件夹,则获取路径下所有的信息,并循环 var files = fs.readdirSync(path); for(var i=0, len=files.length; i
2024年10月21日
4 阅读
0 评论
0 点赞
2024-10-21
从0到1学习node(四)之简易的网络爬虫
总索引: 从0到1学习node系列总索引我们这节的目标是学习完本节课程后,能进行网页简单的分析与抓取,对抓取到的信息进行输出和文本保存。爬虫的思路很简单: 确定要抓取的URL; 对URL进行抓取,获取网页内容; 对内容进行分析并存储; 重复第1步 在这节里做爬虫,我们使用到了两个重要的模块: request : 对http进行封装,提供更多、更方便的接口供我们使用,request进行的是异步请求。更多信息可以去[request-github]上进行查看 cheerio : 类似于jQuery,可以使用$(), find(), text(), html()等方法提取页面中的元素和数据,不过若仔细比较起来,cheerio中的方法不如jQuery的多。 1. hello world # 说是hello world,其实首先开始的是最简单的抓取。我们就以cnode网站为例(https://cnodejs.org/),这个网站的特点是: 不需要登录即可访问首页和其他页面 页面都是同步渲染的,没有异步请求的问题 DOM结构清晰 代码如下:var request = require('request'), cheerio = require('cheerio'); request('https://cnodejs.org/', function(err, response, body){ if( !err && response.statusCode == 200 ){ // body为源码 // 使用 cheerio.load 将字符串转换为 cheerio(jQuery) 对象, // 按照jQuery方式操作即可 var $ = cheerio.load(body); // 输出导航的html代码 console.log( $('.nav').html() ); } }); 这样的一段代码就实现了一个简单的网络爬虫,爬取到源码后,再对源码进行拆解分析,比如我们要获取首页中第1页的 问题标题,作者,跳转链接,点击数量,回复数量。通过chrome,我们可以得到这样的结构:每个div[.cell]是一个题目完整的单元,在这里面,一个单元暂时称为$item{ title : $item.find('.topic_title').text(), url : $item.find('.topic_title').attr('href'), author : $item.find('.user_avatar img').attr('title'), reply : $item.find('.count_of_replies').text(), visits : $item.find('.count_of_visits').text() } 因此,循环div[.cell],就可以获取到我们想要的信息了:request('https://cnodejs.org/?_t='+Date.now(), function(err, response, body){ if( !err && response.statusCode == 200 ){ var $ = cheerio.load(body); var data = []; $('#topic_list .cell').each(function(){ var $this = $(this); // 使用trim去掉数据两端的空格 data.push({ title : trim($this.find('.topic_title').text()), url : trim($this.find('.topic_title').attr('href')), author : trim($this.find('.user_avatar img').attr('title')), reply : trim($this.find('.count_of_replies').text()), visits : trim($this.find('.count_of_visits').text()) }) }); // console.log( JSON.stringify(data, ' ', 4) ); console.log(data); } }); // 删除字符串左右两端的空格 function trim(str){ return str.replace(/(^\s*)|(\s*$)/g, ""); } 2. 爬取多个页面 # 上面我们只爬取了一个页面,怎么在一个程序里爬取多个页面呢?还是以CNode网站为例,刚才只是爬取了第1页的数据,这里我们想请求它前6页的数据(别同时抓太多的页面,会被封IP的)。每个页面的结构是一样的,我们只需要修改url地址即可。 2.1 同时抓取多个页面 # 首先把request请求封装为一个方法,方便进行调用,若还是使用console.log方法的话,会把6页的数据都输出到控制台,看起来很不方便。这里我们就使用到了上节文件操作内容,引入fs模块,将获取到的内容写入到文件中,然后新建的文件放到file目录下(需手动创建file目录):// 把page作为参数传递进去,然后调用request进行抓取 function getData(page){ var url = 'https://cnodejs.org/?tab=all&page='+page; console.time(url); request(url, function(err, response, body){ if( !err && response.statusCode == 200 ){ console.timeEnd(url); // 通过time和timeEnd记录抓取url的时间 var $ = cheerio.load(body); var data = []; $('#topic_list .cell').each(function(){ var $this = $(this); data.push({ title : trim($this.find('.topic_title').text()), url : trim($this.find('.topic_title').attr('href')), author : trim($this.find('.user_avatar img').attr('title')), reply : trim($this.find('.count_of_replies').text()), visits : trim($this.find('.count_of_visits').text()) }) }); // console.log( JSON.stringify(data, ' ', 4) ); // console.log(data); var filename = './file/cnode_'+page+'.txt'; fs.writeFile(filename, JSON.stringify(data, ' ', 4), function(){ console.log( filename + ' 写入成功' ); }) } }); } CNode分页请求的链接:https://cnodejs.org/?tab=all&page=2,我们只需要修改page的值即可:var max = 6; for(var i=1; i
2024年10月21日
4 阅读
0 评论
0 点赞
2024-10-21
从0到1学习node(五)之mysql数据库的操作
总索引: 从0到1学习node系列总索引使用node操作mysql数据库时,需要先下载mysql模块:npm install mysql --save-dev 在引入mysql模块后,就可以进行数据库的连接和其他的操作了。// test.js var mysql = require('mysql'); 1. 连接数据库 # 首先保证本地已经安装数据库,并已正常启动,然后开始进行连接:// test.js var mysql = require('mysql'); // 创建连接 var conn = mysql.createConnection({ host : '127.0.0.1', user : 'root', password : '123', database : 'test' }); // 创建连接后不论是否成功都会调用 conn.connect(function(err){ if(err) throw err; console.log('connect success!'); }); // 其他的数据库操作,位置预留 // 关闭连接时调用 conn.end(function(err){ if(err) throw err; console.log('connect end'); }) 执行node test.js后,就会输出:$ node test.js connect success! connect end 连接成功,然后连接关闭。这就说明程序可以正常连接数据库了,然后就开始进行增删改查的操作。 2. CURD # 比如我们有这样的一个user表,里面有4个字段,其中uid是自增字段: uid username password email 1 meizi meizi 123@qq.com 2 test test 456@qq.com 我们就对这个表进行增删改查的操作。mysql中有个query方法可以用来执行任意正确的sql语句,然后在回调函数里给出执行sql语句后的结果。query方法是异步执行的,若并列书写多个query方法的话,是不能按照书写顺序依次阻塞式执行的。 2.1 查询 # 使用最普遍最多的就是查询操作了。conn.query('SELECT * FROM `user`', function(err, result, fields){ if(err) throw err; console.log(result); }); console.log( 'select ended!' ); 输出的结果:select ended! // 先输出 [ RowDataPacket { uid: 1, username: 'meizi', password: 'meizi' email: '123@qq.com' }, RowDataPacket { uid: 2, username: 'test', password: 'test' email: '456@qq.com' } ] 可以看到,结果集是一个数组,数组中的每条数据都是一个RowDataPacket对象,在使用时,可以像json对象一样获取数据,也可以使用JSON.stringify把result转换为json字符串,但是,result并不是JSON数据。而且,即使结果集中只有一条数据,也是以数组的形式返回的。console.log(result[0].username); // meizi 输出即为字符串类型的数据。 2.2 添加 # 向数据库中添加数据使用的是INSERT,INSERT语句有两种形式都可以使用:第1种,先列好要插入的数据对应的字段,然后跟上数据(如果要给所有的字段都插入数据,可以省略字段不写,但是数据的书写顺序要跟数据表里的字段一一对应):INSERT INTO table_name ( field1, field2,...fieldN ) VALUES ( value1, value2,...valueN ); 第2种,可以像update操作一样书写,将field与value对应的更紧密:INSERT INTO table_name SET field1=value1, field2=value2, ... fieldN=valueN; 我更加喜欢第2种方式,这种方式更能看出操作了哪些字段,看出字段和数据的对应关系。在node中插入数据:conn.query("INSERT INTO `user` SET `username`='qwerty', `password`='741', `email`='qwerty@qq.com'", function(err, result){ if(err) throw err; console.log(result); }); 插入数据后返回的结果是:OkPacket { fieldCount: 0, affectedRows: 1, insertId: 4, // 数据插入成功时,对应的主键id serverStatus: 2, warningCount: 0, message: '', protocol41: true, changedRows: 0 } affectedRows表示数据表中受影响的行数,数据插入成功则为1,失败则为0;在主键自增的情况下,insertId是数据插入成功后对应的主键id,如果主键不自增,则insertId为0。 2.3 更新 # 使用update语句更新数据:// 更新uid的密码 conn.query('UPDATE `user` SET `password`="123456" WHERE `uid`=4', function(err, result){ if(err) throw err; console.log(result); }); 输出的结果:OkPacket { fieldCount: 0, affectedRows: 1, insertId: 0, serverStatus: 2, warningCount: 0, message: '(Rows matched: 1 Changed: 1 Warnings: 0', protocol41: true, changedRows: 1 } 可以看到输出结果的类型与插入数据时输出结果的类型是一样的。我们分析一下,执行sql语句后,有3种结果: 成功修改数据: affectedRows:1, changedRows:1 要修改的数据与原数据相同: affectedRows:1, changedRows:0 未找到需要修改的数据: affectedRows:0, changedRows:0 因此可以根据这两个字段输出相应的结果。 2.4 删除 # 使用delete语句删除语句:conn.query('DELETE FROM `user` WHERE `uid`=4', function(err, result, fields){ if(err) throw err; console.log(result); }); 输出的结果:OkPacket { fieldCount: 0, affectedRows: 1, insertId: 0, serverStatus: 2, warningCount: 0, message: '', protocol41: true, changedRows: 0 } 删除成功,则affectedRows为1,删除的数据不存在,则为0。 3. 连接池 # 数据库连接是一种有限的,能够显著影响到整个应用程序的伸缩性和健壮性的资源,在多用户的网页应用程序中体现得尤为突出。数据库连接池正是针对这个问题提出来的,它会负责分配、管理和释放数据库连接,允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个连接,释放空闲时间超过最大允许空闲时间的数据库连接以避免因为连接未释放而引起的数据库连接遗漏。 3.1 创建连接池 # 使用mysql.createPool()可创建连接池:// test.js var mysql = require('mysql'); var pool = mysql.createPool({ host : '127.0.0.1', user : 'root', password : '123', database : 'test' }) pool.query('SELECT * FROM `user`', function(err, result){ if(err) throw err; console.log(result); pool.end(function(err){ if(err) throw err; console.log('connection ended'); }) }); getConnection()可以共享一个连接,或管理多个连接。// test.js var mysql = require('mysql'); var pool = mysql.createPool({ host : '127.0.0.1', user : 'root', password : '123', database : 'test' }) pool.getConnection(function(err, connection){ if(err) throw err; connection.query('SELECT * FROM `user`', function(err, result){ if(err) throw err; console.log(result); }) }); 连接使用完后通过调用connection.release()方法可以将连接返回到连接池中,这个连接可以被其它人重复使用:pool.getConnection(function(err, connection){ if(err) throw err; connection.query('SELECT * FROM `user`', function(err, result){ if(err) throw err; console.log(result); connection.release(); // 接下来connection已经无法使用,它已经被返回到连接池中 }) }); 可以使用connection.destroy()彻底销毁连接。 3.2 连接池事件 # createPool()方法会返回一个连接池实例对象,这个对象中有一些事件。connection连接池中产生新连接时会发送'connection'事件:pool.on('connection', function (connection) { console.log('new connection'); }); 3.3 query与getConnection的区别 # 这两个方法都能进行操作,那么这两者有什么区别呢?在pool.getConnection中的connection在其回调函数里是一直的,可以保证这一系列的操作都是在同一个connection中执行的;pool.query则每次执行时可能会在不同的connection中执行,可能会得到意想不到的结果。比如SQL_CALC_FOUND_ROWS和FOUND_ROWS这需要两个sql语句完成,是获取检索行的数目。pool.query('SELECT SQL_CALC_FOUND_ROWS * FROM `user`'); pool.query('SELECT FOUND_ROWS()'); 这两个可能在不同的connection中执行,第2个sql语句返回的就不是上一个sql语句的结果了。 4. sql防注入 # sql防注入的关键就是不能直接把数据拼接到sql语句中,必须得对数据进行转义,或者使用提供的方法拼接sql语句。这里主要有四种方法可以使用。 4.1 使用escape()对参数进行编码 # 参数编码方法有:mysql.escape()/connection.escape()/pool.escape(),这三个方法可以在你需要的时候调用:var sql = 'SELECT * FROM `user` WHERE `uid`='+connection.escape('"123";//--'); console.log(sql); // SELECT * FROM `user` WHERE `uid`='\"123\";//--' connection.query(sql, function(err, result){ if(err) throw err; console.log(result); }) 对双引号进行了安全转义。escapeId()可以对不信任的表名,字段名进行转义。var sql = 'SELECT * FROM '+connection.escapeId('user')+' WHERE `uid`=1'; connection.query(sql, function(err, result){ console.log(result); }) console.log(query.sql); // SELECT * FROM `user` WHERE `uid`=1 同时,escape()的编码规则如下: Numbers不进行转换 Booleans转换为true/false Date对象转换为’YYYY-mm-dd HH:ii:ss’字符串 Buffers转换为hex字符串,如X'0fa5' Strings进行安全转义 Arrays转换为列表,如['a', 'b']会转换为'a', 'b' 多维数组转换为组列表,如[['a', 'b'], ['c', 'd']]会转换为('a', 'b'), ('c', 'd') Objects会转换为key=value键值对的形式。嵌套的对象转换为字符串 undefined/null会转换为NULL MySQL不支持NaN/Infinity,并且会触发MySQL错误 4.2 占位符 # 可以使用?作为参数占位符。在使用查询参数占位符时,在其内部自动调用 connection.escape() 方法对传入参数进行编码。var params = ['test', 'test']; var query = connection.query('SELECT * FROM `user` WHERE `username`=? AND `password`=?', params, function(err, result){ console.log(result); }); console.log(query.sql); // SELECT * FROM `user` WHERE `username`='test' AND `password`='test' 同时,如果执行添加或更新操作时,还可以这样写:var params = {username:'qwerty', password:'qwerty', email:'qwerty@qq.com'}; var query = connection.query('INSERT INTO `user` SET ?', params, function(err, result){ if(err) throw err; console.log(result); }); console.log(query.sql); // INSERT INTO `user` SET `username` = 'qwerty', `password` = 'qwerty', `email` = 'qwerty@qq.com' 数据库中的表明和字段名,可以使用??作为占位符,在拼接完成后会自动添加上``:var params = ['user', 'username', 'test', 'password', 'test']; var query = connection.query('SELECT * FROM ?? WHERE ??=? AND ??=?', params, function(err, result){ console.log(result); }) console.log(query.sql); // SELECT * FROM `user` WHERE `username`='test' AND `password`='test' 4.3 使用mysql.format()转义参数 # 不多说,样例如下:var userId = 1; var sql = "SELECT * FROM ?? WHERE ?? = ?"; var inserts = ['user', 'uid', userId]; sql = mysql.format(sql, inserts); // SELECT * FROM `user` WHERE `uid` = 1 5. 多语句查询 # 出于安全考虑node-mysql默认禁止多语句查询(可以防止SQL注入),启用多语句查询可以将multipleStatements选项设置为true:var connection = mysql.createConnection({multipleStatements: true}); 启用后可以在一个query查询中执行多条语句:connection.query('SELECT 1; SELECT 2', function(err, results) { if (err) throw err; // `results`是一个包含多个语句查询结果的数组 console.log(results[0]); console.log(results[1]); }); 6. 总结 # 本节只是总结了node对mysql数据库的各种操作,但如果实际应用起来的话,还远远不够。努力学习中...
2024年10月21日
7 阅读
0 评论
0 点赞
2024-10-21
第13页
Front-end Engineer,前端开发工程师目前是一名前端开发工程师,主要负责前端规划、框架与架构、前端性能优化。专注前端技术,关注交互体验,擅长web ajax开发。坚信前端工程师的价值是最终能把技术和设计完美结合在一起。用最新的技术方案巧妙地帮助这些设计得以实现。
2024年10月21日
3 阅读
0 评论
0 点赞
2024-10-21
从0到1学习node(六)之express初识
总索引: 从0到1学习node系列总索引这是春节后的第一篇文章,拖的时间比较久,有些读者还以为这个系列就这么终止了,其实不是,只是因为春节的原因耽搁了。好了,废话不多说,我们开始来了解下express。 1. 介绍 # 什么是express,为什么要使用express?根据官方网站的说法,express是一个基于 Node.js 平台的极简、灵活的web应用开发框架,它提供一系列强大的特性、丰富的API接口,对web应用的接口进行了二次的封装,提供了MVC模式,方便我们可以快速地创建各种web和移动应用。Express 框架核心特性: 可以设置中间件来响应 HTTP 请求。 定义了路由表用于执行不同的 HTTP 请求动作。 可以通过向模板传递参数来动态渲染 HTML 页面。 本文也只是简单的了解下express框架的内容,希望大家能比较快速的入门,更多详细的内容还是阅读官网并查看相关的API。express的中文官方网站:【Express】 2. 入门 # 创建一个目录myapp,进入到myapp后,使用命令npm express --save-dev把express安装到本地,然后创建app.js(或server.js)作为程序的入口。// app.js var express = require('express'); var app = express(); app.get('/', function(req, res){ res.send('hello world'); }); app.listen(3000, function(){ console.log('server has running at port 3000'); }) 运行app.js文件:$ node app.js server has running at port 3000 在浏览器中访问http://127.0.0.1:3000/就能看到页面上输出的hello world。说明基本的express程序可以正常运行了。 2.1 app # 引入express模块后,执行express()得到一个app实例,app实例中有get, post, use, listen等方法。app.get(path, handler): 当使用get方法访问路径path时,执行handler指定的方法,而且handler方法还带有req和res两个参数供我们使用。req是请求过来时带的信息,比如参数query, body, 头部header等; res是我们作为服务器想要返回给浏览器的信息设置。res.send('hello world')表示是向页面中发送'hello world'字符串。当然,如果想要接收post过来的请求,可以使用 app.post(path, function(req, res){}) 接收post到path的请求。app.listen用来监听本地的端口后运行web程序,监听成功后执行回调函数。 2.2 路由 # 我们在之前讲解《从0到1学习node(二)之搭建http服务器》也说过一点路由的内容,不过那时候我们制定的路由规则非常简单,而且只是处理了3个左右的页面而已。而express则对路由功能进行丰富。 app.get(path, handler) : get方式访问path路径 app.post(path, handler) : post方式访问path路径 app.put(path, handler) : put方式访问path路径 app.delete(path, handler) : delete方式访问path路径 app.all(path, handler) : 任何方式访问path路径 同时,我们也应该注意的是: /是表示根路径下,/user是表示user路径下,如果访问/user/login时,是直接访问/user/login路由的,前面的两个路由是不访问的。// 根路径下的请求 app.get('/', function(req, res){ console.log('hello world'); res.send('hello world'); }); // /user路径下的请求 app.get('/user', function(req, res){ console.log('user'); res.send('huser'); }); // /user/login下的请求 app.get('/user/login', function(req, res){ console.log('user/login'); res.send('user/login'); }); 而且,path路径还可以通过字符串匹配和正则匹配的方式进行路由选择。 2.3 res响应方法 # 我们在刚上面的例子中,使用res.send()向页面中输出一段'hello world'的纯文本字符串,而且res.send()也可以输出其他类型的数据,比如html字符串(浏览器可以解析),Buffer类型,Object类型,Array类型等。比如我们要输出一段html字符串。var html = '\ \ \ \ Document\ \ \ \hello world\\\ \ '; app.get('/', function(req, res, next){ res.send(html); }); 我们可以在浏览器上一个红色的hello world和一个文本输入框。但是若html的代码比较长,我们可以把这些代码都放到一个单独的html文件里,然后使用res.sendFile()方法,将html文件里的内容输出到页面中。在根目录下创建一个index.html文件,把完整的html代码放进去,然后:app.get('/', function(req, res, next){ res.sendFile('index.html'); }); 这样就能在浏览器中看到一个完整的页面了。此外,res中还提供了一些别的方法供我们使用: 方法 描述 res.download() 下载文件。 res.end() 终结响应处理流程。 res.json() 发送一个 JSON 格式的响应。 res.jsonp() 发送一个支持 JSONP 的 JSON 格式的响应。 res.redirect() 重定向请求。 res.render() 渲染视图模板。 res.send() 发送各种类型的响应。 res.sendFile 以八位字节流的形式发送文件。 res.sendStatus() 设置响应状态代码,并将其以字符串形式作为响应体的一部分发送。 3. 中间件 # 上面我们执行app.get('/', function(){})时,里面的回调函数就是中间件。中间件其实就是一个函数,在使用app.get, app.post, app.use等方法时,都是在调用中间件作为回调函数。 中间件都可以调用req和res对象,如果多个中间件顺序向下执行的话,上一个中间还需要一个next变量,来调用下一个中间件。这里app.use的使用方法与app.get一样,都是有两个参数:path和回调函数,而在这里,path参数是可以忽略不写的(忽略不写则每个请求都会执行该中间件)。// 任何的请求,该中间件都会响应 app.use(function(req, res, next){ console.log('index m url: '+req.url); next(); // 若没有next(),则请求就会被挂起,一直等待 }) // /topic 下的请求都会响应,包括 /topic/1.html, /topic/c/1.html等 app.use('/topic', function(req, res, next){ console.log('topic m url: '+req.url); next(); }) // 处理/根目录下的请求 app.get('/', function(req, res, next){ res.send('index'); }); // 处理 /topic/1.html 这种类型的请求 app.get('/topic/:id.html', function(req, res, next){ res.send('topic'); }); 我们在浏览器中输入一些不同的url看看: url 控制台输出 浏览器输出 说明 127.0.0.1:3000 index m url: / index /user index m url: /user Cannot GET /user 中间件响应了不存在页面的请求 /topic/1.html index m url: /topic/1.htmltopic m url: /1.html topic 两个use中间件都响应了请求 /topic/c/1.html index m url: /topic/c/1.htmltopic m url: /c/1.html Cannot GET /topic/c/1.html 两个use中间件都响应了请求,只是没有路由来对该url进行处理 同时,app.use()和app.get()等方法,可以调用多个中间件依次执行,使用next()将控制权交由下一个中间件。多个中间件既可以依次作为传输传递进去,也可以都放到数组中,也可以两者混用(app.get等同理):app.use(path, m1, m2, m3, m4...); app.use(path, [m1, m2, m3, ...]); app.use(path, [m1, m2, m3, ...], m7, m8, ...); 在上面代码的基础上,我们编写多个中间件。// 作为数组方式 app.use([ function(req, res, next){ console.log('index m 1'); next(); }, function(req, res, next){ console.log('index m 2'); next(); }, function(req, res, next){ console.log('index m 3'); next(); } ]) // 每个中间件作为一个参数 app.get('/topic/:id.html', function(req, res, next){ // res.send('topic'); console.log('topic get 1'); next(); }, function(req, res, next){ console.log('topic get 2'); next(); }, function(req, res, next){ console.log('topic get 3'); res.send('topic'); }); 当我们访问127.0.0.1/topic/1.html时,在控制台则会输出:index m 1 index m 2 index m 3 topic get 1 topic get 2 topic get 3 说明中间件是依次向下执行的。我们可以在每个中间件都做不同的处理,不过要记得使用next()方法,不然页面就挂了。我们在上面看到res中的方法,至少需要调用一个,不然请求就会被挂起,一直等待或404。如果对外没有任何的回复,也可以使用res.end()结束。同时,如果在某个中间件中使用了res中的方法,则后面的中间件不再调用。 4. 总结 # 这里我们也是简要的了解了下express框架,更多的内容还是需要查看官网网站。下一篇我们将使用express构建一个简单的论坛系统。
2024年10月21日
5 阅读
0 评论
0 点赞
1
...
22
23
24
...
125