Laravel 5 how to validate route parameters?

2019-03-11 00:54发布

问题:

I want to validate the route parameters in the "form request" but don't know how to do it.

Below is the code sample, I am trying with:

Route

// controller Server
Route::group(['prefix' => 'server'], function(){
    Route::get('checkToken/{token}',['as'=>'checkKey','uses'=> 'ServerController@checkToken']);
});

Controller

namespace App\Http\Controllers;


use App\Http\Controllers\Controller;

use Illuminate\Http\Request;
use App\Http\Requests;


class ServerController extends Controller {

public function checkToken( \App\Http\Requests\CheckTokenServerRequest $request) // OT: - why I have to set full path to work??
    {

        $token = Token::where('token', '=', $request->token)->first();      
        $dt = new DateTime; 
        $token->executed_at = $dt->format('m-d-y H:i:s');
        $token->save();

        return response()->json(json_decode($token->json),200);
    }
}

CheckTokenServerRequest

namespace App\Http\Requests;

use App\Http\Requests\Request;

class CheckTokenServerRequest extends Request {

    //autorization

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {

        return [
            'token' => ['required','exists:Tokens,token,executed_at,null']
        ];
    }

}

But when I try to validate a simple url http://myurl/server/checkToken/222, I am getting the response: no " token " parameter set.

Is it possible to validate the parameters in a separate "Form request", Or I have to do all in a controller?

ps. Sorry for my bad English.

回答1:

The way for this is overriding all() method for CheckTokenServerRequest like so:

public function all() 
{
   $data = parent::all();
   $data['token'] = $this->route('token');
   return $data;
}

EDIT

Above solution works in Laravel < 5.5. If you want to use it in Laravel 5.5 or above, you should use:

public function all($keys = null) 
{
   $data = parent::all($keys);
   $data['token'] = $this->route('token');
   return $data;
}

instead.



回答2:

Override the all() function on the Request object to automatically apply validation rules to the URL parameters

class SetEmailRequest
{

    public function rules()
    {
        return [
            'email'    => 'required|email|max:40',
            'id'       => 'required|integer', // << url parameter
        ];
    }

    public function all()
    {
        $data = parent::all();
        $data['id'] = $this->route('id');

        return $data;
    }

    public function authorize()
    {
        return true;
    }
}

Access the data normally from the controller like this, after injecting the request:

$setEmailRequest->email // request data
$setEmailRequest->id, // url data


回答3:

The form request validators are used for validating HTML form data that are sent to server via POST method. It is better that you do not use them for validating route parameters. route parameters are mostly used for retrieving data from data base so in order to ensure that your token route parameter is correct change this line of your code, from

$token = Token::where('token', '=', $request->token)->first();

to

$token = Token::where('token', '=', $request->input(token))->firstOrFail();

firstOrFail() is a very good function, it sends 404 to your user, if the user insert any invalid token.

you get no " token " parameter set because Laravel assumes that your "token" parameter is a POST data which in your case it is not.

if you insist on validating your "token" parameter, by form request validators you gonna slow down your application, because you perform two queries to your db, one in here

$token = Token::where('token', '=', $request->token)->first();

and one in here

return [
            'token' => ['required','exists:Tokens,token,executed_at,null']
        ];

I suggest to use firsOrFail to do both validating and retrieving at once.



回答4:

A trait can cause this validation to be relatively automagic.

Trait

<?php

namespace App\Http\Requests;

/**
 * Class RouteParameterValidation
 * @package App\Http\Requests
 */
trait RouteParameterValidation{

    /**
     * @var bool
     */
    private $captured_route_vars = false;

    /**
     * @return mixed
     */
    public function all(){
        return $this->capture_route_vars(parent::all());
    }

    /**
     * @param $inputs
     *
     * @return mixed
     */
    private function capture_route_vars($inputs){
        if($this->captured_route_vars){
            return $inputs;
        }

        $inputs += $this->route()->parameters();
        $inputs = self::numbers($inputs);

        $this->replace($inputs);
        $this->captured_route_vars = true;

        return $inputs;
    }

    /**
     * @param $inputs
     *
     * @return mixed
     */
    private static function numbers($inputs){
        foreach($inputs as $k => $input){
            if(is_numeric($input) and !is_infinite($inputs[$k] * 1)){
                $inputs[$k] *= 1;
            }
        }

        return $inputs;
    }

}

Usage

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class MyCustomRequest extends FormRequest{
    use RouteParameterValidation;

    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize(){
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules(){
        return [
            //
            'any_route_param' => 'required'//any rule(s) or custom rule(s)
        ];
    }
}


回答5:

For \App\Http\Requests\CheckTokenServerRequest you can add use App\Http\Requests\CheckTokenServerRequest; at the top.
If you pass the token by url you can use it likes a variable in controller.

public function checkToken($token) //same with the name in url
{

    $_token = Token::where('token', '=', $token)->first();      
    $dt = new DateTime; 
    $_token->executed_at = $dt->format('m-d-y H:i:s');
    $_token->save();

    return response()->json(json_decode($token->json),200);
}


回答6:

If you dont want to specify each route param and just put all route params you can override like this:

public function all()
{
    return array_merge(parent::all(), $this->route()->parameters());
}


回答7:

You just missing the underscore before token. Replace with

_token

wherever you check it against the form generated by laravel.

public function rules()
{

    return [
        '_token' => ['required','exists:Tokens,token,executed_at,null']
    ];