static写完了,下面来继续写api。api需要根据请求的URL来响应对应的内容。例如客户端请求“/”,就响应它网站的主页,请求“/detail?id=0”就响应它id为0的食品的详情页面。如果客户端请求了不存在的URL,则给回一个404响应,表示没有找到。代码如下所示。这里我分了两个handler,本项目没有post操作,所以只有getHandler会使用到。给出postHanlder的目的是为了简单说明如何写处理客户端post请求的路由。
以getHanlder[‘/']为例,当客户端请求“/”的时候,不是简单地把index.html响应给服务器这么简单,想象一下,一家食品店,每天提供的菜式可能会有所不同,或者因为季节问题而导致每个季节的特色菜都有所不同,所以我们网站主页展示的菜式也可能随之而变化。因此,我们需要根据数据库中存储的主页数据来动态渲染主页的内容。我把idnex.html写成模板,为了不适用jade等模板引擎,我在html里面使用如同“{{foodMenu}}”这种形式的标记,当读取完模板之后,利用简单的字符串操作将标记替换成我们需要动态渲染的内容,即可实现动态渲染HTML的目的。
静态文件之外的其他路由,或者叫控制器(controller),一般都会包含业务逻辑,即业务逻辑一般是在这一层完成的。像上面的根据数据库内容动态渲染出首页,或者你在其他场景下面会见到的如登录注册的数据检验,成功登录之后将客户端重定向到对应的用户界面等等业务逻辑都是在这一层实现。
var fs = require('fs'); var url = require('url'); var querystring = require('querystring'); var foods = require('../model/foods')(); var detail = require('../model/detail')(); var getHandler = {}; var postHandler = {}; // 处理对主页的请求 getHandler['https://www.jb51.net/'] = function(req, res) { var foodMenu = ""; // 拼装首页数据 var food = foods.getAllFoods(); for (var i = 0; i < food.length; ++i) { foodMenu += '<div><img src="'; foodMenu += food[i].image + '"><h1>' + food[i].name + '</h1><h2>' + food[i].price + '</h2></div>'; } res.writeHead(200, {"Content-Type": "text/html"}); fs.readFile(__dirname + '/../views/index.html', (err, data) => { if (err) { console.log(err); res.end(); } else { // 动态渲染模板 res.end(data.toString().replace('{{foodMenu}}', foodMenu)); } }); }; // 处理对详情页面的请求 getHandler['/detail'] = function(req, res) { var query = querystring.parse(url.parse(req.url).query); var foodDetail = detail.getDetail(query.id); res.writeHead(200, {"Content-Type": "text/html"}); fs.readFile(__dirname + '/../views/detail.html', (err, data) => { // 动态渲染模板 res.end(data.toString().replace('{{image}}', foodDetail.image) .replace('{{name}}', foodDetail.name) .replace('{{description}}', foodDetail.description) .replace('{{price}}', foodDetail.price)); }); }; // 404响应,告知客户端资源未找到 getHandler['/404'] = function(req, res) { res.writeHead(404, {"Content-Type": "text/plain"}); res.end("404 Not Found"); }; // post请求的处理方法示例 postHandler['https://www.jb51.net/'] = function(res, data) { // do something }; // get请求 function get(req, res) { var reqUrl = url.parse(req.url); if (typeof getHandler[reqUrl.pathname] === "function") { getHandler[reqUrl.pathname](req, res); } else { getHandler["/404"](req, res); } } // post请求(示例) function post(req, res) { var reqUrl = url.parse(req.url); if (typeof postHandler[reqUrl.pathname] === "function") { var postData = ""; req.on('data', (data) => { postData += data; }); req.on('end', () => { postData = querystring.parse(postData); postHandler[reqUrl.pathname](res, postData); }); } else { getHandler["/404"](req, res); } } // 提供给其他模块使用的接口 module.exports = { get: get, post: post };
最后,讲一下post方法的处理过程,虽然本项目中没有使用到post。post方法跟get方法最主要的不同之处在于post方法除了发送http头部信息之外还带有客户端提交的数据。在接收到post请求的时候,需要将数据读取出来,读取数据的方式也挺简单,只要给request设置监听器就行了。当request对象收到数据的时候会触发“data”事件,因此,给这个事件设置监听器,让它收到数据的时候就把数据保存起来。在接收完一个请求全部的post数据之后会触发“end”事件,因此,给这个事件设置监听器,使得在接收完全部数据之后才开始对提交的数据进行相关的操作。
编写数据模型