Call Laravel controller from code and pass HTTP headers to it

谁都会走 提交于 2019-12-12 04:13:55

问题


To call a controller I use a CURL request like this:

curl -kX POST https://site/method  -H 'Authorization: Bearer ACCESS_TOKEN\
Content-Type:application/json' -d '
{
    "param1":"value 1",
    "param2":"value 2",
    "param3":"value 3"
}'

The route calls UserController@method.

How can I do the same from Laravel code (internal request, not sending CURL)?

I've found an advice to use something like this

        $controller = app()->make($controllerName);
        $response = app()->call([$controller, 'method'], []);
        echo $response;

where the last [] should contain some parameters. But if this is the way, I cannot figure out how the array should look in my case.

P.S. Please, don't answer "it's a bad practice" instead of giving an advice how to implement what I need.


回答1:


I do not see a simple solution to do this. It's not a good idea to call a controller from other methods. The controller responds to the request and should not be used as trivial class. If you need to test the controller, laravel provides methods for testing, including calling the controller. https://laravel.com/docs/5.4/testing. If you need to call some method in several places - better define it in model, or repository and then call it where you want.




回答2:


Here is my solution (Laravel 5.4). Inspired by https://stackoverflow.com/a/40366119/518704 but needed much time to dig into the code to find how to pass my Authorization: Bearer token

I need this approach to authorize in a browser-less socket connection (Ratchet), where JS code is not running. So I could not use Ratchet WebSocket, only simple socket Ratchet IoServer. My API controller returns JSON so it's easy to work with the response further. I need this approach mainly to authenticate the socket connection assuming the client knows the access token when one opens the socket (got by REST on login).

First of all I need to use a trait, used in Tests

Next in onMessage code I assume a JSON message is passed with the access token. To simplify here I don't write JSON validity check etc.

namespace App;

use Ratchet\ConnectionInterface;
use Askedio\LaravelRatchet\RatchetServer as RatchetServerBase;

class RatchetServer extends RatchetServerBase
{
...
    public function onMessage(ConnectionInterface $conn, $input)
    {
          $input = json_decode($input);
          $token = $input->token;


          // Some data (may be taken from JSON $input)
          $data = [
            "param1" => "value 1",
            "param2" => "value 2",
            "param3" => "value 3"
          ];

          // Prepare the server headers
          $server = ['HTTP_AUTHORIZATION' => 'Bearer ' . $token];

          // This way! If calling app() here, it reuses the same Auth::user() 
          $app = require __DIR__.'/../bootstrap/app.php';
          $kernel = $app->make(\Illuminate\Contracts\Http\Kernel::class);
          $response = $kernel->handle(
              $request = \Illuminate\Http\Request::create('/user', 'GET', $data, [], [], $server)
          );
          // ~ var_dump(Auth::id());
          $controllerResult = $response->getContent();
          $kernel->terminate($request, $response);

          // My controller hidden under /user route is an API one and returns JSON string
          echo $controllerResult;
    }
...
}

My previous solution didn't work. The Auth::user() always returned the first logged in user (app() seemed to use singleton). So I leave the previous code for reference purpose.

namespace App;

use Ratchet\ConnectionInterface;
use Askedio\LaravelRatchet\RatchetServer as RatchetServerBase;

class RatchetServer extends RatchetServerBase
{
    use \Illuminate\Foundation\Testing\Concerns\MakesHttpRequests;

...
    public function onMessage(ConnectionInterface $conn, $input)
    {
          $input = json_decode($input);
          $token = $input->token;

          $data = [
            "param1" => "value 1",
            "param2" => "value 2",
            "param3" => "value 3"
          ];

          // The 2 lines below can me moved to the __constructor method
          $this->baseUrl = request()->getSchemeAndHttpHost();
          $this->app     = app();

          // Prepare the server headers
          $server = ['HTTP_AUTHORIZATION' => 'Bearer ' . $token];

          // Call my controller by route (this seems to be internal call)
          $response = $this->call('GET','/method', $data, [], [], $server)->json();
    }
...
}

P.S. Just a note for myself when I come here later. Too see available headers and proper naming I did a var_dump in for $this->parameters ./vendor/symfony/http-foundation//ServerBag.php



来源:https://stackoverflow.com/questions/44982067/call-laravel-controller-from-code-and-pass-http-headers-to-it

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!