11.2.5、搭建RESTful API 之 实现WSGI服务的URL映射

问题引出:对于一个稍具规模的网站来说,实现的功能不可能通过一条URL来完成。如何定义多条URL,也是RESTful API考虑的问题。

需求:

本小节将考虑这样一个虚拟机管理的WSGI服务。用户可以通过发送HTTP请求,来实现对虚拟机的管理(包括创建、查询、更新以及删除虚拟机等操作)。这个WSGI服务不会真正的在物理机上创建虚拟机,只是在服务中保存相应的虚拟机记录而已。

(nova已经实现类似的功能。当用户向 Nova 发送创建虚拟机的请求时,Nova 首先会在数据库中添加相应的记录,同时在物理机上创建相应的虚拟机。

Nova必须保证数据库中保存的虚拟机信息和实际创建的虚拟机信息一致,我们直接将记录保存在内存中)

开始今天的行程:

一、基础知识

(1)、RESTful API提供一套URL的规则。在RESTful API中,每条URL都是与资源相对应的。 一个资源可能是集合,也可能是一个个体。集合通常用集合名来标志。

例如:本小节实例中,使用instances表示虚拟机的集合,而个体通常使用统一的ID标志,使用UUID标志虚拟机

(2)、对于集合的操作通常是添加和查询; 对于个体的操作是虚拟机的查询、删除和更新。对应的URL如下图示:  

{instance_id}是虚拟机的UUID。将资源的ID放在URL中,是RESTful API的一大特点。

(3)、在WSGI中,要实现URL映射,主要是依赖Mapper和Controller两个类。

Mapper类用于实现URL的映射。当用户发送请求时,Mapper类会根据用户请求的URL及其方法来确定处理放入方法。

Controller类,则是实现处理HTTP请求的各种方法。


二、代码实现

1、配置文件:configure.ini

[pipeline:main]
pipeline = auth instance
[app:instance]
paste.app_factory = routers:app_factory
[filter:auth]
paste.filter_factory = auth:filter_factory

WSGI服务共使用了auth过滤器和instance应用程序两个部件。auth与上一节的相同,不同的是新定义了instance应用程序。instance应用程序对应的工

厂方法是routers包的app_factory方法。

2、URL映射的实现

WSGI服务使用了auth过滤器和instance应用程序两个部件。其中auth过滤器是用于HTTP头认证的,比较简单。核心功能都在instance应用程序中实现。

from webob.dec import *
from webob import Request,Response
import webob.exc

from routes import Mapper,middleware
import controllers

class Router(object):
    def __init__(self):
        self.mapper = Mapper()
        self.add_routes()
        self.router = middleware.RoutesMiddleware(self._dispatch, self.mapper)

    @wsgify
    def __call__(self, req):
        return self.router

    def add_routes(self):
        controller = controllers.Controller()

        self.mapper.connect('/instances', controller = controller,action = 'create', conditions = {'method' : ['POST']})
        self.mapper.connect('/instances', controller = controller,action = 'index', conditions = {'method' : ['GET']})
        self.mapper.connect('/instances/{instance_id}', controller = controller,action = 'show', conditions = {'method' : ['GET']})
        self.mapper.connect('/instances/{instance_id}', controller = controller,action = 'update', conditions = {'method' : ['PUT']})
        self.mapper.connect('/instances/{instance_id}', controller = controller,action = 'delete', conditions = {'method':['DELETE']})

    @staticmethod
    @wsgify
    def _dispatch(req):
        match = req.environ['wsgiorg.routing_args'][1]  #获取URL解析结果
        if not match:   #如果解析结果为空,则输出错误信息
            return webob.exc.HTTPNotFound()

        app = match['controller']   #获取URL对应的controller实例
        return app

def app_factory(global_config, **local_config):
    return Router()

(1)instance应用程序对应的工厂方法为routers包的app_factory方法。app_factory方法返回一个Router对象。工厂方法必须返回一个函数的实例。

Router类中必须实现__call__方法。  Router类的__call__方法的定义。返回router变量,其实也是一个可调用的实例。

__call__是在服务器收到HTTP请求时被调用。当服务器调用__call__方法时,会转而调用router方法。

(2)Router类的初始化方法(两件事情)

  • 初始化mapper成员变量。mapper成员变量是routes包的Mapper标准类,主要功能是 创建Mapper对象,用于URL解析。 然后,调用自身的add_routes方法往Mapper对象中注册URL映射
  • 初始化router成员变量。 (路由匹配,修改环境变量,实现分发) router成员变量的功能将HTTP请求分发给相应的方法进行处理。router成员变量是一个 RoutesMiddleware对象。初始化需要提供两个参数-----dispatch方法和mapper变量。dispatch方法是Router类的静态方法,功能是实现HTTP请求的分发。      

RoutesMiddleware对象(可调用的实例)执行过程:首先通过Mapper对象解析信息(匹配路由,修改环境变量),讲解析结果传递给dispatch方法。

(3)addroutes方法

addroutes方法共添加了5条URL路由,各条URL映射对应的功能参见上表。在添加URL映射时,调用mapper变量的connect方法。

Mapper类的connect方法用法:


<URL>:请求的URL

<controller>:处理HTTP请求的controller对象,这个对象必须是可调用的(实现了__call__方法)

<action>:指的是处理HTTP请求的方法名。所有定义的<action>对应的方法都在controller类中

<method-list>:是HTTP请求的方法列表。在RESTful API中,定义了GET、POST、PUT、DEAD和DELETE等方法,不同的方法对应资源的某项操作

action是一个字符串,指定的是方法名,而不是方法的实例。

(4)dispatch方法

  首先获取URL的解析结果(通过mapper类对象进行匹配路由,修改环境变量)。如果结果为空,说明相应的URL没有在mapper对象中注册,输出错误信

息。如果不为空,返回URL对应的controller对象。由于controller对象是可调用的,最终WSGI服务会调用controller对象的__call__方法处理HTTP请

求。  (dispatch方法只是将URL请求进行分发给相应的controller,并没有具体到方法)


3、controller类的实现

import uuid
from webob import Request,Response
import simplejson
from webob.dec import wsgify

class Controller(object):
    def __init__(self):
        self.instances = {}
        for i in range(3):
            inst_id = str(uuid.uuid4())
            self.instances[inst_id] = {'id' : inst_id, 'name' : 'inst-' + str(i)}
        print self.instances

    @wsgify
    def create(self, req):
        print req.params
        name = req.params['name']
        if name:
            inst_id = str(uuid.uuid4())

            inst = {'id' : inst_id, 'name' : name}
            self.instances[inst_id] = inst
            return {'instance' : inst}

    @wsgify
    def show(self, req, instance_id):
        inst = self.instances.get(instance_id)
        return {'instance' : inst}

    @wsgify
    def index(self, req):
        return {'instances': self.instances.values()}

    @wsgify
    def delete(self, req, instance_id):
        if self.instances.get(instance_id):
                self.instances.pop(instance_id)
    @wsgify
    def update(self, req, instance_id):
        inst = self.instances.get(instance_id)
        name = req.params['name']
        if inst and name:
            inst['name'] = name
            return {'instance': inst}

    @wsgify
    def __call__(self, req):
        arg_dict = req.environ['wsgiorg.routing_args'][1] #获取URL解析结果
        action = arg_dict.pop('action')  #获取处理的方法
        del arg_dict['controller']   #删除 controller项,剩下的都是参数列表

        method = getattr(self, action)  #搜索controller类中定义的方法
        result = method(req, **arg_dict)  #调用方法,处理HTTP请求

        if result is None:   #无返回值
            return Response(body='',status='204 Not Found',headerlist=[('Content-Type','application/json')])
        else:  #有返回值
            if not isinstance(result, str):
                result = simplejson.dumps(result) #将返回值转化为字符串
            return result
(1)准备参数   (2)查找并执行controller中相应的方法  (3)返回结果

定义的 /instances/{instance_id}这样的URL。当Router的mapper对象解析这样的URL时,会把{instance_id}解析成一个参数。

例如:对于/instances/123 这条url,解析完毕后,会在以上代码的arg_dict字典中产生 {'instance_id':123}。

create方法:

功能是创建一条虚拟机记录,其对应的URL是 POST/instances。在发送请求时,必须提供name字段来指定虚拟机名。 

def create(self, req):
        print req.params
        name = req.params['name']   #获取虚拟机名
        if name:
            inst_id = str(uuid.uuid4())  #自动生成UUID

            inst = {'id' : inst_id, 'name' : name}  #构造虚拟机信息元祖
            self.instances[inst_id] = inst   #添加虚拟机记录
            return {'instance' : inst}
可使用req.param来获取客户端提交的数据。

代码测试:

执行python WSGIService.py,便可启动WSGI服务。WSGI服务启动后,可在另一个终端进行测试。对所有的URL进行测试:

(1)、GET /instances

 

WSGI服务启动时,会自动生成3条虚拟机记录。输出的结果,便是生成的3条虚拟机记录信息。

(2)、POST /instances

 

以上命令创建名为new-inst的虚拟机记录。

(3)、GET /instances/{instance_id} 

  curl -H 'X-Auth-Token:open-sesame' -X GET > 127.0.0.1:8000/instances/c81e83fe-ae90-44a3-89e7-20918dfa9aef

{instance_id}是要查询的虚拟机的id,需要根据自己的实际情况修改。可以通过GET /instances来查看所有虚拟机记录的id

(4)、PUT /instances/{instance_id} 

curl -H 'X-Auth-Token:open-sesame' -X PUT --data 'name=new-inst2' > 127.0.0.1:8000/instances/c81e83fe-ae90-44a3-89e7-20918dfa9aef
 更名虚拟机

(5)、DELETE /instances/{instance_id}

curl -H 'X-Auth-Token:open-sesame' -X DELETE  > 127.0.0.1:8000/instances/c81e83fe-ae90-44a3-89e7-20918dfa9aef

 删除虚拟机


RESTful API

1、使用PasteDeploy配置WSGI服务

(1)、RESTful API底层是HTTP协议。传统HTTP协议基础上,明确定义各种HTTP方法的意义。 RESTful API定义的标准方法。


(2)、RESTful API使用PasteDeploy定制WSGI服务。WSGI服务的功能可以通过配置文件配置。在配置文件中可以定义app、filter、pipeline和composite等部件,

   

(3)、app和filter都须对应一个工厂方法。工厂方法通常有如下参数:

def factory(global_config, **local_config)

global_config参数保存从客户端传入的参数,例如HTTP消息体、客户上下文信息等。

local_config参数保存了在配置文件中设置的参数。

每个工厂方法最终会返回一个处理对应的app和filter HTTP请求的方法实例。处理app请求的方法实例通常有如下参数:  def app(request): request参数保存HTTP请求的上下文信息。

处理filter请求的方法实例通常有如下参数:def filter(request, app):其中request参数保存HTTP请求的上下文,app参数指定从当前过滤器的下一个过滤器(或应用程序)开始,到最后一个应用程序位置所形成的的子pipeline的实例。

2、Mapper.connect方法的调用

一个完整的WSGI服务,通常需要解析和处理多条URL。routes标准包中,定义了Mapper类来实现URL映射的管理。可以通过调用Mapper对象的connect方法来向WSGI服务注册URL映射。Mapper对象的connect方法一个调用示例。

mapper.connect(url,controller=controller, action=action, conditions = condition)

(1)、url参数指定的是请求的URL。两种形式,/<resources>类型的URL对应的是集合的操作,/<resources>/<resource-id>类型的URL对应的是成员操作。<resources>是资源的集合名,<resource-id>是资源的UUID。

(2)、controller指定的是处理HTTP请求的controller对象

(3)、action参数指定controller对象中处理HTTP请求的方法

(4)、condition参数指定服务器接收和处理HTTP请求的条件。通常在condition中指定HTTP请求的方法。例如:conditions = dict(method=[‘POST’]),指定只接受HTTP POST请求,conditions =dict(method=[‘POST’,‘PUT’])指定只接收HTTP POST和PUT请求。


RESTful API告一段落!!! 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
一、序言 4 二、需求分析说明书 4 2.1系统介绍 4 2.2系统面向的用户群体 4 2.3系统的功能性需求 4 2.4系统的非功能性需求 5 2.4.1用户界面需求 5 2.4.2软硬件环境需求 5 2.4.3软件质量需求 5 三、可行性分析报告 5 3.1技术可行性 5 3.2人员可能性 5 3.3时间、设备可能性 5 3.4系统工作量 5 3.5代码工作量 5 3.6文档要求 5 四、开发环境与项目规划 5 4.1开发环境 5 4.2项目规划与管理 5 4.2.1开发人员安排 5 4.2.2开发进度安排 6 五、软件界面设计标准与规范 6 5.1编写目的 6 5.2界面设计思想 6 5.3界面设计原则 6 5.4界面设计样式 6 5.5常见提示信息样式 6 5.6常见错误信息样式 7 5.7其他界面约定 7 六、软件编码设计标准与规范 7 6.1对象命名约定 7 6.2常量和变量命名约定 8 6.3结构化编码约定 8 6.4数据源的约定 9 6.5数据库访问约定 9 6.6其他约定 9 七、数据库分析与设计 10 7.1数据库环境说明 10 7.2数据库命名标准与规范 10 7.3数据库逻辑设计 10 7.4数据库物理设计 10 7.4.1表、视图汇总 10 7.4.2各表、视图设计详解 11 八、软件体系结构设计说明书 15 8.1系统概述 15 8.2设计约束 15 8.3设计策略 15 8.4系统概要设计说明书 16 8.4.1图例说明 16 8.4.2系统总体结构图 16 8.5系统详细设计说明书 17 8.5.1系统模块汇总 17 8.5.2系统核心模块详解 18 8.5.3系统模块详解 28 九、用户界面设计报告 42 9.1界面设计规范 42 9.2系统窗体汇总 42 9.3主界面设计 43 9.4子界面设计 43 9.5界面资源设计 44 十、软件测试分析报告 44 10.1测试范围与主要内容 44 10.2测试方法 44 10.3测试报告 44 10.4改进建议与措施 45 十一、软件使用说明书 45 11.1软件概述 45 11.2使用说明 45 11.2.1系统登陆 45 11.2.2系统退出 46 11.2.3图书类别管理 46 11.2.4图书信息管理 48 11.2.5查询图书信息 48 11.2.6读者类别管理 49 11.2.7读者信息管理 49 11.2.8查询读者信息 50 11.2.9借书管理 50 11.2.10还书管理 50 参考资料 51

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值