Chung
Estoy usando FormularioSolicitud para validar desde el cual se envía en una llamada API desde la aplicación de mi teléfono inteligente. Entonces, quiero que FormRequest siempre devuelva json cuando falle la validación.
Vi el siguiente código fuente del marco de Laravel, el comportamiento predeterminado de FormRequest es devolver json si el reqeust es Ajax o wantJson.
//IlluminateFoundationHttpFormRequest class
/**
* Get the proper failed validation response for the request.
*
* @param array $errors
* @return SymfonyComponentHttpFoundationResponse
*/
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);
}
Sabía que puedo agregar Accept= application/json
en el encabezado de la solicitud. FormRequest devolverá json. Pero quiero proporcionar una forma más fácil de solicitar mi API mediante el soporte json de forma predeterminada sin configurar ningún encabezado. Entonces, traté de encontrar algunas opciones para forzar la respuesta json de FormRequest en IlluminateFoundationHttpFormRequest
clase. Pero no encontré ninguna opción que sea compatible de forma predeterminada.
Solución 1: Clase abstracta de solicitud de sobrescritura
Traté de sobrescribir la clase abstracta de solicitud de mi aplicación como sigue:
<?php
namespace Laravel5CgHttpRequests;
use IlluminateFoundationHttpFormRequest;
use IlluminateHttpJsonResponse;
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 SymfonyComponentHttpFoundationResponse
*/
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);
}
}
yo añadí protected $forceJsonResponse = false;
para establecer si necesitamos forzar la respuesta json o no. Y, en cada FormRequest que se extiende desde la clase abstracta Request. Yo configuro esa opción.
Por ejemplo: hice un StoreBlogPostRequest y configuré $forceJsoResponse=true
para este FormRequest y haz que responda json.
<?php
namespace Laravel5CgHttpRequests;
use Laravel5CgHttpRequestsRequest;
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',
];
}
}
Solución 2: Agregue un Middleware y fuerce el encabezado de solicitud de cambio
Construyo un middleware como los siguientes:
namespace Laravel5CgHttpMiddleware;
use Closure;
use SymfonyComponentHttpFoundationHeaderBag;
class AddJsonAcceptHeader
{
/**
* Add Json HTTP_ACCEPT header for an incoming request.
*
* @param IlluminateHttpRequest $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);
}
}
Es trabajo. Pero me pregunto si estas soluciones son buenas. ¿Y hay alguna forma Laravel de ayudarme en esta situación?
Me sorprende por qué esto es tan difícil de hacer en Laravel. Al final, en base a su idea de anular la clase Solicitud, se me ocurrió esto.
app/Http/Requests/ApiRequest.php
<?php
namespace AppHttpRequests;
class ApiRequest extends Request
{
public function wantsJson()
{
return true;
}
}
Luego, en cada controlador simplemente pase AppHttpRequestsApiRequest
public function index(ApiRequest $request)
-
Aceptar: application/json en su encabezado también puede ayudar con esto.
– Garabatos.
12 dic. 16 a las 22:49
-
Me encontré con este problema mientras probaba una aplicación 5.4. Simplemente agregando este método a un solicitud de formulario Hace el truco.
– craig_h
5 de agosto de 2017 a las 15:29
-
“Me sorprende lo difícil que es esto”…….”aquí hay una manera súper fácil de hacerlo”, lol, eso me hizo reír. +1
–Wesley Smith
23 sep 2017 a las 20:10
-
No funciona: dicha solicitud tiene vacío
$request->all()
– Delicadeza
30 ene.
-
Esto funciona en el sentido más binario de que funciona o no funciona, pero es una solución realmente mediocre teniendo en cuenta lo bien redactada que estaba la pregunta.
– Tarek Adán
21 abr.
simondepelchin
Sé que esta publicación es un poco antigua, pero acabo de crear un Middleware que reemplaza el encabezado “Aceptar” de la solicitud con “aplicación/json”. Esto hace que el wantsJson()
retorno de función true
cuando se usa. (Esto fue probado en Laravel 5.2 pero creo que funciona igual en 5.1)
Así es como lo implementas:
-
Crear el archivo
app/Http/Middleware/Jsonify.php
namespace AppHttpMiddleware; use Closure; class Jsonify { /** * Change the Request headers to accept "application/json" first * in order to make the wantsJson() function return true * * @param IlluminateHttpRequest $request * @param Closure $next * * @return mixed */ public function handle($request, Closure $next) { $request->headers->set('Accept', 'application/json'); return $next($request); } }
-
Agregue el middleware a su
$routeMiddleware
mesa de tuapp/Http/Kernel.php
Archivoprotected $routeMiddleware = [ 'auth' => AppHttpMiddlewareAuthenticate::class, 'guest' => AppHttpMiddlewareRedirectIfAuthenticated::class, 'jsonify' => AppHttpMiddlewareJsonify::class ];
-
Finalmente úsalo en tu
routes.php
como lo haría con cualquier middleware. En mi caso se ve así:Route::group(['prefix' => 'api/v1', 'middleware' => ['jsonify']], function() { // Routes });
-
Tengo el mismo problema y pensé en probar esta solución. El problema que tengo con esto es que cuando mi método de controlador carga mi instancia de FormRequest implícitamente, no es la misma instancia de solicitud que la modificada en el middleware Jsonify, por lo que wantJson se “restablece” efectivamente a falso.
– Jaime
12 feb. 16 a las 16:04
-
FormRequest extiende la Solicitud, por lo que debería ser la misma instancia, tal vez mostrar algún código
– Simon Depelchin
14 feb. 16 a las 13:43
-
@SimonDepelchin Esta solución es como la Solución 2 que mencioné en la pregunta.
– Chung
17 feb.
-
Sí lo es, con más detalles y más en el “camino de Laravel” en mi humilde opinión.
– Simon Depelchin
17 de febrero de 2016 a las 12:15
-
Esta solución es mejor ya que devuelve todo como JSON. Si realiza una solicitud no autorizada con
ApiRequest
devolverá una página html 404, sin embargo, esto devolverá un error JSON no autorizado 401.– Ryan
12 oct.
T30
Según la respuesta de ZeroOne, si está utilizando Solicitud de formulario de validación puede anular el método de validación fallida para devolver siempre json en caso de falla de validación.
Lo bueno de esta solución es que no está anulando todas las respuestas para devolver json, sino solo las fallas de validación. Entonces, para todas las demás excepciones de Php, aún verá la página de error amigable de Laravel.
namespace AppHttpRequests;
use IlluminateContractsValidationValidator;
use IlluminateFoundationHttpFormRequest;
use IlluminateHttpExceptionsHttpResponseException;
use SymfonyComponentHttpFoundationResponse;
class InventoryRequest extends FormRequest
{
protected function failedValidation(Validator $validator)
{
throw new HttpResponseException(response($validator->errors(), Response::HTTP_UNPROCESSABLE_ENTITY));
}
}
-
Solución clara, muchas gracias, funciona bien en Laravel 6.14
– Odin Thunder
24 feb. 2020 a las 20:10
-
Tengo este problema en Laravel 8 y este método me funciona. Gracias T30
– Máx.
05 dic. 21 en 09:51
si su solicitud tiene X-Request-With: XMLHttpRequest encabezado o aceptar el tipo de contenido como aplicación/json FormRequest devolverá automáticamente una respuesta json que contiene los errores con un estado de 422.
Cero uno
simplemente anulo el failedValidation
función
protected function failedValidation(Validator $validator)
{
if ($this->wantsJson()) {
throw new HttpResponseException(
Response::error(__('api.validation_error'),
$validator->errors(),
470,
[],
new ValidationException)
);
}
parent::failedValidation($validator);
}
Así que mi muestra de salida personalizada como a continuación:
{
"error": true,
"message": "Validation Error",
"reference": [
"The device id field is required.",
"The os version field is required.",
"The apps version field is required."
],
}
Por cierto, Respuesta:: el error no existe en laravel. Estoy usando macroable para crear un nuevo método
Response::macro('error', function ($msg = 'Something went wrong', $reference = null, $code = 400, array $headers = [], $exception = null) {
return response()->json(//custom here);
});
-
¿Puedes compartir el espacio de nombres completamente clasificado de
Response
clase– Manoj Kiran Appathurai
10 sep.
-
@Manojkiran.A IlluminateSupportFachadasRespuesta.
– Cero uno
25 oct.
-
¿Puedes compartir el espacio de nombres completamente clasificado de
Response
clase– Manoj Kiran Appathurai
10 de septiembre de 2019 a las 7:42
-
@Manojkiran.A IlluminateSupportFachadasRespuesta.
– Cero uno
25 oct.
.
Hola, solo como una sugerencia, ya que está solicitando un tipo de respuesta predeterminado, entonces, ¿por qué no simplemente agregando un middleware y agregando el tipo de solicitud a json en su método de manejo?
$request->header('accept', 'application/json'); return $next($request);
con estos, tiene un lugar para hacer una mayor expansión, sin anular siempre ningún método– terry bajo
20 jul.
Gracias ! Es una buena idea. Creo. Actualizaré esta implementación en la pregunta anterior.
– Chung
20 jul.
Lo siento. Traté de configurar $request->header(‘Accept’,’application/json’); en un middleware, pero descubrí que mi solicitud tiene el encabezado Aceptar predeterminado ‘/‘, por lo que no pude sobrescribir ese encabezado Aceptar. No puse nada en mi solicitud.
– Chung
20 jul.
no importa, ya sea que tenga el valor de aceptación predeterminado establecido en su encabezado, el valor del middleware lo anulará
$request = $request->header('Accept','application/json'); return $next($request);
Estoy pensando en eso, la solicitud no se mantiene.– terry bajo
20 jul.
Encontré la forma de sobrescribir el encabezado de la solicitud. Necesitamos configurar $request->server y reconstruir headerBag de la siguiente manera: $request->server->set(‘HTTP_ACCEPT’, ‘application/json’); $solicitud->encabezados = new HeaderBag($solicitud->servidor->getHeaders());
– Chung
21 jul.