J'utilise FormRequest pour valider à partir duquel est envoyé un appel API depuis mon application smartphone. Donc, je veux que FormRequest retourne toujours json lorsque la validation échoue.
J'ai vu le code source suivant de Laravel framework, le comportement par défaut de FormRequest est return json si reqeust est Ajax ou wantJson.
//Illuminate\Foundation\Http\FormRequest class
/**
* Get the proper failed validation response for the request.
*
* @param array $errors
* @return \Symfony\Component\HttpFoundation\Response
*/
public function response(array $errors)
{
if ($this->ajax() || $this->wantsJson()) {
return new JsonResponse($errors, 422);
}
return $this->redirector->to($this->getRedirectUrl())
->withInput($this->except($this->dontFlash))
->withErrors($errors, $this->errorBag);
}
Je savais que je pouvais ajouter Accept= application/json
dans l'en-tête de la demande. FormRequest renverra json. Mais je veux fournir un moyen plus simple de demander mon API en prenant en charge json par défaut sans définir d'en-tête. J'ai donc essayé de trouver des options pour forcer la réponse json de FormRequest dans Illuminate\Foundation\Http\FormRequest
classe. Mais je n'ai trouvé aucune option prise en charge par défaut.
J'ai essayé d'écraser ma classe abstraite de demande d'application comme suit:
<?php
namespace Laravel5Cg\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\JsonResponse;
abstract class Request extends FormRequest
{
/**
* Force response json type when validation fails
* @var bool
*/
protected $forceJsonResponse = false;
/**
* Get the proper failed validation response for the request.
*
* @param array $errors
* @return \Symfony\Component\HttpFoundation\Response
*/
public function response(array $errors)
{
if ($this->forceJsonResponse || $this->ajax() || $this->wantsJson()) {
return new JsonResponse($errors, 422);
}
return $this->redirector->to($this->getRedirectUrl())
->withInput($this->except($this->dontFlash))
->withErrors($errors, $this->errorBag);
}
}
J'ai ajouté protected $forceJsonResponse = false;
pour définir si nous devons forcer la réponse json ou non. Et, dans chaque FormRequest qui s'étend de la classe abstraite Request. J'ai défini cette option.
Par exemple: j'ai créé un StoreBlogPostRequest et défini $forceJsoResponse=true
pour cette FormRequest et en faire une réponse json.
<?php
namespace Laravel5Cg\Http\Requests;
use Laravel5Cg\Http\Requests\Request;
class StoreBlogPostRequest extends Request
{
/**
* Force response json type when validation fails
* @var bool
*/
protected $forceJsonResponse = true;
/**
* 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 [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
];
}
}
Je construis un middleware comme les suivants:
namespace Laravel5Cg\Http\Middleware;
use Closure;
use Symfony\Component\HttpFoundation\HeaderBag;
class AddJsonAcceptHeader
{
/**
* Add Json HTTP_ACCEPT header for an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$request->server->set('HTTP_ACCEPT', 'application/json');
$request->headers = new HeaderBag($request->server->getHeaders());
return $next($request);
}
}
C'est du travail. Mais je me demande si ces solutions sont bonnes? Et y a-t-il un Laravel moyen de m'aider dans cette situation?
Cela m'embarrasse de savoir pourquoi c'est si difficile à faire à Laravel. En fin de compte, sur la base de votre idée de remplacer la classe Request, j'ai trouvé cela.
app/Http/Requests/ApiRequest.php
<?php
namespace App\Http\Requests;
class ApiRequest extends Request
{
public function wantsJson()
{
return true;
}
}
Ensuite, dans chaque contrôleur, passez simplement \App\Http\Requests\ApiRequest
public function index(ApiRequest $request)
Je sais que ce message est un peu ancien, mais je viens de créer un middleware qui remplace l'en-tête "Accept" de la demande par "application/json". Cela fait que la fonction wantsJson()
retourne true
lorsqu'elle est utilisée. (Cela a été testé en Laravel 5.2 mais je pense que cela fonctionne de la même façon en 5.1)
Voici comment vous implémentez cela:
Créez le fichier app/Http/Middleware/Jsonify.php
namespace App\Http\Middleware;
use Closure;
class Jsonify
{
/**
* Change the Request headers to accept "application/json" first
* in order to make the wantsJson() function return true
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle($request, Closure $next)
{
$request->headers->set('Accept', 'application/json');
return $next($request);
}
}
Ajoutez le middleware à votre $routeMiddleware
tableau de votre app/Http/Kernel.php
fichier
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'jsonify' => \App\Http\Middleware\Jsonify::class
];
Enfin, utilisez-le dans votre routes.php
comme pour tout middleware. Dans mon cas, cela ressemble à ceci:
Route::group(['prefix' => 'api/v1', 'middleware' => ['jsonify']], function() {
// Routes
});
Sur la base de réponse de ZeroOne , si vous utilisez validation de demande de formulaire , vous pouvez remplacer la méthode failedValidation pour toujours renvoyer json en cas d'échec de la validation.
La bonne chose à propos de cette solution, c'est que vous ne remplacez pas toutes les réponses pour renvoyer json, mais seulement les échecs de validation. Donc, pour toutes les autres exceptions Php, vous verrez toujours la page d'erreur conviviale Laravel.
namespace App\Http\Requests;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException;
use Symfony\Component\HttpFoundation\Response;
class InventoryRequest extends FormRequest
{
protected function failedValidation(Validator $validator)
{
throw new HttpResponseException(response($validator->errors(), Response::HTTP_UNPROCESSABLE_ENTITY));
}
je remplace simplement la fonction failedValidation
protected function failedValidation(Validator $validator)
{
if ($this->wantsJson()) {
// flatten all the message
$collection = collect($validator->errors())->flatten()->values()->all();
throw new HttpResponseException(Response::error('Validation Error', $collection));
}
parent::failedValidation($validator);
}
Échantillon de sortie:
{
"error": true,
"message": "Validation Error",
"reference": [
"The device id field is required.",
"The os version field is required.",
"The apps version field is required."
],
}