使用GraphQL机制,但返回CSV

时间:2019-10-31 03:07:26

标签: csv graphql mime-types graphene-python

普通的REST API可能会让您使用不同的Accept标头(例如, application/jsontext/htmltext/csv格式的响应。

但是,如果您使用的是GraphQL,似乎JSON是唯一可接受的返回内容类型。但是,我需要我的API能够返回CSV数据,以供不了解JSON的不太成熟的客户端使用。

如果给定Accept: text/csv头,那么GraphQL端点返回CSV数据是否有意义?如果没有,是否有更好的实践方法?

这更多是一个概念性问题,但是我专门使用Graphene来实现我的API。是否提供任何处理自定义内容类型的机制?

2 个答案:

答案 0 :(得分:1)

是的,你可以,但它不是内置的,你必须覆盖一些东西。这更像是一种解决方法。

采取这些步骤,您将获得 csv 输出:

  1. csv = graphene.String() 添加到您的查询中并将其解析为您想要的任何内容。

  2. 创建一个继承 GraphQLView

    的新类
  3. 覆盖 dispatch 函数如下:

    def dispatch(self, request, *args, **kwargs):
        response = super(CustomGraphqlView, self).dispatch(request, *args, **kwargs)
        try:
            data = json.loads(response.content.decode('utf-8'))
            if 'csv' in data['data']:
                data['data'].pop('csv')
                if len(list(data['data'].keys())) == 1:
                    model = list(data['data'].keys())[0]
                else:
                    raise GraphQLError("can not export to csv")
                data = pd.json_normalize(data['data'][model])
                response = HttpResponse(content_type='text/csv')
                response['Content-Disposition'] = 'attachment; filename="output.csv"'
    
                writer = csv.writer(response)
                writer.writerow(data.columns)
                for value in data.values:
                    writer.writerow(value)
        except GraphQLError as e:
            raise e
        except Exception:
            pass
        return response
    
  4. 导入所有必要的模块

  5. 用新的视图类替换 GraphQLView 文件中的默认 urls.py

现在,如果您在 GraphQL 查询中包含“csv”,它将返回原始 csv 数据,然后您可以将数据保存到前端的 csv 文件中。示例查询类似于:

query{
  items{
    id
    name
    price
    category{
        name
    }
  }
  csv
}

请记住,这是一种以 csv 格式获取原始数据的方法,您必须保存它。您可以使用以下代码在 JavaScript 中执行此操作:

req.then(data => {
    let element = document.createElement('a');
    element.setAttribute('href', 'data:text/csv;charset=utf-8,' + encodeURIComponent(data.data));
    element.setAttribute('download', 'output.csv');

    element.style.display = 'none';
    document.body.appendChild(element);

    element.click();

    document.body.removeChild(element);
})

这种方法将 JSON 数据展平,因此不会丢失任何数据。

答案 1 :(得分:0)

GraphQL依赖于(并因此而发光)响应嵌套数据。据我了解,CSV只能显示平面键值对。这使得CSV不太适合GraphQL响应。

我认为实现您想要做的最干净的方法是将GraphQL客户端放在您的客户端之前:

+------+  csv  +-------+  http/json  +------+
|client|<----->|adapter|<----------->|server|
+------+       +-------+             +------+

这里的好处是您的适配器只需要将其指定的查询转换为CSV。

显然,您可能并不总是能够这样做(但是您如何使他们发送GraphQL查询)。或者,您可以构建将JSON转换为CSV的中间件。但是然后您必须处理整个GraphQL规范。祝你好运,翻译此回复:

{
  "__typename": "Query",
  "someUnion": [
    { "__typename": "UnionA", "numberField": 1, "nested": [1, 2, 3, 4] },
    { "__typename": "UnionB", "stringField": "str" },
  ],
  "otherField": 123.34
}

因此,如果您无法解决通过HTTP传输CSV的问题,那么GraphQL就是错误的选择,因为它不是为此而构建的。而且,如果您不允许那些难以转换为CSV的GraphQL功能,那么您就不再具有GraphQL,因此没有必要再将其称为GraphQL。