没有不值得去解决的问题,也没有不值得去学习的技术!

在 yii2 中,一个接口,不希望暴露 SQL 错误在接口响应中,但是又希望暴露字段本身的验证错误在接口响应中,应该如何实现了?

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

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

PHP / Laravel / Yii2 老项目维护与长期技术支持

如果你的 PHP / Laravel / Yii2 项目已经上线,但遇到原开发离职、Bug 长期无人修复、接口不稳定、性能下降、代码难以接手等问题,可以联系我做一次远程技术排查。

适合以下情况:
✅ 老旧 PHP 系统无人维护
✅ Laravel / Yii2 项目 Bug 修复
✅ 后台管理系统小功能迭代
✅ RESTful API 接口排查
✅ MySQL / Redis / Nginx 性能问题
✅ 长期远程兼职维护

可先从一次小问题开始:
✅ 线上报错排查
✅ 接口异常分析
✅ 慢查询与性能瓶颈定位
✅ 代码结构初步评估
✅ 部署环境与日志检查

如需咨询,请联系我,并注明:PHP 维护咨询

联系方式:
Telegram:@shuijingwan
微信:13980074657
邮箱:shuijingwanwq@gmail.com

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理