lambda表达式的出现使得JDK8内部发生了很多有趣的变化, 其中就包括惰性计算的特性.
使用REST API操作MongoDB(一)
使用REST API, 我们能够通过HTTP请求来对MongoDB进行CRUD操作. REST API对于我们的应用来说就是一个无状态的接口, 对于MEAN技术栈来讲, REST API的地位是创建一个暴露一个操作数据库的无状态接口, 供其他应用操作数据库的数据.
设计REST API的请求处理部分
REST API功能
通常, 我们希望借助REST API完成以下操作
- 创建一个新记录
- 读取一个list的记录
- 读取指定的记录
- 更新指定记录
- 删除指定记录
假如我们现在要创建的是地址记录, 假定我们要使用的URL路径是/locations
, 那么各个操作对应到URL
路径之后如下表所示
动作 | URL路径 | 路径参数 | 例子 |
---|---|---|---|
创建一个新记录 | /locations | http://example/api/locations | |
读取一个list的记录 | /locations | http://example/api/locations | |
读取指定的记录 | /locations | locationId | http://example/api/locations/123 |
更新指定记录 | /locations | locationId | http://example/api/locations/123 |
删除指定记录 | /locations | locationId | http://example/api/locations/123 |
通过上表可以看出, 多个不同的动作可能对应的是相同的URL路径, 那么如何进行动作的区分呢? 答案是通过HTTP请求类型来判断.
HTTP请求类型
通常, REST API使用到四种HTTP请求, 它们的用途和相应如下所示
HTTP请求类型 | 用途 | 响应 |
---|---|---|
POST | 创建新记录 | 数据库中插入新记录 |
GET | 读取记录 | 从数据库返回指定记录 |
PUT | 更新记录 | 更新数据库中的指定记录 |
DELETE | 删除记录 | 数据库中指定记录被删除 |
结合HTTP请求类型和URL路径确定唯一的操作
动作 | HTTP请求类型 | URL路径 | 路径参数 | 例子 |
---|---|---|---|---|
创建一个新记录 | POST | /locations | http://example/api/locations | |
读取一个list的记录 | GET | /locations | http://example/api/locations | |
读取指定的记录 | GET | /locations | locationId | http://example/api/locations/123 |
更新指定记录 | PUT | /locations | locationId | http://example/api/locations/123 |
删除指定记录 | DELETE | /locations | locationId | http://example/api/locations/123 |
使用REST API操作子文档
以上介绍都是REST API操作父文档, 如果想操作子文档, 首先需要获取到父文档, 也就是如上表中的http://example/api/locations/123这样的路径, 现在假定每个location
文档中还内嵌了名为reviews
的子文档, 那么它所对应的操作如下
动作 | HTTP请求类型 | URL路径 | 路径参数 | 例子 |
---|---|---|---|---|
创建一个新记录 | POST | /locations/locationId/reviews | locationId | http://example/api/locations/123/reviews |
读取指定的记录 | GET | /locations/locationId/reviews | locationId reviewId |
http://example/api/locations/123/reviews/abc |
更新指定记录 | PUT | /locations/locationId/reviews | locationId reviewId |
http://example/api/locations/123/reviews/abc |
删除指定记录 | DELETE | /locations/locationId/reviews | locationId reviewId |
http://example/api/locations/123/reviews/abc |
注意, 子文档的操作并没有一个读取list的操作, 因为这个操作可以通过对父文档操作进行实现.
设计REST API响应(Response)和状态码
REST API的另一部分就是响应的设计, 响应一般来说包含两个部分:
- 返回数据
- HTTP状态码
对于返回数据, 通常是JSON或者XML类型的, 这里我们选择JSON类型, 因为它比XML数据更加紧凑, 并且天然适应MEAN技术栈. 对于每个请求, 返回数据都应该有三种类型:
- 包含正确返回的被请求数据的JSON对象
- 包含错误信息的JSON对象
- null响应
常用的HTTP状态码
HTTP状态码通常是用来和响应一同返回的, 用于表明HTTP请求的执行情况.
常用的HTTP状态码共有10种
状态码 | 名称 | 适用场景 |
---|---|---|
200 | OK | GET 或者PUT 请求成功 |
201 | Created | POST 请求成功 |
204 | No content | DELETE 请求成功 |
400 | Bad request | GET , PUT 或者POST 请求由于内容不符合标准而失败 |
401 | Unauthorized | 身份验证未通过 |
403 | Forbidden | 不允许的请求 |
404 | Not found | 请求的URL没有资源或者参数错误 |
405 | Method not allowed | 指定的URL不允许此种请求类型 |
409 | Conflict | POST 失败, 试图插入重复数据 |
500 | Internal server error | 服务器或数据库故障 |
在Express中建立响应API
使REST API不与应用的其他处理逻辑混杂, 所以这里我们单独对其进行管理.
首先, 在应用的根目录新建一个app_api
目录, 这个目录将会包含routes
, controllers
, models
(注意并没有views
)
创建REST API路由
首先创建根路由, 也就是index.js
, 并将其加载到app.js
中,
|
|
TODO 未完待续
Angular基础(二)--第一个Angular程序
添加Angular到现有的web程序中的方法
步骤1: 下载Angular的lib
到官网上下载即可, 下载的文件一般叫做angular.min.js
步骤2: 创建一个Angular模块
创建一个js文件, 然后进行angular模块的创建, controller的创建等一系列操作. 例如, 我们想创建一个叫做”loc8rApp”的Angular模块.
|
|
步骤3: 关联Angular模块到html文件
|
|
经过以上配置, 一个简单的Angular程序就完成了, 我们可以通过在浏览器中打开网页查看效果:
随着我们的输入, 视图也会随之改变.
使用ng-repeat遍历数组元素
我们可以如下定义一个控制器:
|
|
然后在html中这样遍历(注意, ng-repeat
要绑定在需要重复的元素上, 在下例中是li
元素)
|
|
在浏览器中查看是如下效果:
Angular filter
Angular基础(一)
Angular是客户端的框架, 让我们能够在浏览器中构建整个应用程序.
我们能够使用Angular来与REST API进行交互.
Angular是一个MVW模式的框架, W的意思是”Whatever works for you”. 意思就是它能够作为controller, view-model, services的任意一种使用.
双向数据绑定(two-way data binding)
双向数据绑定指的是model和view的绑定, 它有以下特征
视图的改变会更新模型, 模型的改变也会更新视图, 一切都是发生在浏览器中的
Angular Scope
Angular有一个rootScope
, 也就是ng-app
定义的scope, 子scope是由ng-controller
创建的.
在Angular中, scope是与view, model和controller绑定的, 它们使用的是相同的scope.
Oracle到MySQL数据库迁移之--主键生成策略替换
Oracle数据库到MySQL数据库迁移过程中的一大难题就是主键生成策略的替换. 如果之前的程序中使用Oracle的Sequence
机制来实现主键的自增的话. MySQL中需要使用TableGenerator
进行等价替换.
替换的时候, 主要有三个地方需要修改:
- 以注解方式完成hibernate映射的实体;
- 以xml方式完成hibernate映射的实体;
- 数据库存储过程;
注解方式完成hibernate映射的实体的修改
使用Oracle Sequence
假如你之前的程序使用的是Sequence
, 这里以一个名为SEQ
的Sequence
为例, 那么你操作id
字段的代码应该长的是下面这个样子:
|
|
使用MySQL表模拟Oracle Sequence
首先你需要在MySQL中建立一个表sys_sequence
, 表中有两个字段, 一个是seq_name
, 代表Oracle序列的名称, 另一个是current_value
, 代表该序列的当前值(注意: 需要将此初始值设定为Oracle数据库中对应序列的当前值.). 表的样子如下:
|
|
. 然后在程序中如下编写:
|
|
xml方式完成hibernate映射的实体的修改
使用Oracle Sequence
之前使用Sequence
作为主键生成策略的时候, xml映射文件片段如下:
|
|
使用MySQL表模拟Oracle Sequence
表的结构与前面注解方式所用的表结构相同. 这次我们在xml中使用的generator是org.hibernate.id.enhanced.TableGenerator
:
|
|
存储过程的修改
存储过程中, 主要涉及到Sequence
的nextval()
等方法的替换, 我们同样可以在MySQL中进行模拟.
使用Oracle Sequence
假设我们现在有一个Sequence
名为SEQ
, 那么我们通常在存储过程中使用如下函数获得SEQ
的下一个值:
|
|
使用MySQL模拟Oracle Sequence
首先需要创建一个表格, 用于存储所有序列的名称, 当前值以及递增步长. 这里我们继续沿用前面所述的sys_sequence
表, 不过还需要为表新增一个字段increment_by
, 我们对照Oracle数据库的设置手动进行increment_by
初值的设定.
|
|
然后在数据库中新增两个函数, 一个是currval
, 用于获取模拟的Sequence
的当前值:
|
|
另一个是nextval
, 用户获取模拟的Sequence
的下一个值:
|
|
之后在需要使用Sequence
的地方, 使用如下语句替代即可:
|
|
小结
如此一来, 就达到了利用MySQL的表来模拟Oracle的Sequence的目的. 以后每当有需要替换的序列, 都在前面建立的sys_sequence
中新增一行即可.
Heroku连接云端MongoDB的方法
之前我们已经简单的在Heroku上部署了正在开发的web应用, 现在我们的应用要使用MongoDB数据库, 如何在云端部署一个MongoDB连接呢?
再进一步, 最终我们的目的是生产和开发环境连接不同数据库. 本文将完整叙述整个流程.
注册MongoLab账号
我们这里将使用MongoLab, 首先需要注册账号, 相关文档请见这里
创建一个新数据库
登陆后按照如下操作
选择single node
, sandbox
, 注意, 只有特定的区域才有single node
节点可选, 需要自己手动试一下哪个可行.
创建成功后的效果
如果没有创建成功, 多半是数据库名称冲突了, 多试几个就好.
点击Name
进入数据库, 提示要创建一个用户来使用数据库, 那么我们切换到Users
标签, 点击add database user
配置Mongolab数据库连接字符串到heroku的配置文件中
|
|
同步本地开发数据库的测试数据到Mongolab
创建一个临时文件夹, 用于本地开发数据库的备份
|
|
备份本地开发数据库
|
|
还原数据到云端数据库
|
|
检查数据还原情况
首先使用mongo shell连接到远程数据库
|
|
列出数据
|
|
让应用根据环境自动选择连接的数据库
经过以上操作, 我们获得了一个和本地开发数据库同步的云端数据库, 由于我们的应用同时在本地开发和Heroku部署, 需要让应用根据环境自动连接到正确的数据库.
设置NODE_ENV环境变量
首先需要将heroku上部署的应用切换到production
环境(注意: 要在应用根目录下执行指令).
|
|
更改应用源码中数据库连接的设置
|
|
本地测试连接到本地开发数据库和Mongolab数据库
首先测试连接到本地开发数据库
|
|
然后测试连接到Mongolab数据库
|
|
测试成功后, 将应用推送到Heroku仓库部署
|
|
通过检查heroku云端应用日志, 验证数据库连接情况
|
|
以上, 就完成了生产和开发环境连接不同数据库的配置.
百度地图静态图API使用指南
本文的目的是根据经纬度获取一张指定大小的静态地图, 用于内嵌在网页中.
步骤1: 申请百度开发者账号
这部没什么好讲的, 和申请百度账号相仿
步骤2: 申请一个密钥
点击API控制台
->开发
->静态图API
点击获取密钥
, 勾选全部权限, 然后设置访问网址为0.0.0.0/0
.
点击API控制台
, 这里可以看见先前生成的密钥
步骤3: 在应用中嵌入图片, url指定为符合静态图API的网址
这里举一个示例网址
http://api.map.baidu.com/staticimage/v2?ak=这里是你自己的ak&mcode=666666¢er=116.403874,39.914888&width=400&height=350&zoom=11&markers=116.403874,39.914888
嵌在程序中会达到以下效果
其中
ak
是刚才的密钥center
是经纬度markers
是地图标注的经纬度
Mongoose+Mongo+Express
Mongoose是在Express应用与MongoDB之间, 负责两者的连接与数据交换. 使用Mongoose我们可以方便的定义数据结构.
Mongoose的地位如下图所示
添加Mongoose到应用中
Mongoose是一个npm模块, 可以像其他模块一样添加到程序中, 并写入依赖
|
|
配置Mongoose到应用范围内
在Express应用范围导入mongoose连接, 应在工程根目录的app.js
中进行引入, 这里由于我们使用的是MVC架构, 所以应该将数据库部分的管理归类在models文件夹中. 我们在目录结构如下
|
|
我们在db.js中添加如下
|
|
然后在app.js
中新增如下配置
|
|
由于我们不需要从db.js
模块暴露方法, 不需要为require
操作指定变量.
使用Mongoose指定连接
连接格式如下
如果我们是连接到本地mongo实例, 那么username
, password
, port
字段都是可以省略的.
继续向db.js
模块添加如下语句, 完成到本地mongo实例的连接
|
|
注: 关于如何启动本地mongo实例, 请见我的文章:MongoDB基础知识
监控mongoose连接事件
现在我们想监控连接成功, 连接失败, 连接断开三个事件, 只需要向db.js
追加如下代码
|
|
关闭Mongoose连接
由于node应用停止时, mongo实例的连接池不会自动关闭, 下面我们将编写代码, 在Node应用退出的时候, 自动关闭mongo连接池.
Node应用退出前, 根据你启动应用的方式不同, 会释放出不同的信号, 如果我们想在任何场景都使Node应用退出时自动关闭连接, 则需要捕获所有的信号类型.
为此, 我们首先要定义一个通用的关闭连接的函数, 在里面完成mongoose
连接的关闭.
|
|
类型1: SIGINT
此种信号产生在使用npm start
指令启动的node应用停止前, 现在我们要在应用捕获到这个信号的时候先执行mongoose
的关闭逻辑.
|
|
类型2: SIGTERM
此种信号产生在Heroku关闭应用进程前, 同样, 我们要在捕获到这个信号的时候先关闭mongoose
连接.
|
|
类型3: SIGUSR2
此种信号产生是用来通知nodemon
进程来重启node应用的, 由于nodemon
进程本身也依赖这个信号来进行node应用的重启逻辑, 所以我们不应该完全截断, 而是应该在捕获信号, 完成关闭mongoose
逻辑之后再手动发出一个同样的信号, 通知nodemon
完成应用的重启. 并且仅捕获此信号一次, 防止第二次手动释放的信号又被应用进程截获.
|
|
Jade模板中变量的使用
形式1: JavaScript行内代码
|
|
形式2: 插值
|
|
注意: 以上两种做法都会将html代码直接转为文本输出(出于安全考虑), 如果想保留原来的html标签输出, 则使用!=
和!{}
的形式即可
形式3: 使用JavaScript代码段
|
|
注意:
- 整体上来看, 与JavaScript语法相当像, 不过现在不是用大括号标识代码块, 而是用缩进来标志.
if
语句不需要用”-“连接
形式4: 使用mixin定义可重用的代码段
无参数的mixin
定义
|
|
调用mixin
的语法为
|
|
下面是带参数的mixin
定义
|
|
调用带参数的mixin
语法
|
|
如果我们想要在其他文件中使用在本文件中定义的mixin
, 通过include
关键字加路径实现. 例如, 现在我们的目录层级如下所示:
|
|
我们想通过location-list.jade
文件来引用_includes/sharedHTMLfunctions.jade
中的mixin, 可以如下使用:
|
|
从零构建部署Node.js+Express+Bootstrap Web应用
本文将包括以下内容:
- 创建一个Express应用
- 使用npm和
package.json
管理应用依赖 - 调整Express工程结构到MVC架构
- Route和Controller概念分离
- 创建新的Node模块(module)
- 使用Git在线部署Express应用到Heroku