¿Cómo forzar a FormRequest a devolver json en Laravel 5.1?

8 minutos de lectura

¿Cómo forzar a FormRequest a devolver json en Laravel 5.1?
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?

  • 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.

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.

¿Cómo forzar a FormRequest a devolver json en Laravel 5.1?
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:

  1. 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);
        }
    }
    
  2. Agregue el middleware a su $routeMiddleware mesa de tu app/Http/Kernel.php Archivo

    protected $routeMiddleware = [
        'auth'       => AppHttpMiddlewareAuthenticate::class,
        'guest'      => AppHttpMiddlewareRedirectIfAuthenticated::class,
        'jsonify'    => AppHttpMiddlewareJsonify::class
    ];
    
  3. 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.


¿Cómo forzar a FormRequest a devolver json en Laravel 5.1?
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.

ingrese la descripción de la imagen aquí

¿Cómo forzar a FormRequest a devolver json en Laravel 5.1?
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.

.

¿Ha sido útil esta solución?