APNs功能之Node.js和Mysql应用总结
这篇文档主要是总结Node.js和Mysql的学习心得体会。当然也可以看作是此前所写的消息推送服务的续篇。
简单描述下应用背景,我们的应用需要实现苹果的消息推送服务APNs,之前已经实现了iOS客户端配置和功能代码,也实现了推送通知的本地Provider功能代码,具体参考此前的系列总结。好比一个三角形,A点代表iOS移动设备端,B点代表苹果的消息推送服务器,C点代表应用开发者的本地服务器,现在A和B连接好了,B和C也连接好了,就差A和
B建立连接沟通。
这样一来,很明显就是要在本地搭建一个服务器了,可以处理A发送的请求;当然也少不了数据库,用来存储相关数据。
说到这里,不同技术背景的人会有不同的方案,在此我采用了Node.js+Mysql的解决方案。当然其他方案如ASN.NET、JSP、PHP加上SQLServer、Mysql、MongoDB等等就不谈论了。纯粹作为技术学习和新方案的尝试。
Node.js
作为这篇文档的重点内容,Node.js肯定是要优先总结的。
我这种刚学习Node.js的新手,就不敢把此文当做Node.js的入门资料了,只是简单介绍一下,然后直接根据应用需求实现代码。
Node.js实现了服务端Javascript,通过简单快捷的环境搭建,就可以实现一个运行Javascript的服务器。
1、 去官网下载最新的Node.js安装程序。说明一下,我是在Windows32位操作系统上安装,所以下载Windows对应安装程序即可。我的版本为:node-v0.10.24-x86。
2、 安装Node.js。
3、 在开始—>所有程序中,找到Node.js的命令行程序Node.js command prompt并运行
好了,至此,你已经可以开始编程了。就是这么简单快捷。
或许很多人还是很困惑,那在哪里写代码呢,命令行?
在此,我要说一下刚接触时候我的感受:我觉得很郁闷,不知道从何入手。
对于很多新手,大家需要的是把事情说明白一点,而不是玄乎。可以这样理解,Node.js就是一个服务器,用于搭载Javascript、CSS、Html等网页所可以有的一切。但是他本身不集成IDE。再具体点,他的出现,使我们可以不用熟悉的IIS、Apache、Tomcat来部署了,只需要运行Node.js,然后通过一句话“node xxx.js”,加载已经写好的Javascript,就一切OK了。是的,Javascript,那用你熟悉的任何方式和工具去书写吧。Notepad++是我选择的工具。
一定还有疑惑,没关系,我们先来看实例。
下列实例实现一个可以上传图片文件并预览的页面,还有一个向数据库插入一条数据的请求。
先看这个例子NodejsDemo的文件夹结构
说明:index.js是入口,server.js用于启动服务,router.js用于路由请求,requesHandlers.js用于处理路由后的对应请求,mysqlOperation.js用于处理数据库操作,mysqlDBHelper.js用于连接数据库并操作
接下就是具体代码部分了,太多语法我无法一下说清楚,在需要的地方,我会稍加说明,想了解更多,建议去官网查看API说明,毕竟是抛砖引玉哈。
Index.js的代码:
1 var server = require("./server"); 2 3 var router = require("./router"); 4 5 var requestHandlers = require("./requestHandlers"); 6 7 8 9 var handle = {} 10 11 handle["/"] = requestHandlers.start; 12 13 handle["/start"] = requestHandlers.start; 14 15 handle["/upload"] = requestHandlers.upload; 16 17 handle["/show"] = requestHandlers.show; 18 19 handle["/sendMyInfo"] = requestHandlers.sendMyInfo; 20 21 22 23 server.start(router.route, handle);
说明:这是示例的入口,最后一句,调用server中的start方法,并传递两个参数,第一个是router中route函数,第二个是类似字典实例的handle。
server.js的代码:
1 var http = require('http'); 2 3 var url = require('url'); 4 5 function start(route, handle){ 6 7 function onRequest(req,res){ 8 9 var pathname = url.parse(req.url).pathname; 10 11 console.log("Request for " + pathname + " received."); 12 13 14 15 route(handle, pathname, res,req); 16 17 } 18 19 http.createServer(onRequest).listen(1337, 'Your IP'); 20 21 console.log('Server running at http://Your IP:1337/'); 22 23 } 24 25 26 27 exports.start = start;
说明:与require(“./server”)调用同级目录js文件不同,require(‘http’)是生成一个http对象实例的方法。url.parse(string).pathname用于获取url字符串中路径名称。Your IP为本机的IP地址。http.createServer(function).listen(端口号,’IP’)用于通过一个方法创建一个服务,并在设置的iP和端口号监听。Exports.start=start为其他js对象调用本方法暴露接口。
route.js的代码
1 function route(handle, pathname, res, req){ 2 3 console.log("About to route a request for " + pathname); 4 5 if(typeof handle[pathname] === 'function'){ 6 7 handle[pathname](res, req); 8 9 }else{ 10 11 console.log("No request handler found for " + pathname); 12 13 res.writeHead(404, {'Content-Type': 'text/plain'}); 14 15 res.end('404 route not found'); 16 17 } 18 19 } 20 21 22 23 exports.route = route;
说明:路由请求,当请求未定义的路径时,则在response的head里面返回404错误。
requestHanlders.js的代码:
1 var querystring = require ("querystring"); 2 3 var url = require('url'); 4 5 var fs = require("fs"); 6 7 var formidable = require("formidable"); 8 9 var util = require("util"); 10 11 var mysqlOperation = require("./mysqlOperation"); 12 13 var testPicPath = "/testPic/test.png"; 14 15 function start(res, req){ 16 17 console.log("Request handler 'start' was called."); 18 19 var body = '<html>'+ 20 21 '<head>'+ 22 23 '<meta http-equiv="Content-Type" content="text/html; '+ 24 25 'charset=UTF-8" />'+ 26 27 '</head>'+ 28 29 '<body>'+ 30 31 '<form action="/upload" enctype="multipart/form-data" method="post">'+ 32 33 '<input type="file" name="upload">'+ 34 35 '<input type="submit" value="Upload file" />'+ 36 37 '</form>'+ 38 39 '</body>'+ 40 41 '</html>'; 42 43 res.writeHead(200, {'Content-Type': 'text/html'}); 44 45 res.end(body); 46 47 } 48 49 50 51 function upload(res, req){ 52 53 console.log("Request handler 'upload' was called."); 54 55 56 57 var form = new formidable.IncomingForm(); 58 59 60 61 console.log("about to parse"); 62 63 64 65 form.parse (req, function(error, fields, files) { 66 67 console.log("parsing done"); 68 69 /* res.writeHead(200,{"Content-type":"text/plain"}); 70 71 res.write("received upload: "); 72 73 res.write(util.inspect({fields:fields,files:files})); */ 74 75 76 77 fs.rename(files.upload.path, '/testPic/' + files.upload.name, function(err){ 78 79 if(err) throw err; 80 81 console.log("renamed complete"); 82 83 testPicPath = "/testPic/" + files.upload.name; 84 85 86 87 res.writeHead (200, {"Content-Type": "text/html"}); 88 89 90 91 res.write ("received image:<br/>"); 92 93 94 95 res.write ("<img src= '/show' />"); 96 97 98 99 res.end(); 100 101 }); 102 103 }); 104 105 } 106 107 108 109 function show(res, req) { 110 111 console.log("Request handler 'show' was called."); 112 113 fs.readFile (testPicPath, "binary", function(error, file) { 114 115 if(error) { 116 117 res.writeHead (500, {"Content-Type": "text/plain"}); 118 119 res.write ("Image error: " + error + " "); 120 121 res.end(); 122 123 } else { 124 125 res.writeHead (200, {"Content-Type": "image/png"}); 126 127 res.write (file, "binary"); 128 129 res.end(); 130 131 } 132 133 }); 134 135 } 136 137 138 139 function sendMyInfo(res,req){ 140 141 console.log("Request handler 'sendMyInfo' was called."); 142 143 var urlstring = url.parse(req.url).query; 144 145 var user = { 146 147 name : querystring.parse(urlstring).name, 148 149 country : querystring.parse(urlstring).country, 150 151 city : querystring.parse(urlstring).city, 152 153 street : querystring.parse(urlstring).street 154 155 }; 156 157 158 159 if(use.name == undefined || user.country == undefined 160 161 || user.city == undefined || user.street == undefined){ 162 163 res.writeHead (500, {"Content-Type": "text/plain"}); 164 165 res.write ("error: paras undefined."); 166 167 res.end(); 168 169 console.log("error: paras undefined."); 170 171 return; 172 173 } 174 175 176 177 console.log(user); 178 179 mysqlOperation.storeUserInfo(user,res); 180 181 } 182 183 184 185 exports.start = start; 186 187 exports.upload = upload; 188 189 exports.show = show; 190 191 exports.sendMyInfo = sendMyInfo;
说明:这个js文件是处理请求的主要地方,当路由路径后,会找到相应方法。
这里需要重点说一下start方法。在返回response内容时候,直接采用字符串方式表达body,当然这是不好的习惯,那么在疑惑页面的朋友就可以在这里找到答案了。Node.js有负责加载页面的API,还有类似于模板页的结构,以前熟悉的方式依然可以使用。至于其他方法,相信你会看明白的。还有,为什么错误返回码总是500呢,因为是我随意设置的。。。
至此,一个简单的Node.js的demo就完成了,噢,忘了方法sendMyInfo,那是等下继续要说的。马上运行Node.js的命令行程序,然后在“路径>”后面输入“node index.js路径”,回车就可以看到Server running的输出提示了。在浏览器里面访问“Your IP:Your port/”。
多说一句,index.js的全路径是绝对无误的,当index.js处于用户根目录时,可以直接输入“node index.js”;当index.js处于node.js的安装磁盘根目录时候,可以直接输入“node /index.js”。
在方法sendMyInfo中,我试图向数据库中插入一条记录。那就接着进入node.js的数据库连接和操作吧。
Node.js的数据库操作之创建数据库
我们可以先在Mysql数据库里面创建好一个新的database和一个用户表users。也可以通过Node.js来创建,代码如下:
1 var mysql = require('mysql'); 2 3 var db_options = { 4 5 host: "localhost", 6 7 port: 3306, 8 9 user: "root", 10 11 password: "Your password" 12 13 }; 14 15 var client = new mysql.createConnection(db_options); 16 17 18 19 //要创建的数据库名 20 21 var TEST_DATABASE = 'NodeJsTest', 22 23 //要创建的表名 24 25 TEST_TABLE = Users; 26 27 28 29 client.query('show databases', function(err, rows, fields){ 30 31 if(err){ 32 33 console.log(err); 34 35 }else{ 36 37 console.log(rows); 38 39 } 40 41 }); 42 43 44 45 client.query('CREATE DATABASE '+TEST_DATABASE, function(err) { 46 47 if (err && err.number != client.ERROR_DB_CREATE_EXISTS) { 48 49 throw err; 50 51 } 52 53 }); 54 55 56 57 // If no callback is provided, any errors will be emitted as `'error'` 58 59 // events by the client 60 61 client.query('USE '+TEST_DATABASE); 62 63 client.query( 64 65 'CREATE TABLE '+TEST_TABLE+ 66 67 ‘name VARCHAR(255), '+ 68 69 ' country VARCHAR(255),'+ 70 71 ' city VARCHAR(255), '+ 72 73 ' street VARCHAR(255),' 74 75 ); 76 77 78 79 client.query( 80 81 'INSERT INTO '+TEST_TABLE+' '+ 82 83 'SET name = ?, country = ?, city = ?, street = ?', 84 85 ['Li Lei', 'China', 'Bei jing', ‘ ’] 86 87 ); 88 89 90 91 client.query('USE '+TEST_DATABASE); 92 93 client.query( 94 95 'SELECT * FROM '+TEST_TABLE, 96 97 function selectCb(err, results, fields) { 98 99 if (err) { 100 101 throw err; 102 103 } 104 105 106 107 console.log(results); 108 109 console.log(results[0].name); 110 111 console.log(results.length); 112 113 console.log(fields); 114 115 } 116 117 ); 118 119 120 121 client.end();
说明:Your password为你的Mysql数据库密码。好了,可以将代码所在的js文件直接加载到命令行中,方法还是“node xxxx.js”。如果你之前已经在运行index.js了,那么按下crtrl+C停下之前的服务。我想运行结果会成功的,除非node.js的API又升级了,这个时候就去查官网API吧。
Node.js的数据库操作之插入数据
1 var mysql = require('mysql'); 2 3 var db_options = { 4 5 host: "localhost", 6 7 port: 3306, 8 9 user: "root", 10 11 password: "Your password" 12 13 }; 14 15 16 17 //database name 18 19 var DATABASE = ‘NodeJsTest’, 20 21 //table name 22 23 TABLE = 'Users'; 24 25 function storeUserInfo(user, res){ 26 27 if(user.name == " "){ 28 29 res.writeHead (600, {"Content-Type": "text/plain"}); 30 31 res.write ("error: name can not be empty. "); 32 33 res.end(); 34 35 return; 36 37 } 38 39 var client = new mysql.createConnection(db_options); 40 41 client.query('USE '+DATABASE); 42 43 44 45 client.query( 46 47 "SELECT * FROM "+ TABLE + " WHERE name = ?", 48 49 [user.name], 50 51 function(err, results, fields) { 52 53 if (err) { 54 55 client.end(); 56 57 res.writeHead (700, {"Content-Type": "text/plain"}); 58 59 res.write ("error: " + err); 60 61 res.end(); 62 63 return; 64 65 } 66 67 68 69 console.log(results.length); 70 71 if(results.length < 1){ 72 73 //Added new record 74 75 client.query( 76 77 "INSERT INTO "+ TABLE + " SET name = ?, country = ?, city = ?, street = ? ", [user.name, user.country, user.city, user.street], 78 79 function(err, results, fields) { 80 81 if (err) { 82 83 client.end(); 84 85 res.writeHead (700, {"Content-Type": "text/plain"}); 86 87 res.write ("error: " + err); 88 89 res.end(); 90 91 return; 92 93 } 94 95 96 97 client.end(); 98 99 res.writeHead (200, {"Content-Type": "text/plain"}); 100 101 res.write ("added new user"); 102 103 res.end(); 104 105 console.log(results); 106 107 console.log(fields); 108 109 } 110 111 ); 112 113 }else if(results.length == 1){ 114 115 //Update record 116 117 client.query( 118 119 "UPDATE " + TABLE + " SET name = ?, country = ?, city = ?, street = ? WHERE name = ?", [user.name], 120 121 function(err, results, fields) { 122 123 if (err) { 124 125 client.end(); 126 127 res.writeHead (700, {"Content-Type": "text/plain"}); 128 129 res.write ("error: " + err); 130 131 res.end(); 132 133 return; 134 135 } 136 137 138 139 client.end(); 140 141 res.writeHead (200, {"Content-Type": "text/plain"}); 142 143 res.write ("updated"); 144 145 res.end(); 146 147 console.log(results); 148 149 console.log(fields); 150 151 } 152 153 ) 154 155 }else{ 156 157 client.end(); 158 159 res.writeHead (700, {"Content-Type": "text/plain"}); 160 161 res.write ("error: Repeat records exist!"); 162 163 res.end(); 164 165 console.log("Repeat records exist!"); 166 167 //handle 168 169 } 170 171 } 172 173 ); 174 175 } 176 177 178 179 exports.storeUserInfo = storeUserInfo;
说明:这个js实现向数据库里面插入一条记录,如果已存在该用户,则更新用户信息。就是这样的熟悉味道。
忘了一点,我没有封装这个demo的数据库连接,因为当操作多了以后,还是封装一个负责数据库连接操作的DBHelper比较好。
到此,Node.js的总结就结束了,别忘了重新node index.js后,在浏览器里面访问“Your IP : Your port/sendMyInfo?name=xxx&country=xxx&city=xxx&street=xxx”。