验证失败 returns 默认错误消息,即使提供了自定义消息

Failed validation returns default error message even though custom messages supplied

我没有收到预期的回复。

这是位置网络服务请求的控制器代码:

<?php
namespace App\Http\Controllers;

use App\Location;
use Illuminate\Http\Request;


class LocationController extends Controller
{


    /**
     * Action method to add a location with the supplied Data
     * 
     * @param \Illuminate\Http\Request $p_oRequest Request
     * 
     * @return JSON
     */
    public function add(Request $p_oRequest)
    {

        try {

            $p_oRequest->validate(
                array(
                    'name' => 'required|alpha_num',
                    'user_id' => 'required|integer',
                ),
                array(
                    'name.required' => 'Name is required',
                    'name.string' => 'Name must be alphanumeric',
                    'user_id.required' => 'Curator User Id is required',
                    'user_id.required' => 'Curator User Id must be an integer',
                )
            );

        } catch (\Exception $ex) {

            $arrResponse = array(
                'result' => 0,
                'reason' => $ex->getMessage(),
                'data' => array(),
                'statusCode' => 404
            );

        } finally {

            return response()->json($arrResponse);

        }

    }

}

请求是http://mydomain/index.php/api/v1/location/add?name=@!^

我期望的响应原因是:{ "result": 0, "reason": "Name must be alphanumeric", "data": [], "statusCode": 404 }

我得到的实际响应是:{ "result": 0, "reason": "The given data was invalid.", "data": [], "statusCode": 404 }

请帮忙。这让我很烦。

您的消息应该是验证规则,所以不是:

'name.required' => 'Name is required',
'name.string' => 'Name must be alphanumeric',
'user_id.required' => 'Curator User Id is required',
'user_id.required' => 'Curator User Id must be an integer',

你应该有:

'name.required' => 'Name is required',
'name.alpha_num' => 'Name must be alphanumeric',
'user_id.required' => 'Curator User Id is required',
'user_id.integer' => 'Curator User Id must be an integer',

我终于发现了为什么这不起作用。这不是实现代码或 Laravel 中的错误问题,而是其中之一:(i)。编写好的 PHP 代码来处理不言而喻的结果,这显然是我没有做的; (ii). Laravel 中关于如何实际使用验证错误响应的文档不足。任你选。

Laravel 的验证抛出 Illuminate\Validation\ValidationError。信不信由你,这实际上将错误消息默认为“给定的数据无效。”,因此当您捕获 \Exception 并检索其 $e->getMessage() 时,此默认错误消息就是您(正确地)得到.

您需要做的是捕获 \Illuminate\Validation\ValidationError - 我本来应该做的,呃! - 然后使用它的方法来帮助您从中提取错误消息。

这是我想出的解决方案:

<?php
namespace App\Http\Controllers;

use App\Location;
use Illuminate\Http\Request;


class LocationController extends Controller
{

    /**
     * Action method to add a location with the supplied Data
     * 
     * @param \Illuminate\Http\Request $p_oRequest Request
     * 
     * @return JSON
     */
    public function add(Request $p_oRequest)
    {
        try {
        
            $arrValid = array(
                'name' => 'required|alpha_num',
                'user_id' => 'required|integer',
            );
            $p_oRequest->validate(
                $arrValid,
                array(
                    'name.required' => 'Name is missing',
                    'name.alpha_num' => 'Name must be alphanumeric',
                    'user_id.required' => 'User Id is missing',
                    'user_id.integer' => 'User Id must be an integer',
                )
            );
            
        } catch (\Illuminate\Validation\ValidationException $e ) {
        
            /**
             * Validation failed
             * Tell the end-user why
             */
            $arrError = $e->errors(); // Useful method - thank you Laravel
            /**
             * Compile a string of error-messages
             */
            foreach ($arrValid as $key=>$value ) {
                $arrImplode[] = implode( ', ', $arrError[$key] );
            }
            $message = implode(', ', $arrImplode);
            /**
             * Populate the respose array for the JSON
             */
            $arrResponse = array(
                'result' => 0,
                'reason' => $message,
                'data' => array(),
                'statusCode' => $e->status,
            );

        } catch (\Exception $ex) {

            $arrResponse = array(
                'result' => 0,
                'reason' => $ex->getMessage(),
                'data' => array(),
                'statusCode' => 404
            );

        } finally {

            return response()->json($arrResponse);

        }
        
    }
    
}

所以,确实,Laravel 提供了正确的响应,并且按照罐子侧面的说明做了,但我没有正确应用它。无论如何,为了帮助我和其他在 Laravel-sea 迷路的 PHP-marines,我提供解决方案。

此外,感谢 Marcin 指出了我的错误编码,即使我实施了上述解决方案也会导致问题。

问题可能是 Laravel 的默认异常处理程序未准备好将详细的验证信息传递回用户。相反,它向用户隐藏了异常详细信息,这通常是正确的做法,因为它可能对除验证异常之外的其他异常构成安全风险。

换句话说;如果异常处理程序的 render 函数(在 /app/Exceptions/Handler.php 中实现)捕获您的验证错误,它们将被解释为一般应用程序异常,并且向用户转发的一般错误消息将始终显示为 'The given data was invalid' .

确保 render 方法忽略 \Illuminate\Validation\ValidationException 的实例,您应该得到预期的响应:

public function render($request, Exception $exception) {

    if (! $exception instanceof \Illuminate\Validation\ValidationException)) {

        // ... render code for other Exceptions here

    }

}

使异常处理程序在响应中传递 ValidationException 详细信息的另一种方法是在 render 方法中执行类似的操作:

if ($exception instanceof ValidationException && $request->expectsJson()) {
    return response()->json(['message' => 'The given data was invalid.', 'errors' => $exception->validator->getMessageBag()], 422);
}

背景

Laravel 在这里基本上(滥用)使用异常。通常异常表示代码中的(运行时)问题,但 Laravel 将它们用作促进请求验证和向用户提供反馈的机制。这就是为什么在这种情况下,让您的异常处理程序处理异常是不正确的——它不是应用程序异常,它是为用户准备的信息。

OP 提供的答案中的代码有效,因为他自己捕获了 ValidationException,防止它被应用程序的异常处理程序捕获。我认为没有任何场景是需要的,因为它是一个明显的关注点组合,并且会产生非常长且不可读的代码。像我上面展示的那样简单地忽略 ValidationExceptions 或在异常处理程序中以不同的方式对待它们应该就可以了。

我刚刚才看到这个,但您需要做的就是将验证调用 移动到 之前 try/catch

$p_oRequest->validate(
    [
        'name'    => 'required|alpha_num',
        'user_id' => 'required|integer',
    ],
    [
        'name.required'    => 'Name is required',
        'name.string'      => 'Name must be alphanumeric',
        'user_id.required' => 'Curator User Id is required',
        'user_id.required' => 'Curator User Id must be an integer',
    ]
);

try {

...

} catch(\Exception $e) {
    return back()->withErrors($e->getMessage())->withInput();
}

因为 Laravel 自动捕获验证异常并且 returns 你返回旧输入和一系列错误,你可以像

@if ($errors->any())
    <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

将响应放在变量中并使用 dd() 打印它。您会在“消息”方法中找到它。为我工作。

dd($response);