微服务和微应用
121
:git => 'https://github.com/nopressurelabs/rails-api',
:branch => 'master'
gem 'moped', github: 'mongoid/moped'
gem 'mongoid', '
~
> 4.0.0', github: 'mongoid/mongoid'
gem 'bson_ext'
gem 'spring', :group => :development
# Serializer for JSON
gem 'active_model_serializers'
# CORS
gem 'rack-cors', :require => 'rack/cors'
现在可以初始化应用了。首先,执行
bundle
$
bundle install
然后执行:
$
rake db:create
Rack CORS
中间件
Rack::Cors
为兼容
Rack
Web
应用提供跨域资源共享(
Cross-Origin Resource
Sharing
CORS
)支持。
CORS
http://www.w3.org/TR/cors/
)允许
Web
应用发起跨域
Ajax
请求,而不用采
取变通方法,例如
JSONP
跨域资源共享是
W3C
制定的一个机制,其作用是允许客户端跨域请求。这个机制
定义了一个规范,允许
API
跨域请求某些资源。例如,
http://example.org
中的
API
如果想跨域请求
http://hello-world.example
中的资源,就可以使用这一机制(例如,
在响应首部中指定
Access-Control-Allow-Origin: http://example.org
)。
探索
/lib
目录
Citywalks API
使用
WikiCat API
查找地点所属的分类,使用
Wikipin API
查找附近的地
点。
为了从外部获取数据,我们要编写一些逻辑,发起
REST
请求,获取并解析数据。严格
来说,实现这一需求的代码与这个应用的核心功能无关,既不属于模型,也不属于控制器,
更不是对二者的扩展。
这部分代码要保存在
/lib
目录中。
122
8
我们要编写的第一个模块用于处理
REST
请求和响应,如果出错,还会创建有意义的错
误对象。在
/lib
目录中新建一个文件,命名为
restful.rb
,然后写入下述代码:
require 'net/http'
require 'json'
module Restful
def send_request(end_point)
request_url = URI.parse(URI.encode(end_point))
log "Request URL: #{request_url}"
res = Net::HTTP.get_response(request_url)
unless res.kind_of? Net::HTTPSuccess
raise Restful::RequestError, "HTTP Response: #{res.code} #{res.message}"
end
Response.new(res.body)
end
class RequestError < StandardError; end
# REST
请求得到的响应对象
class Response
def initialize(json)
@doc = JSON.parse(json)
end
#
返回
JSON
对象
def doc
@doc
end
#
如果响应出错,返回
true
def has_error?
!(error.nil? || error.empty?)
end
#
返回错误消息
def error
@doc.has_key? "error"
end
#
返回错误代码
def error_code
if @doc.has_key? "error"
@doc["error"]["status"]
end
end
end
protected
微服务和微应用
123
def log(s)
if defined? RAILS_DEFAULT_LOGGER
RAILS_DEFAULT_LOGGER.error(s)
elsif defined? LOGGER
LOGGER.error(s)
else
puts s
end
end
end
接下来再定义两个类似的模块:一个针对
Wikipin API
,另一个针对
WikiCat API
先在
/lib
目录中新建
wikicat.rb
文件,写入如下代码:
require 'net/http'
require 'json'
module Wikicat
#
定义
API
的版本和要使用的端点
CAT_VERSION = '1.0'
CAT_SERVICE_URL = 'http://0.0.0.0:3000'
PIN_SERVICE_URL = 'http://wikipin-nopressurelabs.rhcloud.com'
#
提供地点的相对
URL
,获取上游的分类图
def pin_upper(pin)
get_pin = self.class.send_request("#{PIN_SERVICE_URL}#{pin}")
cat = get_pin.doc["pin"]
if cat
sub_category = cat["title"]
request_upper_graph(sub_category).doc[sub_category]
end
end
#
提供地点的相对
URL
,获取下游的分类图
def pin_lower(pin)
get_pin = send_request("#{PIN_SERVICE_URL}#{pin}")
cat = get_pin.doc["pin"]
if cat
sub_category = cat["title"]
request_lower_graph(sub_category).doc[sub_category]
end
end
#
获取上游分类图
def request_upper_graph(category)
self.class.send_request("#{CAT_SERVICE_URL}/api/v1/graph/up/
#{category.gsub!(/\s/,'_')}")
end
124
8
#
获取下游分类图
def request_lower_graph(category)
self.class.send_request("#{CAT_SERVICE_URL}/api/v1/graph/
#{category.gsub!(/\s/,'_')}")
end
end
然后在
/lib
目录中新建
wikipin.rb
文件,写入下述代码:
require 'net/http'
require 'json'
module Wikipin
PIN_VERSION = '1.0'
PIN_SERVICE_URL = 'http://wikipin-nopressurelabs.rhcloud.com'
#
获取所在位置附近的地点
#
可以提供经纬度,也可以让服务根据
IP
地址定位
def request_pins(point=nil)
# point = "lon,lat"
if point
send_request("#{PIN_SERVICE_URL}/api/v1/pins/?point=#{point}")
else
send_request("#{PIN_SERVICE_URL}/api/v1/pins")
end
end
#
获取
IP
地址块信息
def request_block(ip)
send_request("#{PIN_SERVICE_URL}/api/v1/blocks?ip_address=#{ip}")
end
#
获取地点
def get_pin(pin)
send_request("#{PIN_SERVICE_URL}#{pin}")
end
end
/lib
目录适合用于测试代码,这些代码最终会从应用中提取出来,制成
Ruby gem
。把代
码放在
/lib
目录中便于隔离测试。此外,这样做还迫使自己编写独立于应用的类。如果
对实现的效果满意,可以轻易把这些代码提取出来,制成独立的
gem
定义模型
构建逻辑定义好了,接下来可以定义应用的模型了。
首先,定义
Walk
模型。在
app/models
目录中新建
walk.rb
文件:
微服务和微应用
125
class Walk
include Mongoid::Document
include Mongoid::Timestamps::Created
include ActiveModel::SerializerSupport
#
记得
extend
include
之间的区别吗
extend Wikipin
extend Restful
include Wikicat
# Walk
模型包含的字段
field :title, type: String
field :author, type: String
field :pins, type: Array
field :location, :type => Array
field :categories, :type => Array
index({ location: "2d" }, { min: -200, max: 200 })
validates :pins, length: { minimum: 0, maximum: 10 }
end
然后,执行下述命令,在数据库中动态创建索引:
$
rake db:mongoid:create_indexes
此外,我们还要定义一个非持久存储的错误对象。这个错误对象与本章前面定义的那个
一模一样,所以我不再重复列出代码(仓库中有相应的代码)。
编写控制器
Walks
控制器用于对
Walks
资源做所有
CRUD
操作。我们想列出全部徒步路线、创建徒
步路线、删除徒步路线和修改徒步路线。
class Api::V1::WalksController < ApplicationController
respond_to :json
#
引入后面定义的两个
concern
,分别用于创建和定位徒步路线
include WalkLocator
include WalkCreator
# before_action
是过滤器。
#
过滤器指在控制器动作之前、之后或前后运行的方法。
#
过滤器会被继承,因此,如果在
ApplicationController
#
中设置了过滤器,应用中的每个控制器都会运行那个过滤器
before_action :set_walk, only: [:show, :edit, :update, :destroy]

Get RESTful Rails Development (中文版) now with O’Reilly online learning.

O’Reilly members experience live online training, plus books, videos, and digital content from 200+ publishers.