简体   繁体   中英

Laravel Route Filter not Updating Response

I am using the lucadegasperi/oauth2-server-laravel package for OAuth in my API (a Laravel 4.1.* app) and it provides a filter to easily verify authorization before running a route like this:

Route::group(array('prefix' => 'api', 'before' => 'oauth:auth'), function() {
   // My API Routes
});

It returns a JSON response that doesn't follow the format I am using in my API and would like to change it to be consistent. So I created a filter in filters.php and set it to run as an after filter.

Route::group(array('prefix' => 'core', 'before' => 'oauth:auth', 'after' => 'oauth.cleanresponse'), function() {
   // My API Routes
)};

And the filter:

/**
* OAuth Package returns JSON response in custom format that is not consistent with
* Core API output. We need to alter the output to fit the standard response.
*/
Route::filter('oauth.cleanresponse', function($request, $response) {

   if ($response instanceof Illuminate\Http\JsonResponse)
   {
      $responseData = $response->getData();
      if (isset($responseData->error_message));
      {
        $newResponse = new API\ApiResponse();
        $newResponse->setError($responseData->error_message);
        $newResponse->setCode($responseData->status);
        return Response::json($newResponse->error(), $responseData->status);      
      }
   }

   });

The filter runs fine, and I can var_dump() out my changes just before returning them.

But the value returned from the API call in the response is not my new value, it's still the original value created in the before filter by the oauth library.

TL;DR; Why does the response from an after filter not override the response from a before filter and how can I work around this issue?

NOTE: I do not want to edit the OAuth package because then anytime I do a composer update it might overwrite my changes.

EDIT

A closer inspection of the Laravel router ( Illuminate/Routing/Router.php ) has this:

/**
 * Dispatch the request to a route and return the response.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return mixed
 */
public function dispatchToRoute(Request $request)
{
    $route = $this->findRoute($request);

    $this->events->fire('router.matched', array($route, $request));

    // Once we have successfully matched the incoming request to a given route we
    // can call the before filters on that route. This works similar to global
    // filters in that if a response is returned we will not call the route.
    $response = $this->callRouteBefore($route, $request);

    if (is_null($response))
    {
        $response = $route->run($request);
    }

    $response = $this->prepareResponse($request, $response);

    // After we have a prepared response from the route or filter we will call to
    // the "after" filters to do any last minute processing on this request or
    // response object before the response is returned back to the consumer.
    $this->callRouteAfter($route, $request, $response);

    return $response;
}

It appears to block dispatching a request if the before filter returns a result and then it calls the after filter. The problem is it doesn't capture the response! It seems that this:

$this->callRouteAfter($route, $request, $response);

Should be something like this (although this specifically doesn't work):

$response = $this->callRouteAfter($route, $request, $response);

Can anyone think of a way around this?

You have to set the response in the current instance using

$json = 'prepare the json here';
// set and return response with new data
return $response->setContent($json);

"After" filters have 3 parameters and not 2: route, request and response ("before" filters only have 2: route and request, because there is no response at that stage).

In your code, your after filter function has only two parameters, so your $request contains a Route , and your $response contains a Request. You need to add $route as first parameter.

After that, you can then call $response->header() and $response->setContent() to modify the generated response.

"After" filters cannot return a new $response , they can only modify the one passed to them.

Sample working code:

Route::filter('api_headers', function($route, $request, $response) {
    $response->header('Access-Control-Allow-Origin', '*');
    $response->header('P3P', 'CP="IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA"');
});
Route::group(array('prefix'=>'api', 'after'=>'api_headers'), function()
{
    // ...
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM