对现有REST API更新为GraphQL API

背景

        一个痛点吧,rest api 可以获取一个接口的全量数据[不管用的上用不上,想不想用,都一股脑的全吐给你],这样就造成一个问题
每次数据传输量都和很大,尤其遇到当一个API被多处调用时,每处需要的数据字段各有不同,当一个rest api接口为了兼容每个请求方
所需要的数据时,就会对返回的大json进行不断地冗余字段,导致各个接收方会活得一些无用的字段[也有可能有的字段涉及到法律问题]
请求方不想获取这些字段进行使用进而不进行请求,根据这一背景,要对每个rest接口逻辑进行最小量修改使其更新成GraphQL api接口

使用框架及语言

python 3.6
Flask 框架

GraphQL API需求

1、接收关键词参数进行模糊查询 [id]
2、接收关键词来控制返回数量 默认返回5个 [count]
3、控制最外层返回参数

需要新增安装的模块

pip install Flask-GraphQL graphene

基本知识

属性参数

class Query(ObjectType):
    hello = String(
        name=String(
            name="HELLO",  # 覆盖字段的名称
            description="XXXXXXXXXX",  # 在GraphiQL浏览器中显示的类型的描述。
            required=True,  # 如果为True,则服务器将对此字段强制执行一个值。请参阅NonNull。默认值为False。
            deprecation_reason="SSSSSSSSS"  # 提供该字段的弃用原因。
            default_value="stranger"  # 提供字段的默认值。
            )
    )

基本标量

graphene.String:表示文本数据,以UTF-8字符序列表示。 GraphQL最常使用String类型来表示人类可读的自由格式文本。
graphene.Int:表示非小数符号整数。根据GraphQL规范,Int是带符号的32位整数
graphene.Float:表示IEEE 754指定的带符号双精度分数值
graphene.Boolean:表示是或否。
graphene.ID:表示唯一的标识符,通常用于重新获取对象或用作缓存的键。 ID类型以字符串形式显示在JSON响应中; 但是,它并不旨在人类可读。 当期望作为输入类型时,任何字符串(例如“ 4”)或整数(例如4)输入值都将被接受为ID。
graphene.types.datetime.Date:表示由iso8601指定的日期值。
graphene.types.datetime.DateTime:表示由iso8601指定的DateTime值。
graphene.types.datetime.Time:表示由iso8601指定的时间值。
graphene.types.json.JSONString:表示一个JSON字符串。

原始单条数据返回结构

描述

[{代表一条数据},{},{}]

数据结构

[
    {
        "one": [
            "xxx",
            "xxxxxx"
        ],
        "two": "xxxxxxxxxxx",
        "image": {
            "large_image": "xxxxxx",
            "medium_image": "xxxxx",
            "small_image": "xxx",
            "swatch_image": "xxxx"
        },
        "four": "xxxxxxxxx",
        "id": "XXXXXSDSDSDSDSD",
        "six": "XXXXXXXX",
        "seven": {
            "xxx": "xxxxx",
            "xxxxx": "xxxxx"
        }
    }
]

解决办法

import collections
from flask import Flask
from flask_graphql.graphqlview import GraphQLView
from graphene import ObjectType, String, Schema, Int, List, JSONString

class Conversion():
    """json data to GraphQL data"""
    def __init__(self):
        self.outpot = ['one', 'two', 'image', 'four', 'id', 'six', 'seven']  # 定义规范化输出字段

    def cover(self, data):
        """
        data: 原始json数据
        PS: 如果想看json data 转换结构 可直接将数据代入此方法
        """
        Person = collections.namedtuple("Person", self.outpot)
        outdict = {}
        for dt in data:
            resp = collections.OrderedDict()
            for tp in self.outpot:
                resp[tp] = dt[tp]
            outdict[data.index(dt)] = Person(*resp.values())
        return outdict


class PersonType(ObjectType):
    one = List(String)
    two = String()
    image = JSONString()
    four = String()
    id = String()
    six = String()
    seven = JSONString()

    def resolve_one(person, info):
        return person.one

    def resolve_two(person, info):
        return person.two

    def resolve_image(person, info):
        return person.image

    def resolve_four(person, info):
        return person.four

    def resolve_id(person, info):
        return person.id

    def resolve_six(person, info):
        return person.six

    def resolve_seven(person, info):
        return person.seven

class Query(ObjectType):
    """
    GraphQL API 方法有 search
    输入参数为 id count 
    """
    search = List(PersonType, id=String(), count=Int(default_value=5), description="Get search details")

    def resolve_search(root, info, id, count):
        data = 原始获取数据方式(id)
        response = Conversion().cover(data)
        resp = list(response.values())[:count]
        return resp


schema = Schema(query=Query)

app.add_url_rule('/graphql', view_func=GraphQLView.as_view('graphql', schema=schema, graphiql=True, pretty=True))

if __name__ == '__main__':
    app.run()

测试方法

1、手写requests 请求
2、postman
3、浏览器直接请求 url [http://127.0.0.1:5000/graphq]

请求结构

浏览器直接请求测试结构

{
  search(id: "lslsl", count:3) {
    one
    two
    id
    image
  }
}

 

posted @ 2020-03-17 17:25  争-渡  阅读(483)  评论(1)    收藏  举报