0

I have token based authorization, for which i have did below changes.

In User model, override findIdentityByAccessToken() method as below.

public static function findIdentityByAccessToken($token, $type = null)
{
  $userlogin = Userdevices::find()->where(['access_token' => $token])->one();

  if ($userlogin == array()) {
      return null;
  } else {
      $User = Users::findOne(['id' => $userlogin->user_id]);
      if (!count($User))
      {
          return null;
      }
      else {
          $dbUser = [
              'id' => $User->id,
          ];
          return new static($dbUser);
      }
  }
}

In Controller, I add behaviors() as below.

public function behaviors()
{
    $behaviors[] = [
        'class' => \yii\filters\ContentNegotiator::className(),
        'formats' => [
            'application/json' => \yii\web\Response::FORMAT_JSON,
        ],
    ];

    $behaviors['authenticator'] = [
        'class' => HttpBearerAuth::className(),
    ];

    return $behaviors;
}

When API does not get token or token is not valid it gives below response

{
    "name": "Unauthorized",
    "message": "You are requesting with an invalid credential.",
    "code": 0,
    "status": 401,
    "type": "yii\\web\\UnauthorizedHttpException"
}

I want to change response as per my requirement as below.

{
    "code": 401,
    "name": "Unauthorized",
    "is_logout": "Y",
    "status": "error",
    "message": "logout"
}
Yasin Patel
  • 5,624
  • 8
  • 31
  • 53

1 Answers1

3

You can change format of response using beforeSend event of yii\web\Response.

For example add following methods in your api controller:

public function init()
{
    parent::init();

    \Yii::$app->response->on(
        \yii\web\Response::EVENT_BEFORE_SEND,
        [$this, 'beforeResponseSend']
    );
}

public function beforeResponseSend(\yii\base\Event $event)
{
    /**
     * @var \yii\web\Response $response
     */
    $response = $event->sender;
    if ($response->data['status'] == 401) {
        $response->data = [
            'code' =>  401,
            'name' => 'Unauthorized',
            'is_logout' => 'Y',
            'status' => 'error',
            'message' => 'logout',
        ];
    }
}

The init method of controller registers the beforeSend event. The beforeResponseSend method handles the event and changes the response format.

If you want to format response in multiple controller it might be better to put the event handler into own class for example

namespace app\components;

class ErrorResponseHelper
{
    public static function beforeResponseSend(Event $event)
    {
        // ... formating code ...
    }
}

And register the event in config/web.php

return [
    // ...
    'components' => [
        'response' => [
            'class' => 'yii\web\Response',
            'on beforeSend' => [
                \app\components\ErrorResponseHelper::class,
                'beforeResponseSend',
            ],
        ],
    ],
];     

But be careful with this solution because this way the \app\components\ErrorResponseHelper::beforeResponseSend will be called during each request.

Michal Hynčica
  • 5,038
  • 1
  • 12
  • 24
  • @Micheal Thanks for the answer, I tried both the way but looks it is not working – Yasin Patel Nov 05 '19 at 05:33
  • @YasinPatel I've tested the first way with new yii2 basic app and it worked fine. Maybe it's not working because of some of your configuration? The second option is just another way to register the event, i haven't tested it but the way it's registered matches the [documentation](https://www.yiiframework.com/doc/guide/2.0/en/runtime-handling-errors#error-format) except the callback is given as class, method pair instead of closure. – Michal Hynčica Nov 05 '19 at 09:30
  • It is possible that this both are for Customizing Error Response – Yasin Patel Nov 05 '19 at 09:41
  • because here it send response before calling any method as user is not logged in – Yasin Patel Nov 05 '19 at 09:42
  • @YasinPatel When the request without proper Bearer token comes the processing should look like this (simplified): 1. The controller is created. The init() method is called during that process unless you've overriden constructor. 2. Filters are applied. `ContentNegotiator` sets format. `HttpBearerAuth` throws exception. 3. The error handler handles the exception and prepares the content of response. 4. Right before the response is send the beforeSend event is raised. 5. The response is sent. In my first variant the event is registered in step 1 and response is modified in step 4. – Michal Hynčica Nov 05 '19 at 12:19
  • I can think about two possible reasons why it might not work. First option is that you've overriden constructor of controller wihtou calling its parent. So the init() method is not called and the event handler is not registered. But in that case the second option should work. Other option is, that there is something done with `\yii\web\Response` that prevents raising event or removes the event handler registration before the event is raised. – Michal Hynčica Nov 05 '19 at 12:24
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/201878/discussion-between-yasin-patel-and-michal-hyncica). – Yasin Patel Nov 05 '19 at 12:25