V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
gosky
V2EX  ›  Python

Python “安全”序列化复杂对象的问题

  •  
  •   gosky · 114 天前 · 1139 次点击
    这是一个创建于 114 天前的主题,其中的信息可能已经有所发展或是发生改变。
    需求是:
    接口里,有多处渲染图片的逻辑
    想把渲染图片的逻辑放到一个个单独的渲染接口

    原接口返回的响应,原本带了几个图片的 base64 ,现在改为带几个图片的渲染参数
    前端拿到渲染参数,拼成一个图片 url ,再请求 ——当然,这里必须是 get ,渲染参数也不能嵌套
    渲染接口,接到渲染参数,再渲染图片,返回一个图片响应到前端

    渲染参数结构大致是这样的:
    {
    code: "123",
    type: "xyz",
    param: "..."
    }

    这个渲染参数,是一个两层结构
    具体的渲染参数都在 param 中,code 和 type 只是用来找到具体的渲染口子
    因为渲染接口只能是 get ,param 给前端必须是字符串
    后端拿到渲染参数第一步,解析出 code 和 type ,找到渲染口子。渲染口子内部再解析 param
    之前没多想,把渲染参数 json 序列化,赋值给 param
    没想到 json 序列化渲染参数时,会将 param 的 json 进行转义…… 导致反序列化时,param 反序列化报错

    当然,有几个可行的方案:
    一、ensure_ascii=False 不转义。这会影响全局。因为渲染参数响应,它的序列化是全局逻辑
    二、忽略渲染参数的两层结构,直接完整的序列化和反序列化
    三、param 先 json 序列化,再 base64 安全编码。这个问题是多了一部操作,且输出结果人类不可读
    四,反序列化 param 时,先反转义

    求一个更加优雅的方案

    我需要的,也许是一个“安全”的序列化方法, 能将 param 数据,序列化为不需要转义的数据。但 python 没有 safe 的 json 方法。base64 倒是一般都有
    js 有一个库,https://www.npmjs.com/package/qs ,可以将 js 嵌套对象、数组对象编码为查询字符串
    但 python 好像没这样的库
    第 1 条附言  ·  114 天前
    分析和描叙都不到位

    目的是要把嵌套的 param ,安全编码,作为查询参数
    第 2 条附言  ·  109 天前
    结果:
    一般客户端应用程序、客户端库都自动会将查询参数值转义。但当时我用的客户端是 postman ,postman 并没有对参数值转义,导致出现意外情况。部分客户端应用程序的转义逻辑有点奇怪,但眼下只有一个客户端程序,所以不用考虑到这点。

    最终方案:服务端业务逻辑将 param 对象 json 序列化/反序列化就好。其它交给各处自动处理。
    9 条回复    2024-08-14 18:49:45 +08:00
    tianzhongs
        1
    tianzhongs  
       114 天前
    可以使用自定义的编码方式来处理这个问题。思路如下:
    对于 param 中的数据,将其键值对按照一定规则拼接成字符串,而不是直接使用 JSON 序列化。
    例如,如果 param 是 {"key1": "value1", "key2": "value2"},可以将其拼接为 key1=value1&key2=value2 的形式。
    在前端和后端约定好这个编码规则。
    前端在发送请求时,按照这个规则将 param 中的数据拼接成字符串,然后将整个渲染参数对象构建成 URL 的查询参数形式。
    后端接收到请求后,先解析出 code 和 type ,然后根据约定的规则将 param 部分的字符串再解析回键值对的形式。
    这样做的好处有:
    不需要依赖复杂的第三方库或者特殊的设置(如 ensure_ascii=False ),并且完全符合只能使用 GET 请求且参数不能嵌套的要求。
    避免了 JSON 序列化可能带来的转义问题,同时也没有增加过多的复杂操作(相比于先序列化再 base64 编码的方式)。
    输出的结果相对简洁且人类可读程度较高,只要了解规则,在调试等情况下也比较容易理解。
    sagaxu
        2
    sagaxu  
       114 天前
    JSON 编码解码,是 JSON 规范,不会因为转义不转义失败。
    URL 的 Query String ,也有自己的编码规范。

    两件事情是完全不相干的,要分别处理,难不成你指望 JSON 序列化后的东西直接拼 URL 里用?
    tolbkni
        3
    tolbkni  
       114 天前
    ```python
    from urllib.parse import parse_qs
    y = parse_qs('a=c')
    ```
    gosky
        4
    gosky  
    OP
       114 天前
    @tianzhongs param 内部是嵌套的哦
    gosky
        5
    gosky  
    OP
       114 天前
    @sagaxu 前面分析不到位
    准确地说,就是要把 param 安全编码,作为查询参数
    sagaxu
        6
    sagaxu  
       114 天前
    @gosky 常规做法是先 JSON 序列化变 str ,再 URL 转义拼参数中,后端拿到 str 后直接 JSON 解码得到原始参数
    MoYi123
        7
    MoYi123  
       114 天前   ❤️ 1
    问题最关键的 param 是怎么样子的, 让你用 param: "..." 这样三个点跳过了.
    gosky
        8
    gosky  
    OP
       114 天前
    @MoYi123 不固定哦 可能有嵌套结构和列表
    MoYi123
        9
    MoYi123  
       114 天前
    @gosky 所以需要你给一些例子啊.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2585 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 01:40 · PVG 09:40 · LAX 17:40 · JFK 20:40
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.