Designing a RESTful API with Python and Flask 201

 

rest服务器的搭建 - CSDN博客 http://blog.csdn.net/zhanghaotian2011/article/details/8760794

REST的架构设计

  REST(Representational State Transfer)是一种轻量级的Web Service架构风格,其实现和操作明显比SOAP和XML-RPC更为简洁,可以完全通过HTTP协议实现,还可以利用缓存Cache来提高响应速度,性能、效率和易用性上都优于SOAP协议。

  REST架构遵循了CRUD原则,CRUD原则对于资源只需要四种行为:Create(创建)、Read(读取)、Update(更新)和Delete(删除)就可以完成对其操作和处理。这四个操作是一种原子操作,即一种无法再分的操作,通过它们可以构造复杂的操作过程,正如数学上四则运算是数字的最基本的运算一样。

  REST架构让人们真正理解我们的网络协议HTTP本来面貌,对资源的操作包括获取、创建、修改和删除资源的操作正好对应HTTP协议提供的GET、POST、PUT和DELETE方法,因此REST把HTTP对一个URL资源的操作限制在GET、POST、PUT和DELETE这四个之内。这种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。

  REST的设计准则

  REST架构是针对Web应用而设计的,其目的是为了降低开发的复杂性,提高系统的可伸缩性。REST提出了如下设计准则:

  网络上的所有事物都被抽象为资源(resource);

  每个资源对应一个唯一的资源标识符(resource identifier);

  通过通用的连接器接口(generic connector interface)对资源进行操作;

  对资源的各种操作不会改变资源标识符;

  所有的操作都是无状态的(stateless)。

  使用REST架构

  对于开发人员来说,关心的是如何使用REST架构,这里我们来简单谈谈这个问题。REST不仅仅是一种崭新的架构,它带来的更是一种全新的Web开发过程中的思维方式:通过URL来设计系统结构。REST是一套简单的设计原则、一种架构风格(或模式),不是一种具体的标准或架构。REST有很多成功的使用案例,著名的Delicious和Flickr都提供基于REST风格的API使用,客户端调用也极其方便。

 

 

 

Designing a RESTful API with Python and Flask - miguelgrinberg.com https://blog.miguelgrinberg.com/post/designing-a-restful-api-with-python-and-flask

 

使用python的Flask实现一个RESTful API服务器端[翻译] - Vovolie - 博客园 https://www.cnblogs.com/vovlie/p/4178077.html

 

 

# !flask/bin/python
from flask import Flask

myapp = Flask(__name__)


@myapp.route('/')
def index():
return "Hello, World!"


if __name__ == '__main__':
myapp.run(host='120.78.187.72', debug=True)



#指明调用的解释器


[root@bigdata-server-02 myrestserver]# cat app.py
from flask import Flask

myapp = Flask(__name__)


@myapp.route('/')
def index():
return "Hello, World!"


if __name__ == '__main__':
myapp.run(host='0.0.0.0', debug=True)

[root@bigdata-server-02 myrestserver]# python app.py
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 229-432-432
220.152.100.225 - - [04/Dec/2017 03:42:42] "GET / HTTP/1.1" 200 -
220.152.100.225 - - [04/Dec/2017 03:43:03] "GET / HTTP/1.1" 200 -

 

浏览器输入云服务器的外网IP 

ip:5000

ok

 

 

[root@bigdata-server-02 myrestserver]# vim mytask.py
[root@bigdata-server-02 myrestserver]# cat mytask.py
from flask import Flask, jsonify

mytask = Flask(__name__)

tasks = [
{
'id': 1,
'title': 'Buy groceries',
'description': 'Milk, Cheese, Pizza, Fruit, Tylenol',
'done': False
},
{
'id': 2,
'title': 'Learn Python',
'description': 'Need to find a good Python tutorial on the web',
'done': False
}
]
tasks_try = [
{
'id': 111,
'title': 'Buy groceries',
'description': 'Milk, Cheese, Pizza, Fruit, Tylenol',
'done': False
},
{
'id': 222,
'title': 'Learn Python',
'description': 'Need to find a good Python tutorial on the web',
'done': False
}
]


@mytask.route('/restapi/todo/v1.0/tasks', methods=['GET'])
def get_tasks():
return jsonify({'tasks': tasks})

@mytask.route('/restapi/todo/v1.0/tasks_try', methods=['GET'])
def get_tasks_try():
return jsonify({'tasks_try': tasks_try})

if __name__ == '__main__':
mytask.run(host='0.0.0.0',debug=True)
[root@bigdata-server-02 myrestserver]# python mytask.py
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 229-432-432
220.152.200.225 - - [04/Dec/2017 04:19:46] "GET /restapi/todo/v1.0/tasks HTTP/1.1" 200 -
220.152.200.225 - - [04/Dec/2017 04:19:52] "GET /restapi/todo/v1.0/tasks HTTP/1.1" 200 -
220.152.200.225 - - [04/Dec/2017 04:19:53] "GET /restapi/todo/v1.0/tasks HTTP/1.1" 200 -
220.152.200.225 - - [04/Dec/2017 04:20:00] "GET /restapi/todo/v1.0/tasks_try HTTP/1.1" 200 -
220.152.200.225 - - [04/Dec/2017 04:20:07] "GET /restapi/todo/v1.0/tasks HTTP/1.1" 200 -

 

 

 

 

 

 

 

from flask import Flask, jsonify, abort, make_response, request

"""
考虑到后续的迁移以及后续的稳定性
此处密码临时至于此
"""
"""
tmp
"""

UidChkUrl = Flask(__name__)
UidChkUrl.config['JSON_AS_ASCII'] = False


@UidChkUrl.route('/RestApi/v1.0/UidChkUrl', methods=['POST'])
def uid_chk_url():
if not request.json or not 'uid' in request.json:
abort(400)
task = {
'uid': request.json['uid'],
'no_open': 12,
'no_ad': 34,
'description': '该uid在ad_direct_order中共计123条当前未失效,其中12条打不开相应页面,34条页面中没有我司广告位',
'cost_time': 123,
'request_time': time.strftime('%Y%m%d_%H%M%S', time.localtime(time.time() - 123)),
'current_time': time.strftime('%Y%m%d_%H%M%S', time.localtime(time.time()))
}
return jsonify({'status': '1', 'info': task}), 201


if __name__ == '__main__':
UidChkUrl.run(host='0.0.0.0', debug=True)



request.json['uid']
class JSONMixin(object):
    """Common mixin for both request and response objects to provide JSON
    parsing capabilities.

    .. versionadded:: 1.0
    """

    _cached_json = (Ellipsis, Ellipsis)

    @property
    def is_json(self):
        """Check if the mimetype indicates JSON data, either
        :mimetype:`application/json` or :mimetype:`application/*+json`.

        .. versionadded:: 0.11
        """
        mt = self.mimetype
        return (
            mt == 'application/json'
            or (mt.startswith('application/')) and mt.endswith('+json')
        )

    @property
    def json(self):
        """This will contain the parsed JSON data if the mimetype indicates
        JSON (:mimetype:`application/json`, see :meth:`is_json`), otherwise it
        will be ``None``.
        """
        return self.get_json()

  

request.get_data()

    def get_data(self, cache=True, as_text=False, parse_form_data=False):
        """This reads the buffered incoming data from the client into one
        bytestring.  By default this is cached but that behavior can be
        changed by setting `cache` to `False`.

        Usually it's a bad idea to call this method without checking the
        content length first as a client could send dozens of megabytes or more
        to cause memory problems on the server.

        Note that if the form data was already parsed this method will not
        return anything as form data parsing does not cache the data like
        this method does.  To implicitly invoke form data parsing function
        set `parse_form_data` to `True`.  When this is done the return value
        of this method will be an empty string if the form parser handles
        the data.  This generally is not necessary as if the whole data is
        cached (which is the default) the form parser will used the cached
        data to parse the form data.  Please be generally aware of checking
        the content length first in any case before calling this method
        to avoid exhausting server memory.

        If `as_text` is set to `True` the return value will be a decoded
        unicode string.

        .. versionadded:: 0.9
        """
        rv = getattr(self, '_cached_data', None)
        if rv is None:
            if parse_form_data:
                self._load_form_data()
            rv = self.stream.read()
            if cache:
                self._cached_data = rv
        if as_text:
            rv = rv.decode(self.charset, self.encoding_errors)
        return rv

  

 

pip包检索

/usr/local/bin/pip3 search aliyun
/usr/local/bin/pip3 install aliyun-python-sdk-core-v3
/usr/local/bin/pip3 install aliyun-python-sdk-sts

 

 

 

 

 

 

 

import json
from flask import Flask, jsonify, abort, make_response, request
import time
from aliyunsdkcore import client
from aliyunsdksts.request.v20150401 import AssumeRoleRequest


def getSts(uid):
    # 通过管理控制后台-访问控制 https://help.aliyun.com/product/28625.html
    # RAM控制台 https://ram.console.aliyun.com/
    # STS授权相关信息获取步骤:
    # 1.RAM控制台用户管理创建子用户(User)同时点击该用户创建并获取AccessKeyID和AccessKeySecret https://help.aliyun.com/document_detail/28637.html
    # 2.对该子用户(User) 授予AliyunSTSAssumeRoleAccess策略(必须),如需自定义策略请看 https://help.aliyun.com/document_detail/28640.html
    # 3.RAM控制台角色管理创建角色role,进行自定义授权设置(控制操作的内容),获取Arn https://help.aliyun.com/document_detail/28649.html
    # 注意点:
    # 只有子用户(User)才能调用 AssumeRole 接口
    # 阿里云主用户(Root User)的AccessKeys不能用于发起AssumeRole请求
    # python sdk说明
    # 构建一个 Aliyun Client, 用于发起请求
    # 构建Aliyun Client时需要设置AccessKeyId和AccessKeySevcret
    # STS是Global Service, API入口位于华东 1 (杭州) , 这里Region填写"cn-hangzhou"
    # clt = client.AcsClient('<access-key-id>','<access-key-secret>','cn-hangzhou')
    AccessKeyID = "************************"
    AccessKeySecret = "************************"
    roleArn = "************************"

    kid, ks = '5', '55'
    AccessKeyID, AccessKeySecret = kid, ks
    roleArn = 'acs:ram::30646318:role/aliyunosstokengeneratorrole'
    clt = client.AcsClient(AccessKeyID, AccessKeySecret, 'cn-hangzhou')
    # 构造"AssumeRole"请求
    request___ = AssumeRoleRequest.AssumeRoleRequest()
    # 指定角色 需要在 RAM 控制台上获取
    request___.set_RoleArn(roleArn)
    # RoleSessionName 是临时Token的会话名称,自己指定用于标识你的用户,主要用于审计,或者用于区分Token颁发给谁
    # 但是注意RoleSessionName的长度和规则,不要有空格,只能有'-' '.' '@' 字母和数字等字符
    # 具体规则请参考API文档中的格式要求

    '''
    #AssumeRole_操作接口_API 参考(STS)_访问控制-阿里云 https://help.aliyun.com/document_detail/28763.html

    RoleSessionName

    类型:String
    必须:是
    描述:用户自定义参数。此参数用来区分不同的Token,可用于用户级别的访问审计。
    格式:^[a-zA-Z0-9\.@\-_]+$ 2-32个字符
    '''
    request___.set_RoleSessionName(uid)

    # OSS Policy settings  could not set by default
    # can read https://help.aliyun.com/document_detail/56288.html
    # case https://help.aliyun.com/knowledge_detail/39717.html?spm=5176.product28625.6.735.5etPTf
    # case https://help.aliyun.com/knowledge_detail/39712.html?spm=5176.7739717.6.729.aZiRgD
    # 发起请求,并得到response
    try:
        response = clt.do_action_with_exception(request___)
        ## { "ErrorDump": "the JSON object must be str, not 'bytes'", "StatusCode": "500" }
        #        text = json.loads(response) win ok  linux  + .decode('utf-8')  加后 win 依然ok
        text = json.loads(response.decode('utf-8'))

        stsDict = dict().fromkeys(['RequestId', 'uid', 'Expiration', 'SecurityToken', 'StatusCode'])
        stsDict["RequestId"] = text["RequestId"]
        stsDict["uid"] = uid
        stsDict['Expiration'] = text["Credentials"]['Expiration']
        stsDict['SecurityToken'] = text["Credentials"]['SecurityToken']
        stsDict["StatusCode"] = "200"

        # stsText = json.dumps(stsDict)
        print('-----------》')
        print(stsDict)
        print('《-----------')
        return stsDict
    except Exception as e:
        print(e)
        # errorDict = dict().fromkeys(['StatusCode', 'ErrorCode', 'ErrorMessage'])
        errorDict = dict().fromkeys(['StatusCode', 'ErrorDump'])
        errorDict["StatusCode"] = "500"
        # errorDict["ErrorMessage"] = e.message
        # errorDict["ErrorCode"] = e.error_code
        errorDict["ErrorDump"] = '{}'.format(e)
        # stsText = json.dumps(errorDict)
        return errorDict
    # return stsText
    pass


aliBridge = Flask(__name__)
aliBridge.config['JSON_AS_ASCII'] = False


@aliBridge.route('/v1.0/aliBridge/aliyunosstokengenerator', methods=['POST'])
def aliyunosstokengeneratorrole():
    if not request.json:
        abort(400)
    chk_k = ['ipv4', 'imei', 'kid', 'ks']
    ipv4, imei, kid, ks = request.json['ipv4'], request.json['imei'], request.json['kid'], request.json['ks']
    #    ipv4, imei, kid, ks = request.json[0:4]
    uid = 'ipv4@{}@imei@{}'.format(ipv4, imei)
    uid = '{}@{}'.format(ipv4, imei)
    # uid ='ipv4__123__imei_123',
    if (kid, ks) != ('ourServerKeyId', 'ourServerKeyValue'):
        abort(400)
    for c in chk_k:
        if c not in request.json:
            abort(400)
    # task = {
    #     'uid': request.json['uid'],
    #     # 'uid': request,
    #     'no_open': 12,
    #     'no_ad': 34,
    #     'description': '该uid在ad_direct_order中共计123条当前未失效,其中12条打不开相应页面,34条页面中没有我司广告位',
    #     'cost_time': 123,
    #     'request_time': time.strftime('%Y%m%d_%H%M%S', time.localtime(time.time() - 123)),
    #     'current_time': time.strftime('%Y%m%d_%H%M%S', time.localtime(time.time()))
    # }
    # return jsonify({'status': '1', 'info': task}), 201

    task = getSts(uid)

    return jsonify(task), 201


'''
{
"ipv4":"197.164.165.154","imei":"123456789012347","kid":"ourServerKeyId","ks":"ourServerKeyValue"
}
'''
'''
{ "Expiration": "2018-06-15T07:04:29Z", "RequestId": "8F24601E-48E7-42F1-9128-2D631595DF9C", "SecurityToken": "CAISjQJ1q6Ft5B2yfSjIr4jhEer53pUV7bSaUWfFgk03QPxtubz91Dz2IHlNdHJpBeoZsfg2nWpQ7PgYlrYqGsADHBKYK5Ius9IOrF+JOtCa55HrsuxV0MT/QjTMU69uPTV6J7eXdsjUX9vwQXKm/3YB7NmXXDGmWEPfQv/toJV7b9MRcxClZD5dfrl/LRdjr8loXhm4d4zaUHjQj3HXEVBjtydllGp78t7f+MCH7QfEh1CIoY185aaRecD6M5c3ZcdFPo3rjLAsRM3oyzVN7hVGzqBygZFf9C3P1tPnWAAIs07fb7CLrYE2dVUhPPZgAd1NqPntiPt/offPkIf6zRlAO+xPWjjYXpqnxMbU3ksPUBqAATD+F5nQSnqFspyaLTywtxkTppmjCcCqTNDzRHG6X8umfz2IBECEIM8kWjmG9d/a6smkzgGiaZKGCy/AfzM06QWXyVmFZ8SdlBxJZxsW5qVKE0Hlbgx6rZao1Gm4uWF8r+1/4yTUd40cyjUjmoiaEj6iw6+oDWsS2RVG2hAjlaUN", "StatusCode": "200", "uid": "197.164.165.154@123456789012347" }
'''

if __name__ == '__main__':
    aliBridge.run(host='0.0.0.0', port=5001, debug=True)

  

 

 

 

 

 

 

《-----------
192.168.36.99 - - [15/Jun/2018 14:38:46] "POST /v1.0/aliBridge/aliyunosstokengenerator HTTP/1.1" 201 -
-----------》
{'SecurityToken': 'CAISjQJ1q6Ft5B2yfSjIr4nYPtPQnLpE5LKCaUj0o3lhf9lEjfPMjTz2IHlNdHJpBeoZsfg2nWpQ7PgYlrYqGsADHBKYK5Ius9IOrF+JOtCa55HrsuxV0MT/QjTMU5tTczR6J7eXdsjUX9vwQXKm/3YB7NmXXDGmWEPfQv/toJV7b9MRcxClZD5dfrl/LRdjr8loXhm4d4zaUHjQj3HXEVBjtydllGp78t7f+MCH7QfEh1CIoY185aaRecD6M5c3ZcdFPo3rjLAsRM3oyzVN7hVGzqBygZFf9C3P1tPnWAAIs07fb7CLrYE2dVUhPPZgAd1NqPntiPt/offPkIf6zRlAO+xPWjjYXpqnxMbU3ksPUBqAAaQv4QxLwoq6uvykmhGQJJNsH6gTnXcrLjhYUZ9G9lBNUV7NGq4hNJnpSWOw/5wBV3N+yZyO4ftTtLQG28tYa/EUy0zOri5xxB9t2GEfv2xNgtm5c5XsgZwAQEoiBvLHWWeBMDHkQGf+2tYWwQWf4IOa1Ij6lacZP6+BaFE7MG9h', 'StatusCode': '200', 'Expiration': '2018-06-15T07:56:14Z', 'RequestId': 'BBD5B0B1-49DA-4931-98F2-029409656A8C', 'uid': '197.164.165.154@123456789012347'}
《-----------

 


https://graph.qq.com/oauth2.0/show?which=Login&display=pc&scope=get_user_info%2Cadd_share%2Cadd_t%2Cadd_pic_t%2Cget_info%2Cget_other_info%2Cget_fanslist%2Cget_idollist%2Cadd_idol%2Cget_repost_list&state=b4a42c9cisQBYRjEAXCpcXpvbmVfc25zxAFzxADEAXICxAFkAMQBdgLEAWkAxAFoxA5hcGkuc25zc2RrLmNvbcQBbQDEAW7ZZmh0dHBzOi8vc3NvLnRvdXRpYW8uY29tL2F1dGgvbG9naW5fc3VjY2Vzcy8_c2VydmljZT1odHRwczovL21wLnRvdXRpYW8uY29tL3Nzb19jb25maXJtLz9yZWRpcmVjdF91cmw9Lw%3D%3D&redirect_uri=http%3A%2F%2Fapi.snssdk.com%2Fauth%2Flogin_success%2F&response_type=code&client_id=100290348



 在header中指明按照json解释,在body中按照json格式形成字符串

 

 

import requests
import json
url = 'http://192.168.3.212:5001/v1.0/aliBridge/aliyunosstokengenerator'
headers = {'Content-Type': 'application/json'}
d = {
    "ipv4": "197.164.165.154", "imei": "123456789012347", "kid": "ourServerKeyId", "ks": "ourServerKeyValue"
}
r=requests.post(url,data=json.dumps(d),headers=headers)

print(r)
dd=9

  

def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True,
        allow_nan=True, cls=None, indent=None, separators=None,
        default=None, sort_keys=False, **kw):
    """Serialize ``obj`` to a JSON formatted ``str``.

    If ``skipkeys`` is true then ``dict`` keys that are not basic types
    (``str``, ``int``, ``float``, ``bool``, ``None``) will be skipped
    instead of raising a ``TypeError``.

    If ``ensure_ascii`` is false, then the return value can contain non-ASCII
    characters if they appear in strings contained in ``obj``. Otherwise, all
    such characters are escaped in JSON strings.

    If ``check_circular`` is false, then the circular reference check
    for container types will be skipped and a circular reference will
    result in an ``OverflowError`` (or worse).

    If ``allow_nan`` is false, then it will be a ``ValueError`` to
    serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) in
    strict compliance of the JSON specification, instead of using the
    JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``).

    If ``indent`` is a non-negative integer, then JSON array elements and
    object members will be pretty-printed with that indent level. An indent
    level of 0 will only insert newlines. ``None`` is the most compact
    representation.

    If specified, ``separators`` should be an ``(item_separator, key_separator)``
    tuple.  The default is ``(', ', ': ')`` if *indent* is ``None`` and
    ``(',', ': ')`` otherwise.  To get the most compact JSON representation,
    you should specify ``(',', ':')`` to eliminate whitespace.

    ``default(obj)`` is a function that should return a serializable version
    of obj or raise TypeError. The default simply raises TypeError.

    If *sort_keys* is true (default: ``False``), then the output of
    dictionaries will be sorted by key.

    To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
    ``.default()`` method to serialize additional types), specify it with
    the ``cls`` kwarg; otherwise ``JSONEncoder`` is used.

    """
    # cached encoder
    if (not skipkeys and ensure_ascii and
        check_circular and allow_nan and
        cls is None and indent is None and separators is None and
        default is None and not sort_keys and not kw):
        return _default_encoder.encode(obj)
    if cls is None:
        cls = JSONEncoder
    return cls(
        skipkeys=skipkeys, ensure_ascii=ensure_ascii,
        check_circular=check_circular, allow_nan=allow_nan, indent=indent,
        separators=separators, default=default, sort_keys=sort_keys,
        **kw).encode(obj)

  

 

201 Created - HTTP | MDN https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status/201

 

在HTTP协议中,201 Created 是一个代表成功的应答状态码,表示请求已经被成功处理,并且创建了新的资源。新的资源在应答返回之前已经被创建。同时新增的资源会在应答消息体中返回,其地址或者是原始请求的路径,或者是 Location 首部的值。

这个状态码的常规使用场景是作为 PUT 请求的返回值。

 

posted @ 2017-12-04 00:16  papering  阅读(631)  评论(0编辑  收藏  举报