var express = require('express');

var app = express();

app.listen(3000);

var _rootDir = __dirname;

var protectDir = _rootDir + '/protect/';

app.use(express.static(_rootDir));

//注册路由

app.get('/', function(req, res){

res.sendFile(_rootDir+'/src/index.html');

});

app.use(function(req, res, next) {

res.status(404).sendFile(_rootDir+'/src/404.html');

});

app.use(function(err, req, res, next) {

console.error(err.stack);

res.status(500).send('500 Error');

});

上述代码实现了这几个功能,首先创建了http服务器,监听在3000端口。

然后app.use(express.static(_rootDir));这一行是使用了静态文件服务的中间件,这样我们项目下的js、css以及图片等静态文件就都可以访问到了。

接下来是注册路由,此处只匹配一个路由规则,那就是"/"(网站的根目录),当匹配到此路由后把首页文件index.html直接用res.sendFile方法给发送到浏览器端。这样浏览器用http://127.0.0.1:3001就可以访问到index.html了。网站的其他页面也可以通过配置类似的路由进行返回。express还支持配置模板引擎,默认支持ejs,你也可以自己配置其他的比如handlebars。

但是在本项目中,我们用的是angular的前端模板,所以后端就不需要模板了,没有进行配置。我们的路由机制也是完全使用的ng的前端路由,所以在express中只配置一条就够了。

在最后还有两块代码,分别是404和500错误的捕获。你可能会疑惑为什么是这样写呢?从上到下排下来就能分别捕获404和500了吗?其实这就是express的中间件机制,在此机制下,对客户端请求的处理像是一个流水线,把所有中间件串联起来,只要某个中间件把请求返回了,就结束执行,否则就从上到下一直处理此请求。

上面代码的流程就是,先按路由规则来匹配路径,如果路由匹配不到,则认为是发生404。500的错误请注意一个细节,在回调函数的参数中,第一个会传入err,就是错误对象,以此来标记是一个500错误。

理解中间件

express的核心是中间件机制,通过使用各种中间件,能够实现灵活的组装我们所需的功能。中间件是在管道中执行的,所谓管道就是像流水线一样,每到达一个加工区,相应的中间件就可以处理request和response对象,处理完后再送往下一个加工区。如果某个加工区把请求终结了,比如调用send方法返回给了客户端,那么处理就终止了。大部分情况下,都有现成的中间件供我们使用,比如用body-parser解析请求实体,用路由(路由也是一种中间件)来正确的派发请求。

比如我们在server.js中添加如下的代码:

app.use(function(req, res, next){

console.log('中间件1');

next();

});

app.use(function(req, res, next){

console.log('中间件2');

});

我们添加了两个中间件,请求过来之后会先被第一个捕获,然后进行处理,输出“中间件1”。后面接着执行了next()方法,就会进入下一个中间件。一个中间件执行后只有两种选择,要么用next指向下一个中间件,要么将请求返回。如果什么都不做,请求将会被挂起,也就是说浏览器端将得不到返回,一直处于pendding状态。例如上面的中间件2,将会造成请求挂起,这是应该杜绝的。

路由设计

运行起了服务器,了解了中间件编程方式,接下来我们就该为前端提供api了。比如前端post一个请求到/api/submitQuestion来提交一份数据,我们该如何接收请求并做出处理呢,这就是路由的设计了。

给app.use的第一个参数传入路径可以匹配到对应的请求,例如:

app.use('/api/submitQuestion', function(){})

这样就可以捕获到刚刚的提交试题的请求,在第二个参数中可以进行相应的处理,比如把数据插入到数据库。

但是,要注意了,express路由的正确使用姿势并不是这样的。app.use是用来匹配中间件的路径的,而不是请求的路径。因为路由也是一种中间件,所以这样的用法也是能够完成功能的,但是我们还是应该按照官方标准的写法来写。

标准的写法是什么样子呢?代码如下:

var apiRouter = express.Router();

apiRouter.post('/submitQuestion', questionController.save);

app.use('/api', apiRouter);

我们利用的是express.Router这个对象,它同样有use、post、get等方法,用来匹配请求路径。然后我们再使用app.use把apiRouter作为第二个参数传进去。

要注意的是apiRouter.post和app.use的第一个参数。app.use匹配的是请求的“根路径”,这样可以把请求分为不同的类别,比如所有的异步接口我们都叫api,那么这类请求我们就都应该挂在“/api”下。按照这样的规则,我们整个项目的路由规则如下:

//注册路由

app.get('/', function(req, res){

res.sendFile(_rootDir+'/src/index.html');

});

var apiRouter = express.Router();

apiRouter.post('/getQuestion', questionController.getQuestion);

apiRouter.post('/getQuestions', questionController.getQuestions);

apiRouter.post('/submitQuestion', questionController.save);

apiRouter.post('/updateQuestion', questionController.update);

apiRouter.post('/removeQuestion', questionController.remove);

apiRouter.post('/getPapers', paperController.getPapers);

apiRouter.post('/getPaper', paperController.getPaper);

apiRouter.post('/getPaperQuestions', paperController.getPaperQuestions);

apiRouter.post('/submitPaper', paperController.save);

apiRouter.post('/updatePaper', paperController.update);

apiRouter.post('/removePaper', paperController.remove);

app.use('/api', apiRouter);

在router的第二个参数中,我们传入了questionController.save这样的方法,这是什么东西呢?怎么有点MVC的味道呢?没错,我们已经能够匹配到路由了,那服务端的业务逻辑以及数据库访问等该如何组织代码呢?

用“MVC”组织代码

用MVC的结构组织代码当然是黄金法则了。express可以用模板引擎来渲染view层,路由机制来组织controller层,但是express并没有明确规定MVC结构应该怎样写,而是把自由选择交给你,自己来组织MVC结构。当然你也可以组织别的形式,比如像Java中的“n层架构”。

在本项目中,我们就以文件夹的形式来简单组织一下。因为我们使用了前端模板,所以后端的view层就不存在了,只有controller和model。看一下项目的目录:

在protect下有两个文件夹controllers和models分别放C和M。我们路由中使用的questionController对象就定义在questionController.js中,来看一下用于保存试题的save方法是如何定义的:

var Question = require('../models/question');

module.exports = {

//添加试题

save: function(req, res){

var data = req.body.question;

Question.save(data, function(err, data){

if(err){

res.send({success: false, error: err});

}

else{

res.send({success: true, data: data});

}

});

}

}

questionController作为一个模块,使用标准的commonjs语法,我们定义了save方法,通过req.body.question,可以拿到前台传过来的数据。在这个模块中,我们require了位于model层的Question模型,没错,它就是用来操作数据库的,调用Question.save方法,这份数据就存入了数据库,然后在回调函数中,我们用res.send将json数据返回给前端。

定义好questionController后,我们就可以在server.js中把它给require进去了,然后就有了之前我们在路由中使用的

apiRouter.post('/submitQuestion', questionController.save);

整个流程就串通起来了。

models文件夹中放的就是模型了,用来管理与数据库的映射和交互,这里使用了mongoose作为数据库的操作工具,model层如何来编写,本篇就不做介绍了,在下一篇中我们再详细讲解。

最后再声明一下,本篇文章的代码是基于一个练习项目QuestionMaker,为了更好理解文章中的叙述,请查看项目的源码:https://github.com/Double-Lv/QuestionMaker

mean技术栈 linux,“MEAN”技术栈开发web应用相关推荐

  1. linux下用c 开发web,用C一步步开发web服务器(2)

    顺着教程1往下走,这个章节我们需要开发支持并发的web服务器,并加入容错处理 首先加入容错处理,建议将socket函数封装在新的wrap_socket.c文件中,并创建他的.h文件,Server端in ...

  2. Linux下用gSOAP开发Web Service服务端和客户端程序(一)

    1.功能说明: 要开发的Web Service功能非常简单,就是一个add函数,将两个参数相加,返回其和. 2.C版本的程序: (1)头文件:SmsWBS.h,注释部分不可少,url部分的IP必须填写 ...

  3. java 开发web页面跳转,javaweb页面跳转

    java动态web页面,JavaWEB入门,javaweb页面跳转,javaweb页面登录 他们之间的联系是什么 8 serverlet 的生命周期及各阶段的作用 9 java web两种跳转方式分别 ...

  4. 全栈必备的技术栈设想

    喔家ArchiSelf 参加今年的SDCC确实挺高兴的,向大师Joe Armstrong 当面求教,与周爱民老师同台,在我们的架构师进阶之路专场有4个七零后的老码农,瞬间没有了孤独感,甚至有一点窃窃之 ...

  5. 分布式(技术栈、关键技术、PaaS平台、资料推荐、相关论文)

    2019独角兽企业重金招聘Python工程师标准>>> 分布式系统架构的冰与火 首先,我们需要阐述一下为什么需要分布式系统,而不是传统的单体架构.也许这对你来说已经不是什么问题了,但 ...

  6. 6大新品重磅发布,华为云全栈云原生技术能力持续创新升级

    本文分享自华为云社区<HDC.Cloud 2021 | 华为云发布重磅新品,全栈云原生技术能力持续创新升级>,原文作者:技术火炬手 . 4月25日,在华为开发者大会(Cloud)2021上 ...

  7. 程序人生:从全栈工程师看技术人生

    开场白:              道德三皇五帝,功名夏后商周.英雄五伯闹春秋,秦汉兴亡过手.              青史几行名姓,北邙无数荒丘. 前人田地后人收,说什么龙争虎斗. 最近国内外都在 ...

  8. linux中的c技术,基于linux下C开发中的几点技术经验总结

    最近一致致力于linux下的C开发,因为老大是某讯出来的.因此,使用的主要技术都是某讯的基本的后台架构思想. 在这段时间,学习到了很多,然后佩服某讯的技术果然很厉害. 因此,自我感觉,从头开发我们这个 ...

  9. 从100场腾讯面试中,抽出来经典面试题,腾讯技术职业等级丨C++后端开发丨Linux服务器开发丨面试经验丨面试总结

    从100场腾讯面试中,抽出来经典面试题,腾讯技术职业等级 视频讲解如下,点击观看: 从100场腾讯面试中,抽出来经典面试题,腾讯技术职业等级丨C++后端开发丨Linux服务器开发丨面试经验丨面试总结丨 ...

最新文章

  1. oracle awr 修改Snapshots设置
  2. Linux课程第六天学习笔记
  3. SQL Server镜像自动生成脚本
  4. keepalived(8)——http、tcp检测
  5. 工程勘察设计收费标准2002修订版_全过程工程咨询收费模式超全解析
  6. 深入理解基本包装类型
  7. vCenter6.0配置一:配置分布式交换机
  8. java 送参数_关于java:如何以编程方式发送带参数的HTTP请求?
  9. white-space 空白处理、强制内容不换行,word-wrap 文本自动换行,text-overflow 文本溢出
  10. 计算机专业工艺流程简述,CNC加工中心编程的工艺流程,新手必读! ! !
  11. SpringMVC 工作原理了解吗?
  12. PCB六层板如何分层最好?
  13. Incapsula专业提供后门特洛伊保护
  14. 一个模仿途牛旅行应用源码
  15. 【经典】思科、华为路由器交换机模拟器大全及教程
  16. ExtJS 表格自定义列-动态widget
  17. 矢量数据光栅数据: 在不同的几何模型之间转换
  18. HTML旅游网页设计制作 DW旅游网站官网滚动网页 DIV旅游风景介绍网页设计与实现
  19. MySQL 错误. Packet for query is too large (*** 4194304). You can change......
  20. CNTK-训练神经网络

热门文章

  1. 将G1内的SIM卡联系人导入到GMAIL的联系人中
  2. join left semi_HIVE--left semi join
  3. Linux多线程实践(1) --线程理论
  4. 求1+2+3+...+n
  5. php做一个计算日期之间天数,PHP计算任意两个日期之间的天数
  6. GIS实战应用案例100篇(三)-基于NDVI指数的绿地信息提取
  7. php while结束循环吗,php while循环退不出是什么有关问题
  8. 前端工程师必备谷歌浏览器F12下的调试知识点
  9. js和css实现手机横竖屏预览思路整理
  10. 一个基于Bmob的OPPO锁屏壁纸小程序,写过之后发现...好像没什么卵用...一脸懵逼