微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

从零学习node.js之搭建http服务器二

前言

中我们了解了一下不同模块规范之间的联系与区别。本文我们正式开始node的学习,首先我们从搭建一个http服务器,能运行简单的程序开始说起。

一、hello world

最经典的hello world。首先我们创建一个server.js来保存我们的代码:

在终端输入node server.js运行:

终端就会输出 hello world 的字样。可是我们一个node服务器程序,总是要在浏览器上访问的呀,这里就要用到node里自带的http模块了:

// 创建http服务器
// request : 从浏览器带来的请求信息
// response : 从服务器返回给浏览器的信息
http.createServer(function(request,response){
response.writeHead(200,{'content-type': 'text/plain'}); // 设置头部信息,输出text文本
response.write('hello world'); // 输出到页面中的信息
response.end(); // 返回结束
}).listen(3000);
console.log('server has started...');

我们再次在终端输入node server.js运行,终端里会有输出 server has started… 的字样,表示服务器已创建并正在运行,然后我们在浏览器上访问127.0.0.1:3000,就可以看到页面中输出了hello world。

二、form表单

刚才我们只是在页面中输出了一段简单的文本,现在我们要在页面中呈现一个表单,可以让用户输入信息并进行提交:

http.createServer(function(request,response){
var html = '\

\ \ \ \
username :

\

password :

\

age :

\

\
\ \ ';

response.writeHead(200,{'content-type': 'text/html'}); // 输出html头信息
response.write(html); // 将拼接的html字符串输出到页面中
response.end(); // 结束
}).listen(3000);
console.log('server has started...');

修改server.js中的内容,重新运行:

刷新页面后,我们发现页面中输出了3个文本框和1个提交按钮。因为我们的程序只是呈现页面,并没有做任何其他的处理,因此在页面中提交数据只是刷新当前页面。

注意: 我们每次修改node中的任何代码后,都要重新进行启动。

2.1 获取表单GET方式提交的数据

我们上面的代码中使用的是POST方式,不过这里要先讨论使用GET方式提交过来的数据,我们先不考虑数据的安全性,只是学习如何获取使用get方式提交过来的form表单数据,将post改为get,重新运行。

我们知道,使用get方式提交数据,会将数据作为URL参数传递过来,因此我们通过解析URL中的参数获取到数据,这里就用到了url模块中的方法:

http.createServer(function(request,response){
var html = '\

\ \ \ \
username :

\

password :

\

age :

\

\
\ \ ';

var query = url.parse( request.url,true ).query;
if( query.submit ){
var data = '

back

'+
'

username:'+query.username+'

'+
'

password:'+query.password+'

'+
'

age:'+query.age+'

';

response.writeHead(200,{'content-type': 'text/html'});
response.write(data);
}else{
response.writeHead(200,{'content-type': 'text/html'});
response.write(html);
}
response.end(); // 结束
}).listen(3000);
console.log('server has started...');

我们再次运行提交后就能在页面显示出数据了。

url.parse是用来解析URL字符串的,并返回解析后的URL对象。若我们只输出一下 url.parse(request.url)

rush:js;"> url.parse(request.url);

result:
Url {
protocol: null,slashes: null,auth: null,host: null,port: null,hostname: null,hash: null,search: '?username=111113&password=123&age=122&submit=submit',query: 'username=111113&password=123&age=122&submit=submit',pathname: '/',path: '/?username=111113&password=123&age=122&submit=submit',href: '/?username=111113&password=123&age=122&submit=submit'
}

如果将第2个参数设置为true,则会将返回结果中的query属性解析为一个对象,其他属性不变;认值为false,即query属性一个字符串:

rush:js;"> url.parse(request.url,true);

result:
Url {
...
query: {
username: '111113',password: '123',age: '122',submit: 'submit' },...
}

因此我们可以通过如下语句判断是否有提交数据并获取提交数据,然后再输出到中即可:

rush:js;"> var query = url.parse( request.url,true ).query; /* { username: '111113',submit: 'submit' } */

2.2 获取表单POST方式提交的数据

现在我们使用post方式来提交数据。因为POST请求一般都比较“重” (用户可能会输入大量的内容),如果用阻塞的方式来处理处理,必然会导致用户操作的阻塞。因此node将post数据拆分为很多小的数据块,然后通过data事件(表示新的小数据块到达了)和end事件传递这些小数据块(表示所有的数据都已经接收完毕)。 所以,我们的思路应该是:在data事件中获取数据块,在end事件中操作数据。

rush:js;"> // server.js var http = require('http'),querystring = require('querystring');

http.createServer(function(request,response){
var html = '\

\ \ \ \
username :

\

password :

\

age :

\

\
\ \ ';

if( request.method.toLowerCase()=='post' ){
var postData = '';

request.addListener('data',function(chunk){
postData += chunk;
});

request.addListener('end',function(){
var data = querystring.parse(postData);
console.log( 'postData: '+postData );
console.log(data);

var s = '

back

'+
'

username:'+data.username+'

'+
'

password:'+data.password+'

'+
'

age:'+data.age+'

';

response.writeHead(200,{'content-type': 'text/html'});
response.write(s);
response.end();
})
}else{
response.writeHead(200,{'content-type': 'text/html'});
response.write(html);
response.end();
}
}).listen(3000);
console.log('server has started...');

这段代码与上段代码项目,主要有的几个变化是:

  1. 不再引入url模块, 改用引入querystring模块。因为我们不再对URL进行操作了,也没必要引入了;
  2. 使用request.method.toLowerCase()=='post'判断当前是否有数据提交;
  3. 在data事件进行数据的拼接,在end事件中进行的处理;
  4. response.end()写在了end事件内部,因为end事件是异步操作,因此必须得数据输出完成之后才能执行response.end()

我们在控制台中可以看出,postData是这样的一个字符串:

rush:js;"> 'username=123&password=123&age=23&submit=submit';

因此我们使用query.parse将postData解析为对象类型,以便获取提交过来的数据。

三、路由

现在我们所有的逻辑都是在根目录下进行的,没有按照url区分,这里我们按照功能进行路由拆分。以上面的post请求为例,我们可以拆分为:页面初始化和form提交后的处理。

页面初始化:

rush:js;"> // starter.js 页面初始化

function start(request,response){
var html = '\

\ \ \ \
username :

\

password :

\

age :

\

\
\ \ ';

response.writeHead(200,{"Content-Type":"text/html"});
response.write( html );
response.end();
}
exports.start = start;

展示获取的数据:

rush:js;"> // uploader.js 展示获取的数据 var querystring = require('querystring');

function upload(request,response){
var postData = '';

request.addListener('data',function(chunk){
postData += chunk;
});

request.addListener('end',function(){
var data = querystring.parse(postData);
console.log( 'postData: '+postData );
console.log(data);

var s = '

back

'+
'

username:'+data.username+'

'+
'

password:'+data.password+'

'+
'

age:'+data.age+'

';

response.writeHead(200,{'content-type': 'text/html'});
response.write(s);
response.end();
})
}
exports.upload = upload;

然后在server.js中进行路由选择

rush:js;"> // server.js var http = require('http'),response){ var pathname = url.parse(request.url).pathname; console.log(pathname); response.end(); }).listen(3000); console.log('server has started...');

我们任意改变URL地址,会看到输出的每个地址的pathname(忽略/favicon.ico):

rush:js;"> http://127.0.0.1:3000/ // 输出: / http://127.0.0.1:3000/show/ // 输出: /show/ http://127.0.0.1:3000/show/img/ // 输出: /show/img/ http://127.0.0.1:3000/show/?username=wenzi // 输出: /show/

因此我们就根据pathname进行路由,对路由进行方法映射:

rush:js;"> // server.js var http = require('http'),url = require('url'),starter = require('./starter'),uploader = require('./uploader');

http.createServer(function(request,response){
var pathname = url.parse(request.url).pathname;
var routeurl = {
'/' : starter.start,'/show' : uploader.upload
}

if( typeof routeurl[pathname]=== 'function' ){
routeurlpathname;
}else{
console.log('404 not found!');
response.end();
}
}).listen(3000);
console.log('server has started...');

如果匹配到路由 / ,则执行 starter.start(request,response) ;如果匹配到路由 /show ,则执行 uploader.upload(request,response) 。如果都没匹配到,则显示404。

四、图片上传显示

在上面我们已经能成功提交数据了,这里来讲解如何进行图片上传显示。使用node自带的模块处理起来非常的麻烦,这里我们使用别人已经开发好的formidable模块进行编写,它对解析上传文件数据做了很好的抽象。

rush:bash;"> npm install formidable --save-dev

在starter.js中,我们添加上file控件:

rush:js;"> // starter.js function start(request,response){ var html = '\ \ \ \ \
file :

\

\
\ \ ';

response.writeHead(200,{"Content-Type":"text/html"});
response.write( html );
response.end();
}
exports.start = start;

4.1 图片上传

首先我们进行的是图片上传操作,首先我们要确保当前目录中存在tmp和img目录。

在 uploader.js 中:

rush:js;"> // uploader.js var formidable = require('formidable'),util = require('util'),fs = require('fs');

function upload(request,response){
if( request.method.toLowerCase()=='post' ){
var form = new formidable.IncomingForm();

form.uploadDir = './tmp/';
form.parse(request,function(err,fields,files) {
var oldname = files.upload.name,newname = Date.Now() + oldname.substr(oldname.lastIndexOf('.'));
fs.renameSync(files.upload.path,"./img/"+newname ); // 上传到 img 目录

response.writeHead(200,{'content-type': 'text/plain'});
response.write('received upload:\n\n');
response.end(util.inspect({fields: fields,files: files}));
});
return;
}
}
exports.upload = upload;

我们上传图片跳转到upload路径,然后显示出相应的信息:

rush:js;"> received upload:

{
fields: { // 其他控件,如input,textarea等
submit: 'submit'
},files:{ // file控件
upload:{
domain: null,_events: {},_maxListeners: undefined,size: 5097,path: 'tmp\upload_b1f7c3e83af224e9f3a020958cde5dcd',name: 'chrome.png',type: 'image/png',lastModifiedDate: Thu Jan 12 2017 23:09:50 GMT+0800 (中国标准时间),_writeStream: [Object]
}
}
}

我们再查看img目录时,就会发现我们刚才上传的照片了。

4.2 图片显示

图片上传到服务器后,怎样才能把图片显示在浏览器上呢。这里我们就使用到了fs模块来读取文件,创建一个shower.js来专门展示图片

rush:js;"> // shower.js var fs = require('fs'),url = require('url');

function show(request,response){
var query = url.parse(request.url,true).query,imgurl = query.src;

// 读取图片并进行输出
// 这里读取链接中的src参数,指定读取哪张图片 /show?src=1484234660592.png
fs.readFile('./img/'+imgurl,"binary",file){
if(err) throw err;
response.writeHead(200,{"Content-Type": "image/png"});
response.write(file,"binary");
response.end();
})
}
exports.show = show;

然后在 server.js 中添加上 show 的路由映射:

rush:js;"> var routeurl = { '/' : starter.start,'/upload' : uploader.upload,'/show' : shower.show // 添加 };

最后在 upload.js 中进行图片的引用:

rush:js;"> form.parse(request,"./img/"+newname ); // 同步上传图片

response.writeHead(200,{'content-type': 'text/html'});
var s = '

back

'; // 显示刚才的图片
response.write(s);
response.end();
});

五、综合

刚才学习了上传数据和上传图片,这里我们将其综合一下,拟定一个题目:“设定用户名密码,并上传头像”。希望可以自己实现一下。

六、接口的实现

在第2部分学习了GET和POST请求,那么在这里一个简单json或jsonp接口应该不是什么难事儿了吧。

创建一个 inter.js :

rush:js;"> // inter.js var url = require('url');

function init(request,response){
if( request.method.toLowerCase()=='get' ){
var query = url.parse(request.url,true).query;

var data = {"code":0,"msg":"success","data":[{"username":"wenzi","age":26},{"username":"bing","age":25}]};
if( query && query.callback ){
// jsonp
response.end( query.callback + '(' + JSON.stringify(data) + ')' );
}else{
// json
response.end( JSON.stringify(data) );
}
}
}
exports.init = init;

在server中添加inter的引用和路由映射:

rush:js;"> var routeurl = { '/' : starter.start,'/show' : shower.show,'/inter' : inter.init // 添加 };

然后对 http://127.0.0.1:3000/inter 进行json请求或jsonp请求即可。

为大家提供一个查询API的中文地址:

https://pinggod.gitbooks.io/nodejs-doc-in-chinese/content

总结

好了,以上就是这篇文章的全部内容了,这节还是写了不少的内容,最核心的就是讲解如何搭建一个简单的http服务器,进行数据和图片的提交与处理,在最后稍微讲了下接口的编写,后面有机会的话,会再具体讲解下接口的编写。下面会和大家分享node之文件操作,请大家继续关注编程之家。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐