完全基于node的web应用

Posted 明明.如月

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了完全基于node的web应用相关的知识,希望对你有一定的参考价值。

完全基于node的web应用

事实上通常“正确的方式”一般都不简单。

这其中涉及了nodejs中服务端javascript,函数式编程,阻塞与非阻塞,回调,事件,内部和外部模块等问题,但是函数式编程,阻塞与非阻塞,回调,事件这些编程思想是通用的,值得学习。
Node入门

用例

  • 用户可以通过浏览器使用我们的应用。

  • 当用户请求http://domain/start时,可以看到一个欢迎页面,页面上有一个文件上传的表单。

  • 用户可以选择一个图片并提交表单,随后文件将被上传到http://domain/upload,该页面完成上传后会把图片显示在页面上。

模块

  • 提供一个web页面,需要http服务器

  • 需要一个路由处理URL请求,对应到请求处理程序(requet handle)。

  • 路由处理POST数据,把数据封装好后传递给请求处理程序。

  • 最终的请求处理程序。

  • 返回一个对应视图给浏览器。

  • 上传功能。

基本http服务器

  • server.js

    1. var http = require ("http"); 
    2. http.createServer(function (request,response)
    3. response.writeHead(200,{"Content-Type":"text/plain"}); 
    4. response.write("hello world"); 
    5. response.end(); 
    6. }).listen(8888); 

基于事件驱动回调

  • server.js

  1. var http = require("http"); 
  2.  
  3. //这是一个事件回调函数 
  4. function onRequest(request, response)
  5. console.log("Request received."); 
  6. response.writeHead(200, {"Content-Type": "text/plain"}); 
  7. response.write("Hello World"); 
  8. response.end(); 

  9. //一旦由请求事件,onRequest函数被调用 
  10. http.createServer(onRequest).listen(8888); 
  11.  
  12. console.log("Server has started."); 

模块化server

  • server.js

    1. var http = require("http"); 
    2.  
    3. function start()
    4. function onRequest(request, response)
    5. console.log("Request received."); 
    6. response.writeHead(200, {"Content-Type": "text/plain"}); 
    7. response.write("Hello World"); 
    8. response.end(); 

    9.  
    10. http.createServer(onRequest).listen(8888); 
    11. console.log("Server has started."); 

    12. //导出函数 
    13. exports.start = start; 

路由模块,依赖注入到service

  • router.js

    1. function route(pathname)
    2. console.log("About to route a request for " + pathname); 

    3.  
    4. exports.route = route; 
  • server.js

    1. var http = require("http"); 
    2. var url = require("url"); 
    3.  
    4. function start(route)
    5. function onRequest(request, response)
    6. var pathname = url.parse(request.url).pathname; 
    7. console.log("Request for " + pathname + " received."); 
    8.  
    9. route(pathname);//依赖注入 
    10.  
    11. response.writeHead(200, {"Content-Type": "text/plain"}); 
    12. response.write("Hello World"); 
    13. response.end(); 

    14.  
    15. http.createServer(onRequest).listen(8888); 
    16. console.log("Server has started."); 

    17.  
    18. exports.start = start; 
  • index.js

    1.  
    2. var server = require("./server"); 
    3. var router = require("./router"); 
    4.  
    5. server.start(router.route);//依赖注入 

行为驱动处理函数,函数式编程

  • requestHandlers.js

    1. //行为实现 
    2. function start()
    3. console.log("Request handler ‘start‘ was called."); 
    4. return "Hello Start"

    5.  
    6. function upload()
    7. console.log("Request handler ‘upload‘ was called."); 
    8. return "Hello Upload"

    9.  
    10. exports.start = start; 
    11. exports.upload = upload; 
  • index.js

    1. var server = require("./server"); 
    2. var router = require("./router"); 
    3. var requestHandlers = require("./requestHandlers"); 
    4.  
    5. var handle = {}//根据路由执行行为定义 
    6. handle["/"] = requestHandlers.start; 
    7. handle["/start"] = requestHandlers.start; 
    8. handle["/upload"] = requestHandlers.upload; 
    9.  
    10. server.start(router.route, handle); 
  • server.js

    1. var http = require("http"); 
    2. var url = require("url"); 
    3.  
    4. function start(route, handle)
    5. function onRequest(request, response)
    6. var pathname = url.parse(request.url).pathname; 
    7. console.log("Request for " + pathname + " received."); 
    8.  
    9. response.writeHead(200, {"Content-Type": "text/plain"}); 
    10. var content = route(handle, pathname) //路由行为分配 
    11. response.write(content); 
    12. response.end(); 

    13. http.createServer(onRequest).listen(8888); 
    14. console.log("Server has started."); 

    15.  
    16. exports.start = start; 
  • router.js

    1. function route(handle, pathname)
    2. console.log("About to route a request for " + pathname); 
    3. //根据路由分配行为 
    4. if (typeof handle[pathname] === ‘function‘) { 
    5. return handle[pathname](); 
    6. } else
    7. console.log("No request handler found for " + pathname); 
    8. return "404 Not found"


    9.  
    10. exports.route = route; 

阻塞与非阻塞

nodeJS它通过事件轮询(event loop)来实现并行操作,所以尽可能的避免阻塞操作,取而代之,多使用非阻塞操作。
要用非阻塞操作,我们需要使用回调。
回调就是:你继续处理你的事情,我(Node.js线程)先不等你了,我继续去处理你后面的代码,请你提供一个callbackFunction(),等你处理完之后我会去调用该回调函数的,谢谢!

阻塞的请求

  • requestHandlers.js

  1. function start()
  2. console.log("Request handler ‘start‘ was called."); 
  3.  
  4. function sleep(milliSeconds)
  5. var startTime = new Date().getTime(); 
  6. while (new Date().getTime() < startTime + milliSeconds); 

  7. sleep(10000); 
  8. return "Hello Start"

  9.  
  10. function upload()
  11. console.log("Request handler ‘upload‘ was called."); 
  12. return "Hello Upload"

  13. exports.start = start; 
  14. exports.upload = upload; 
  15.  

处理POST请求

  • requestHandlers.js

    1. function start(response)
    2. console.log("Request handler ‘start‘ was called."); 
    3.  
    4. var body = ‘<html>‘
    5. ‘<head>‘
    6. ‘<meta http-equiv="Content-Type" content="text/html; ‘
    7. ‘charset=UTF-8" />‘
    8. ‘</head>‘
    9. ‘<body>‘
    10. ‘<form action="/upload" method="post">‘
    11. ‘<textarea name="text" rows="20" cols="60"></textarea>‘
    12. ‘<input type="submit" value="Submit text" />‘
    13. ‘</form>‘
    14. ‘</body>‘
    15. ‘</html>‘
    16.  
    17. response.writeHead(200, {"Content-Type": "text/html"}); 
    18. response.write(body); 
    19. response.end(); 

    20.  
    21. function upload(response)
    22. console.log("Request handler ‘upload‘ was called."); 
    23. response.writeHead(200, {"Content-Type": "text/plain"}); 
    24. response.write("Hello Upload"); 
    25. response.end(); 

    26.  
    27. exports.start = start; 
    28. exports.upload = upload; 

request监听器

  • server.js

    1. var http = require("http"); 
    2. var url = require("url"); 
    3.  
    4. function start(route, handle)
    5. function onRequest(request, response)
    6. var postData = "";//传输的数据 
    7. var pathname = url.parse(request.url).pathname; 
    8. console.log("Request for " + pathname + " received."); 
    9.  
    10. request.setEncoding("utf8"); 
    11. //监听数据传输 
    12. request.addListener("data", function(postDataChunk)
    13. postData += postDataChunk; 
    14. console.log("Received POST data chunk ‘"
    15. postDataChunk + "‘."); 
    16. }); 
    17. //传输结束一次处理 
    18. request.addListener("end", function()
    19. route(handle, pathname, response, postData); 
    20. }); 
    21.  

    22.  
    23. http.createServer(onRequest).listen(8888); 
    24. console.log("Server has started."); 

    25.  
    26. exports.start = start; 

数据传输给路由

  • router.js

    1. function route(handle, pathname, response, postData)
    2. console.log("About to route a request for " + pathname); 
    3. if (typeof handle[pathname] === ‘function‘) { 
    4. handle[pathname](response, postData); 
    5. } else
    6. console.log("No request handler found for " + pathname); 
    7. response.writeHead(404, {"Content-Type": "text/plain"}); 
    8. response.write("404 Not found"); 
    9. response.end(); 


    10.  
    11. exports.route = route; 

响应请求

  • requestHandlers.js

    1. var querystring = require("querystring"); 
    2.  
    3. function start(response, postData)
    4. console.log("Request handler ‘start‘ was called."); 
    5.  
    6. var body = ‘<html>‘
    7. ‘<head>‘
    8. ‘<meta http-equiv="Content-Type" content="text/html; ‘
    9. ‘charset=UTF-8" />‘
    10. ‘</head>‘
    11. ‘<body>‘
    12. ‘<form action="/upload" method="post">‘
    13. ‘<textarea name="text" rows="20" cols="60"></textarea>‘
    14. ‘<input type="submit" value="Submit text" />‘
    15. ‘</form>‘
    16. ‘</body>‘
    17. ‘</html>‘
    18.  
    19. response.writeHead(200, {"Content-Type": "text/html"}); 
    20. response.write(body); 
    21. response.end(); 

    22. function upload(response, postData)
    23. console.log("Request handler ‘upload‘ was called."); 
    24. response.writeHead(200, {"Content-Type": "text/plain"}); 
    25. response.write("You‘ve sent the text: "
    26. querystring.parse(postData).text); 
    27. response.end(); 

    28.  
    29. exports.start = start; 
    30. exports.upload = upload; 

使用外部模块上传文件

node-formidable模块 安装

npm insatll formidable
npm install formidable -g express -d

node-formidable官方的例子

var formidable = require(‘formidable‘),
    http = require(‘http‘),
    util = require(‘util‘);

http.createServer(function(req, res) {
  if (req.url == ‘/upload‘ && req.method.toLowerCase() == ‘post‘) {
    // parse a file upload
    var form = new formidable.IncomingForm();
    form.parse(req, function(err, fields, files) {
      res.writeHead(200, {‘content-type‘: ‘text/plain‘});
      res.write(‘received upload:\n\n‘);
      res.end(util.inspect({fields: fields, files: files}));
    });
    return;
  }

  // show a file upload form
  res.writeHead(200, {‘content-type‘: ‘text/html‘});
  res.end(
    ‘<form action="/upload" enctype="multipart/form-data" ‘+
    ‘method="post">‘+
    ‘<input type="text" name="title"><br>‘+
    ‘<input type="file" name="upload" multiple="multiple"><br>‘+
    ‘<input type="submit" value="Upload">‘+
    ‘</form>‘
  );
}).listen(8888);

读取图片数据

  • requestHandlers.js

    1. var querystring = require("querystring"), 
    2. fs = require("fs"); 
    3.  
    4. function start(response, postData)
    5. console.log("Request handler ‘start‘ was called."); 
    6.  
    7. var body = ‘<html>‘
    8. ‘<head>‘
    9. ‘<meta http-equiv="Content-Type" ‘
    10. ‘content="text/html; charset=UTF-8" />‘
    11. ‘</head>‘
    12. ‘<body>‘
    13. ‘<form action="/upload" method="post">‘
    14. ‘<textarea name="text" rows="20" cols="60"></textarea>‘
    15. ‘<input type="submit" value="Submit text" />‘
    16. ‘</form>‘
    17. ‘</body>‘
    18. ‘</html>‘
    19.  
    20. response.writeHead(200, {"Content-Type": "text/html"}); 
    21. response.write(body); 
    22. response.end(); 

    23.  
    24. function upload(response, postData)
    25. console.log("Request handler ‘upload‘ was called."); 
    26. response.writeHead(200, {"Content-Type": "text/plain"}); 
    27. response.write("You‘ve sent the text: "
    28. querystring.parse(postData).text); 
    29. response.end(); 

    30.  
    31. function show(response, postData)
    32. console.log("Request handler ‘show‘ was called."); 
    33. fs.readFile("/tmp/test.png", "binary", function(error, file)
    34. if(error) { 
    35. response.writeHead(500, {"Content-Type": "text/plain"}); 
    36. response.write(error + "\n"); 
    37. response.end(); 
    38. } else
    39. response.writeHead(200, {"Content-Type": "image/png"}); 
    40. response.write(file, "binary"); 
    41. response.end(); 

    42. }); 

    43.  
    44. exports.start = start; 
    45. exports.upload = upload; 
    46. exports.show = show; 
  • index.js

    1. var server = require("./server"); 
    2. var router = require("./router"); 
    3. var requestHandlers = require("./requestHandlers"); 
    4.  
    5. var handle = {} 
    6. handle["/"] = requestHandlers.start; 
    7. handle["/start"] = requestHandlers.start; 
    8. handle["/upload"] = requestHandlers.upload; 
    9. handle["/show"] = requestHandlers.show; 
    10.  
    11. server.start(router.route, handle); 

上传图片

  • requestHandlers.js

    1. var querystring = require("querystring"), 
    2. fs = require("fs"), 
    3. formidable = require("formidable"); 
    4.  
    5. function start(response)
    6. console.log("Request handler ‘start‘ was called."); 
    7.  
    8. var body = ‘<html>‘
    9. ‘<head>‘
    10. ‘<meta http-equiv="Content-Type" content="text/html; ‘
    11. ‘charset=UTF-8" />‘
    12. ‘</head>‘
    13. ‘<body>‘
    14. ‘<form action="/upload" enctype="multipart/form-data" ‘
    15. ‘method="post">‘
    16. ‘<input type="file" name="upload" multiple="multiple">‘
    17. ‘<input type="submit" value="Upload file" />‘
    18. ‘</form>‘
    19. ‘</body>‘
    20. ‘</html>‘
    21.  
    22. response.writeHead(200, {"Content-Type": "text/html"}); 
    23. response.write(body); 
    24. response.end(); 

    25.  
    26. function upload(response, request)
    27. console.log("Request handler ‘upload‘ was called."); 
    28.  
    29. var form = new formidable.IncomingForm(); 
    30. console.log("about to parse"); 
    31. form.parse(request, function(error, fields, files)
    32. console.log("parsing done"); 
    33. fs.renameSync(files.upload.path, "/tmp/test.png"); 
    34. response.writeHead(200, {"Content-Type": "text/html"}); 
    35. response.write("received image:<br/>"); 
    36. response.write("<img src=‘/show‘ />"); 
    37. response.end(); 
    38. }); 

    39.  
    40. function show(response)
    41. console.log("Request handler ‘show‘ was called."); 
    42. fs.readFile("/tmp/test.png", "binary", function(error, file)
    43. if(error) { 
    44. response.writeHead(500, {"Content-Type": "text/plain"}); 
    45. response.write(error + "\n"); 
    46. response.end(); 
    47. } else
    48. response.writeHead(200, {"Content-Type": "image/png"}); 
    49. response.write(file, "binary"); 
    50. response.end(); 

    51. }); 

    52.  
    53. exports.start = start; 
    54. exports.upload = upload; 
    55. exports.show = show; 
  • server.js

    1. var http = require("http"); 
    2. var url = require("url"); 
    3.  
    4. function start(route, handle)
    5. function onRequest(request, response)
    6. var pathname = url.parse(request.url).pathname; 
    7. console.log("Request for " + pathname + " received."); 
    8. route(handle, pathname, response, request); 

    9.  
    10. http.createServer(onRequest).listen(8888); 
    11. console.log("Server has started."); 

    12.  
    13. exports.start = start; 

fs模块的问题 文件路径问题

  1. function upload(response,request)
  2. console.log("Request handler ‘upload‘ was called"); 
  3. // response.writeHead(200, { 
  4. // "Content-Type": "text/plain" 
  5. // }); 
  6. // response.write("you are sent: " + querystring.parse(postData).text); 
  7. // response.end(); 
  8. // response.write("hello upload"); 
  9. //return "hello upload"; 
  10. // 
  11. //文件上传 
  12. var form =new formidable.IncomingForm(); 
  13. console.log("start to parse"); 
  14. form.uploadDir = "tem"
  15. form.parse(request,function(error,fields,files)
  16. console.log("parsing done"); 
  17. try
  18. fs.renameSync(files.upload.path,form.uploadDir+"/test.png");//主要修改了这个地方 
  19. }catch(e){ 
  20. console.log(e); 

  21.  
  22. response.writeHead(200,{"Content-Type":"text/html"}); 
  23. response.write("received image:<br/>"); 
  24. response.write("<img src=‘/show‘>"); 
  25. response.end(); 
  26. }) 





以上是关于完全基于node的web应用的主要内容,如果未能解决你的问题,请参考以下文章

创业笔记-Node.js入门之一个完整的基于Node.js的web应用

Node.js 创建第一个应用

你可能不知道的JavaScript代码片段和技巧(下)

你可能不知道的JavaScript代码片段和技巧(上)

Node.js:创建应用+回调函数(阻塞/非阻塞)

node 环境下简单web服务器搭建代码