【问题标题】:Python\Flask\SQLAlchemy\Marshmallow - How to process a request with duplicate values without failing the request?Python\Flask\SQLAlchemy\Marshmallow - 如何处理具有重复值的请求而不使请求失败?
【发布时间】:2020-10-11 09:10:57
【问题描述】:

这只是我需要处理的 Python\Flask\SQLAlchemy\Marshmallow 系统中的第二个任务(我需要修复的错误)。所以请尽量对我轻松:)

简而言之:我想批准一个明显无效的请求。

详细说明:

我需要处理这样一种情况,用户可能会发送带有一些 json 的请求,其中他错误地将重复值包含在列表中。

例如:

{
    "ciphers": [
        "TLS_AES_256_GCM_SHA384",
        "AES256-SHA256"
    ],
    "is_default": true,
    "tls_versions": [
        "tls10",
        "tls10",
        "tls11",
    ]
}

我需要做的是消除重复的 tls1.0 值之一,但将请求视为有效,使用正确且不同的 tls 版本更新数据库,并在响应返回正文中不重复的 json。

目前的代码段如下:

tls 控制器:

...
@client_side_tls_bp.route('/<string:tls_profile_id>', methods=['PUT'])
def update_tls_profile_by_id(tls_profile_id):
    return update_entity_by_id(TlsProfileOperator, entity_name, tls_profile_id)
...

一般实体控制器:

...
def update_entity_by_id(operator, entity_name, entity_id):
    """flask route for updating a resource"""
    try:
        entity_body = request.get_json()
    except Exception:
        return make_custom_response("Bad Request", HTTPStatus.BAD_REQUEST)

    entity_obj = operator.get(g.tenant, entity_id, g.correlation)
    if not entity_obj:
        response = make_custom_response(http_not_found_message(entity_name, entity_id), HTTPStatus.NOT_FOUND)
    else:
        updated = operator.update(g.tenant, entity_id, entity_body, g.correlation)
        if updated == "accepted":
            response = make_custom_response("Accepted", HTTPStatus.ACCEPTED)
        else:
            response = make_custom_response(updated, HTTPStatus.OK)

    return response
...

tls 运算符:

...
@staticmethod
def get(tenant, name, correlation_id=None):
    try:
        tls_profile = TlsProfile.get_by_name(tenant, name)
        return schema.dump(tls_profile)
    except NoResultFound:
        return None
    except Exception:
        apm_logger.error(f"Failed to get {name} TLS profile", tenant=tenant,
                         consumer=LogConsumer.customer, correlation=correlation_id)
        raise

@staticmethod
def update(tenant, name, json_data, correlation_id=None):

    schema.load(json_data)

    try:
        dependant_vs_names = VirtualServiceOperator.get_dependant_vs_names_locked_by_client_side_tls(tenant, name)
        # locks virtual services and tls profile table simultaneously

        to_update = TlsProfile.get_by_name(tenant, name)
        to_update.update(json_data, commit=False)
        db.session.flush()  # TODO - need to change when 2 phase commit will be implemented
        snapshots = VirtualServiceOperator.get_snapshots_dict(tenant, dependant_vs_names)

        # update QWE
        # TODO handle QWE update atomically!
        for snapshot in snapshots:
            QWEController.update_abc_services(tenant, correlation_id, snapshot)

        db.session.commit()
        apm_logger.info(f"Update successfully {len(dependant_vs_names)} virtual services", tenant=tenant,
                        correlation=correlation_id)
        return schema.dump(to_update)
    except Exception:
        db.session.rollback()
        apm_logger.error(f"Failed to update {name} TLS profile", tenant=tenant,
                         consumer=LogConsumer.customer, correlation=correlation_id)
        raise
...

在 api 模式类中:

...
@validates('_tls_versions')
def validate_client_side_tls_versions(self, value):
    if len(noDuplicatatesList) < 1:
        raise ValidationError("At least a single TLS version must be provided")
    for tls_version in noDuplicatatesList:
        if tls_version not in TlsProfile.allowed_tls_version_values:
            raise ValidationError("Not a valid TLS version")
...

我宁愿在架构级别解决问题,所以它不会接受重复。

那么,尽管从“value”参数值中删除重复项很简单,但我如何才能将非重复项列表传播回来以便使用它来更新数据库和响应?

谢谢。

【问题讨论】:

    标签: python python-3.x flask sqlalchemy marshmallow


    【解决方案1】:

    我没有测试,但我认为在验证函数中改变 value 会起作用。

    但是,marshmallow 的 API 并不能真正保证这一点。

    正确的做法是添加一个post_load 方法来进行重复数据删除。

        @post_load
        def deduplicate_tls(self, data, **kwargs):
            if "tls_versions" in data:
                data["tls_version"] = list(set(data["tls_version"]))
            return data
    

    这不会维护订单,所以如果订单很重要,或者与重复数据删除本身相关的问题,请参阅https://stackoverflow.com/a/7961390/4653485

    【讨论】:

    • 谢谢杰罗姆!看起来是一个非常有趣的方向。我明天早上在办公室检查一下。
    • 我成功添加了post_load方法,但是我没有看到在这个方法之外更新原始数据。意思是,传递给 schema.load(json_data) 的 json_data 保持不变。我希望它没有重复。有没有办法做到这一点?
    • Jerome,最终的解决方案是最终使用 pre_load 而不是 post_load。根据需要使用 pre_load 更新了 json_data 。你可以更新你的答案,我很乐意接受它有问题的答案。
    • 它应该适用于post_load。使用pre_load 意味着在验证之前进行,因此您必须处理所有潜在问题 -> 重复验证以避免崩溃。
    猜你喜欢
    • 1970-01-01
    • 2014-08-28
    • 2018-10-22
    • 2022-10-18
    • 2021-07-13
    • 2012-12-29
    • 1970-01-01
    • 2017-09-10
    • 2016-01-22
    相关资源
    最近更新 更多