restify构建REST服务(转)
restify构建REST服务
从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!
关于作者
- 张丹(Conan), 程序员Java,R,PHP,Javascript
- weibo:@Conan_Z
- blog: http://blog.fens.me
- email: bsspirit@gmail.com
转载请注明出处:
http://blog.fens.me/nodejs-restify/
前言
随着互联网应用的兴起,web2.0时代的到来,越来越多的人,选择用REST编程来代替原来的页面渲染。REST以资源为中心的web服务,分离了展现层和服务层,让前端和后端程序员能更专注于自己擅长的领域。
restify让REST变得如此简单!
目录
- 什么是REST?
- restify介绍
- restify安装
- restify服务端API
- restify客户端API
1. 什么是REST?
REST(Representational State Transfer表述性状态转移)是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。
REST 定义了一组体系架构原则,您可以根据这些原则设计以系统资源为中心的 Web 服务,包括使用不同语言编写的客户端如何通过 HTTP 处理和传输资源状态。 如果考虑使用它的 Web 服务的数量,REST 近年来已经成为最主要的 Web 服务设计模式。 事实上,REST 对 Web 的影响非常大,由于其使用相当方便,已经普遍地取代了基于 SOAP 和 WSDL 的接口设计。
REST中的资源所指的不是数据,而是数据和表现形式的组合,比如“最新访问的10位会员”和“最活跃的10位会员”在数据上可能有重叠或者完全相同,而由于他们的表现形式不同,所以被归为不同的资源,这也就是为什么REST的全名是Representational State Transfer的原因。资源标识符就是URI(Uniform Resource Identifier),不管是图片,Word还是视频文件,甚至只是一种虚拟的服务,也不管你是xml格式、txt文件格式还是其它文件格式,全部通过 URI对资源进行唯一的标识。
文字介绍,摘自: http://baike.baidu.com/view/1077487.htm
2. restify介绍
restify是一个基于Nodejs的REST应用框架,支持服务器端和客户端。restify比起express更专注于REST服务,去掉了express中的template, render等功能,同时强化了REST协议使用,版本化支持,HTTP的异常处理。
restify提供了DTrace功能,为程序调式带来新的便利!
restifty的发布页:http://mcavage.me/node-restify/
3. restify安装
系统环境
- Linux: Ubuntu 12.04 LTS 64bit
- node: v0.6.12
- npm: 1.1.4
创建项目
~ cd /home/conan/nodejs
~ mkdir nodejs-restify && cd nodejs-restify
~ sudo npm install restify
restify@2.6.1 node_modules/restify
├── assert-plus@0.1.4
├── once@1.3.0
├── deep-equal@0.0.0
├── escape-regexp-component@1.0.2
├── qs@0.6.5
├── tunnel-agent@0.3.0
├── keep-alive-agent@0.0.1
├── lru-cache@2.3.1
├── node-uuid@1.4.0
├── negotiator@0.3.0
├── mime@1.2.11
├── semver@2.2.1
├── spdy@1.14.12
├── backoff@2.3.0
├── formidable@1.0.14
├── csv@0.3.6
├── dtrace-provider@0.2.8
├── verror@1.3.6 (extsprintf@1.0.2)
├── bunyan@0.22.0 (mv@0.0.5)
└── http-signature@0.10.0 (assert-plus@0.1.2, asn1@0.1.11,ctype@0.5.2)
创建一个简单的rest服务
新建文件:app.js
~ vi app.js
var restify = require('restify');
function respond(req, res, next) {
res.send('hello ' + req.params.name);
}
var server = restify.createServer();
server.get('/hello/:name', respond);
server.head('/hello/:name', respond);
server.listen(3900, function() {
console.log('%s listening at %s', server.name, server.url);
});
运行程序
~ node app.js
restify listening at http://0.0.0.0:3900
使用curl访问测试
#空路径
~ curl localhost:3900
{"code":"ResourceNotFound","message":"/ does not exist"
#正常GET请求
~ curl localhost:3900/hello/conan
"hello conan"
#设置content-type, 返回包括header信息
~ curl -i localhost:3900/hello/conan -H 'accept:text/plain'
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 11
Date: Mon, 13 Jan 2014 08:31:30 GMT
Connection: keep-alive
hello conan
#设置connection:close, 返回包括header信息
~ curl -i localhost:3900/hello/conan -H 'connection:close'
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 13
Date: Mon, 13 Jan 2014 08:29:38 GMT
Connection: close
hello conan
我们看到用restify创建REST服务非常的容易,接下来我们将深入学习restify的API。
4. restify服务端API
- restify.createServer(): 创建服务器
- server.use(): 注册handler
- server.get(): 路由控制
- server.formatters: 设置content-type
- 异常处理
- socket.IO集成/li>
- Server API
- 组件管理
- Request API
- Response API
1). restify.createServer(): 创建服务器
创建服务器代码
var restify = require('restify');
var server = restify.createServer({
certificate: ...,
key: ...,
name: 'MyApp',
});
server.listen(8080);
服务器创建参数
- certificate: string, HTTPS服务器的证书
- key: string, HTTPS服务器的证书key
- formatters: object, 自定义的response的content-type
- log: object, 服务器日志,可以配合bunyan一起使用
- name: string, 服务器的response header
- spdy: Object, 允许集成node-spdy服务器
- version: string, 路由版本
- responseTimeHeader: string, X-Response-Time
- responseTimeFormatter: function, 格式化header的值
2). server.use(): 注册handler
注册服务器控制组件,按照代码顺序执行,需要放在路由代码之前。
3). server.get(): 路由控制
REST响应G请求:
- server.get(): 响应GET请求
- server.post(): 响应POST请求
- server.put(): 响应PUT请求
- server.head(): 响应HEAD请求
- server.del(): 响应DELETE请求
路由控制代码
function send(req, res, next) {
res.send('hello ' + req.params.name);
return next();
}
server.post('/hello', function create(req, res, next) {
res.send(201, Math.random().toString(36).substr(3, 8));
return next();
});
server.put('/hello', send);
server.get(/^\/([a-zA-Z0-9_\.~-]+)\/(.*)/, send);
server.head('/hello/:name', send);
server.del('hello/:name', function rm(req, res, next) {
res.send(204);
return next();
});
4). server.formatters: 设置Response的content-type
content-type类型
- application/json
- text/plain
- application/octet-stream
增加自定义的content-type:application/foo
var server = restify.createServer({
formatters: {
'application/foo': function formatFoo(req, res, body) {
if (body instanceof Error)
return body.stack;
if (Buffer.isBuffer(body))
return body.toString('base64');
return util.inspect(body);
}
}
});
显式的设置Response的content-type
res.setHeader('content-type', 'application/foo');
res.send({hello: 'world'});
5). 异常处理
异常处理代码
var server = restify.createServer();
server.get('/hello/:name', function(req, res, next) {
return next(new restify.InvalidArgumentError("I just don't like you"));
});
运行程序
~ curl -i localhost:3900/err/conan
HTTP/1.1 409 Conflict
Content-Type: application/json
Content-Length: 60
Date: Mon, 13 Jan 2014 11:06:26 GMT
Connection: keep-alive
RestError的内置的异常类型:
- BadDigestError: 400
- BadMethodError: 405
- InternalError: 500
- InvalidArgumentError: 409
- InvalidContentError: 400
- InvalidCredentialsError: 401
- InvalidHeaderError: 400
- InvalidVersionError: 400
- MissingParameterError: 409
- NotAuthorizedError: 403
- RequestExpiredError: 400
- RequestThrottledError: 429
- ResourceNotFoundError: 404
- WrongAcceptError: 406
自定义异常:MyError, errorCode:418
var restify = require('restify');
var util = require('util');
function MyError(message) {
restify.RestError.call(this, {
restCode: 'MyError'
statusCode: 418,
message: message,
constructorOpt: MyError
});
this.name = 'MyError';
};
util.inherits(MyError, restify.RestError);
6). Socket.IO集成
服务器端集成代码
var server = restify.createServer();
var io = socketio.listen(server);
server.get('/', function indexHTML(req, res, next) {
fs.readFile(__dirname + '/index.html', function (err, data) {
if (err) {
next(err);
return;
}
res.setHeader('Content-Type', 'text/html');
res.writeHead(200);
res.end(data);
next();
});
io.sockets.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
server.listen(8080, function () {
console.log('socket.io server listening at %s', server.url);
});
7). Server API
Events事件监听:
- NotFound: 404 handler
- MethodNotAllowed: 405 handler
- VersionNotAllowed: 400 handler
- UnsupportedMediaType: 415 handler
- after: 在所有handler之后执行
- uncaughtException: 未处理的异常
注: uncaughtException的处理,可以参考文章, Nodejs异步异常处理domain
Properties配置属性:
- name: string, 服务器名字
- version: string, 路由默认版本
- log: Object, 日志对象
- acceptable: Array(String), content-types列表
- url: string, 服务器信息
Methods函数:
- address(): 绑定地址
- listen(port, [host], [callback]): 启动服务器
- close(): 停止服务器
- pre(): 在路由之前触发的组件
- use(): 注册组件
8). 组件管理
restify已支持的组件
- Accept header parsing: 解析aceept header,返回客户端
- Authorization header parsing: HTTP Basic Auth认证
- Date header parsing: 数据头解析
- JSONP support: JSONP请求
- Gzip Response: 设置accept-encoding:gzip
- Query string parsing: 解析URL参数
- Body parsing (JSON/URL-encoded/multipart form): 解析内容
- Static file serving: 静态文件处理
- Throttling: 优化服务器性能配置
- Conditional request handling: 设置请求条件
- Audit logger: 日志记录
注册组件server.use()
var server = restify.createServer();
server.use(restify.acceptParser(server.acceptable));
server.use(restify.authorizationParser());
server.use(restify.dateParser());
server.use(restify.queryParser());
server.use(restify.jsonp());
server.use(restify.gzipResponse());
server.use(restify.bodyParser());
server.get(/\/docs\/public\/?.*/, restify.serveStatic({
directory: './public'
}));
server.use(restify.throttle({
burst: 100,
rate: 50,
ip: true,
overrides: {
'192.168.1.1': {
rate: 0, // unlimited
burst: 0
}
}
}));
server.use(function setETag(req, res, next) {
res.header('ETag', 'myETag');
res.header('Last-Modified', new Date());
});
server.use(restify.conditionalRequest());
server.on('after', restify.auditLogger({
log: bunyan.createLogger({
name: 'audit',
stream: process.stdout
})
}));
9). Request API
对node内核API:http.ServerRequest的封装
10). Response API
对node内核API:http.ServerResponse的封装
5. restify客户端API
- JsonClient: 收application/json, 发application/json
- StringClient: 收text/plain, 发url-encoded request
- HttpClient: 封装http/https
新建服务器端测试程序
var restify = require('restify');
var server = restify.createServer();
server.use(restify.acceptParser(server.acceptable));
server.use(restify.authorizationParser());
server.use(restify.dateParser());
server.use(restify.queryParser());
server.use(restify.jsonp());
server.use(restify.gzipResponse());
server.use(restify.bodyParser());
# 用于处理JsonClient请求
server.get('/json/v1',function(req,res,next){
var a = {name:'conan',blog:'blog.fens.me'}
res.send(a);
});
# 用于处理StringClient请求
server.get('/json/v2',function(req,res,next){
var a = {name:'conan',blog:'blog.fens.me'}
res.send(JSON.stringify(a));
});
server.listen(3900, function() {
console.log('%s listening at %s', server.name, server.url);
});
1). JsonClient
curl请求
~ curl -i http://localhost:3900/json/v1
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 38
Date: Mon, 13 Jan 2014 12:09:36 GMT
Connection: keep-alive
{"name":"conan","blog":"blog.fens.me"}
创建文件:jsonclient.js
~ vi jsonclient.js
var restify = require('restify');
var client = restify.createJsonClient({
url: 'http://localhost:3900'
});
client.get('/json/v1', function(err, req, res, obj) {
if(err) console.log(err)
console.log(JSON.stringify(obj, null, 2));
});
运行程序:
~ node jsonclient.js
{
"name": "conan",
"blog": "blog.fens.me"
}
2). StringClient
curl请求
~ curl -i http://localhost:3900/json/v2 -H 'accept:text/plain'
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 38
Date: Mon, 13 Jan 2014 12:10:07 GMT
Connection: keep-alive
{"name":"conan","blog":"blog.fens.me"}
创建文件:stringclient.js
~ vi stringclient.js
var restify = require('restify');
var client = restify.createStringClient({
url: 'http://localhost:3900'
});
client.get('/json/v2', function(err, req, res, obj) {
if(err) console.log(err)
console.log(JSON.stringify(obj, null, 2));
});
运行程序:
~ node stringclient.js
"{\"name\":\"conan\",\"blog\":\"blog.fens.me\"}"
3). HttpClient
创建文件: httpclient.js
~ vi httpclient.js
var restify = require('restify');
var client = restify.createClient({
url: 'http://localhost:3900'
});
client.get('/json/v1', function(err, req) {
req.on('result', function(err, res) {
res.body = '';
res.setEncoding('utf8');
res.on('data', function(chunk) {
res.body += chunk;
});
res.on('end', function() {
console.log(res.body);
});
});
});
运行程序
~ node httpclient.js
{"name":"conan","blog":"blog.fens.me"}
我们已经全面了解的了restify包,接下就可以快速构建我们自己的REST服务了。Node实现如此之简单,让原来Java程序员如何生存呢?!
restify构建REST服务(转)的更多相关文章
- 使用ServiceStack构建Web服务
提到构建WebService服务,大家肯定第一个想到的是使用WCF,因为简单快捷嘛.首先要说明的是,本人对WCF不太了解,但是想快速建立一个WebService,于是看到了MSDN上的这一篇文章 Bu ...
- 玩转Windows服务系列——使用Boost.Application快速构建Windows服务
玩转Windows服务系列——创建Windows服务一文中,介绍了如何快速使用VS构建一个Windows服务.Debug.Release版本的注册和卸载,及其原理和服务运行.停止流程浅析分别介绍了Wi ...
- 基于Dubbo框架构建分布式服务(一)
Dubbo是Alibaba开源的分布式服务框架,我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,这个对于很多应用都是迫切希望的,只需要通过简单的配 ...
- [译]Spring构建微服务
此文为译文,原文地址 介绍 本文通过一个使用Spring.Spring Boot和Spring Cloud的小例子来说明如何构建微服务系统. 我们可以通过数个微服务组合成一个大型系统. 我们可以想象下 ...
- 利用node构建本地服务
利用node构建本地服务 首先安装下node.js,地址为https://nodejs.org/en/,然后安装npm. node.js的中文api地址http://nodeapi.ucdok.com ...
- 使用ASP.Net WebAPI构建REST服务(一)——简单的示例
由于给予REST的Web服务非常简单易用,它越来越成为企业后端服务集成的首选方法.本文这里介绍一下如何通过微软的Asp.Net WebAPI快速构建REST-ful 服务. 首先创建一个Asp.Net ...
- 构建高性能服务(三)Java高性能缓冲设计 vs Disruptor vs LinkedBlockingQueue--转载
原文地址:http://maoyidao.iteye.com/blog/1663193 一个仅仅部署在4台服务器上的服务,每秒向Database写入数据超过100万行数据,每分钟产生超过1G的数据.而 ...
- 基于Dubbo框架构建分布式服务
Dubbo是Alibaba开源的分布式服务框架,我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,这个对于很多应用都是迫切希望的,只需要通过简单的配 ...
- 构建微服务:Spring boot
构建微服务:Spring boot 在上篇文章构建微服务:Spring boot 提高篇中简单介绍了一下spring data jpa的基础性使用,这篇文章将更加全面的介绍spring data jp ...
随机推荐
- CSS Box Model(盒子模型)
CSS Box Model(盒子模型) 一.简介 所有HTML元素可以看作盒子,在CSS中,"box model"这一术语是用来设计和布局时使用. CSS盒模型本质上是一个盒子,封 ...
- 20145324 Java实验五
1.运行教材上TCP代码,结对进行,一人服务器,一人客户端: 2.利用加解密代码包,编译运行代码,一人加密,一人解密: 3.集成代码,一人加密后通过TCP发送: 注:加密使用AES或者DES/AES或 ...
- RS232引脚,RS485引脚
1.RS232引脚 2.RS485引脚
- Xampp mysql启动
因为最近项目要用到php,需要集成Xampp环境,但是并没有接触过php,从官网下载了Xampp后,基本上就是傻瓜式安装了, 完成安装界面如下: 点击Apache的start可以正常启动,点击MYSQ ...
- Java数字证书操作
为服务器生成证书 keytool -genkey -v -alias tomcat -keyalg RSA -keystore D:\tomcat.keystore -validity 36500 为 ...
- springMVC多视图的支持
1.在springmvc.xml中加上 <!-- 多视图的支持 --> <bean class="org.springframework.web.servlet.view. ...
- Select级联菜单,用Ajax获取Json绑定下拉框(jQuery)
需求类似这样 ↓ ↓ ↓ --> 菜单A发生变化,动态取数据填充下拉菜单B. JS代码如下: <script type="text/javascript"& ...
- Yii框架(一)
这里接触了 MVC 设计模式中的控制器和视图部分. 创建了一个操作作为控制器的一部分去处理特定请求. 然后又创建了一个视图去构造响应内容. 在这个小例子中,没有模型调用,唯一涉及到数据的地方是 mes ...
- C# Page基础功能,用于各页面继承
IBasePage.cs文件 /// <summary> /// 用于页面或用户控件 /// </summary> public interface IBasePage { / ...
- vim 正则 捕获
在正规表达式中使用 \( 和 \) 符号括起正规表达式,即可在后面使用\1 \2等变量来访问捕获的内容. 将捕获内容前后交换,如下: :s/\(\haha\)\(hehe\)/\\/