验证错误 – 永夜 https://www.shuijingwanwq.com 没有不值得去解决的问题,也没有不值得去学习的技术! Sun, 17 May 2026 06:15:13 +0000 zh-Hans hourly 1 https://wordpress.org/?v=7.0 在 yii2 中,一个接口,不希望暴露 SQL 错误在接口响应中,但是又希望暴露字段本身的验证错误在接口响应中,应该如何实现了? https://www.shuijingwanwq.com/2025/06/19/9154/ https://www.shuijingwanwq.com/2025/06/19/9154/#respond Thu, 19 Jun 2025 01:39:24 +0000 https://www.shuijingwanwq.com/?p=9154 浏览量: 94

1、现有的实现如下,存在字段本身的验证失败后,接口响应中永远是:数据库操作失败,请联系管理员,用户体验不友好。如图1

现有的实现如下,存在字段本身的验证失败后,接口响应中永远是:数据库操作失败,请联系管理员,用户体验不友好。

图1


        if ($animation->save()) {
            return [
                'code' => 10000,
                'message' => '设置动画属性成功',
                'data' => $animation,
            ];
        }
        return [
            'code' => 10004,
            'message' => '数据库操作失败,请联系管理员',
        ];


2、希望如下实现,1、不暴露数据库异常(如 SQL 错误、PDO 异常等) —— 避免安全风险。2、保留模型字段的验证错误(如 required、string 长度等) —— 给前端明确反馈。

3、实现 可复用的 BaseController::safeSave() 方法,可以帮助你在 Yii2 中统一处理模型保存,确保:字段验证错误会返回清晰提示;SQL 或系统异常不会暴露给前端,而是记录日志并返回友好消息。



    /**
     * 安全保存模型:支持字段验证错误输出,系统异常捕获
     *
     * @param ActiveRecord $model 模型实例
     * @param array $options 额外参数支持:
     *    - 'successMessage' 成功时返回的提示信息
     *    - 'failMessage' 验证失败时返回的提示信息
     *    - 'exceptionMessage' 异常时返回的提示信息
     *    - 'isUpdate' 是否更新操作(true/false)
     *
     * @return array
     */
    protected function safeSave(ActiveRecord $model, array $options = []): array
    {
        $successMessage = $options['successMessage'] ?? '保存成功';
        $failMessage = $options['failMessage'] ?? '字段验证失败';
        $exceptionMessage = $options['exceptionMessage'] ?? '数据库操作失败,请联系管理员';
        $isUpdate = $options['isUpdate'] ?? false;

        try {
            if (!$model->validate()) {
                return [
                    'code' => 10200,
                    'message' => $failMessage . ': ' . ModelHelper::getFirstError($model),
                ];
            }

            if (!$isUpdate) {
                $model->save(false); // insert
            } elseif ($model->getDirtyAttributes()) {
                // 如果是更新操作,检查是否变动再 save(false)
                $model->save(false);
            }

            return [
                'code' => 10000,
                'message' => $successMessage,
                'data' => $model,
            ];
        } catch (Throwable $e) {
            Yii::error([
                'exception' => get_class($e),
                'message' => $e->getMessage(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
            ], 'application.model.save.exception');

            return [
                'code' => 10500,
                'message' => $exceptionMessage,
            ];
        }
    }


/api/helpers/ModelHelper.php

<?php
 
namespace api\helpers;
 
use yii\base\Model;
 
class ModelHelper
{
    /**
     * 获取模型验证失败时的第一个错误信息
     * @param Model $model
     * @return string
     */
    public static function getFirstError(Model $model): string
    {
        // 验证失败:$errors 是一个包含错误信息的数组
        $errors = $model->getErrors();
        $firstMessage = '验证失败';
        if (!empty($errors)) {
            // 获取第一个属性名
            $firstAttribute = array_key_first($errors);
            $firstFieldErrors = $errors[$firstAttribute] ?? [];
            // 获取第一个属性的第一个错误
            $firstMessage = is_array($firstFieldErrors) && isset($firstFieldErrors[0])
                ? $firstFieldErrors[0]
                : $firstMessage;
        }
 
        return $firstMessage;
    }
}

4、现有的实现如下,存在字段本身的验证失败后,接口响应:字段验证失败: 动画时长必须是一个数字。 符合预期。如图2

现有的实现如下,存在字段本身的验证失败后,接口响应:字段验证失败: 动画时长必须是一个数字。 符合预期

图2

]]>
https://www.shuijingwanwq.com/2025/06/19/9154/feed/ 0