yii2接口为何自动返回json格式和xml

    'components' => [
        'request' => [
            'class' => Request::class,
            'csrfParam' => "_csrf-{$moduleName}",
            'cookieValidationKey' => "{$moduleName}-{$cookieKey}",
            'parsers' => [
                'application/json' => 'yii\web\JsonParser',
            ],
        ],
        'response' => [
            'class' => 'yii\web\Response',
            'on beforeSend' => function ($event) {
                /** @var \yii\web\Response $response */
                $response = $event->sender;
                if (Yii::$app->controller->module->id == 'screen') {
                    // scree 模块不需要调整
                    return;
                }
                if ($response->data !== null && is_array($response->data)) {
                    $response->data = array_merge([
                        'status' => $response->statusCode,
                        'message' => $response->statusText,
                    ], $response->data);
                    $response->statusCode = 200;
                }
            },
        ],

我们写接口的时候会发现,并没有在组件response中设置返回格式,为啥还会自动返回json,原因在

yii\rest\Controller中的behaviors中的contentNegotiator
    public function behaviors()
    {
        return [
            'contentNegotiator' => [
                'class' => ContentNegotiator::className(),
                'formats' => [
                    'application/json' => Response::FORMAT_JSON,
                    'application/xml' => Response::FORMAT_XML,
                ],
            ],
            'verbFilter' => [
                'class' => VerbFilter::className(),
                'actions' => $this->verbs(),
            ],
            'authenticator' => [
                'class' => CompositeAuth::className(),
            ],
            'rateLimiter' => [
                'class' => RateLimiter::className(),
            ],
        ];
    }

进入contentNegotiator中会看到

protected function negotiateContentType($request, $response)
    {
        if (!empty($this->formatParam) && ($format = $request->get($this->formatParam)) !== null) {
            if (is_array($format)) {
                throw new BadRequestHttpException("Invalid data received for GET parameter '{$this->formatParam}'.");
            }

            if (in_array($format, $this->formats)) {
                $response->format = $format;
                $response->acceptMimeType = null;
                $response->acceptParams = [];
                return;
            }

            throw new UnsupportedMediaTypeHttpException('The requested response format is not supported: ' . $format);
        }

        $types = $request->getAcceptableContentTypes();// 获取你请求中带的contentType类型,一般我们postman是 *,浏览器访问是xml等之类的
        if (empty($types)) {
            $types['*/*'] = [];
        }

        foreach ($types as $type => $params) {
            if (isset($this->formats[$type])) {
                $response->format = $this->formats[$type];
                $response->acceptMimeType = $type;
                $response->acceptParams = $params;
                return;
            }
        }

        foreach ($this->formats as $type => $format) {
            $response->format = $format;
            $response->acceptMimeType = $type;
            $response->acceptParams = [];
            break;
        }

        if (isset($types['*/*'])) {
            return;
        }

        throw new UnsupportedMediaTypeHttpException('None of your requested content types is supported.');
    }

这样就可以解释为啥是json格式
有些小伙伴们发现,有时候接口抛出的异常是html有时是json,这是为啥呢?

1,在控制器中方法中抛出异常,此时为json格式,因为这个时候已经走到了yii\rest\Controller中的behaviors
2,方法找不到的时候抛出异常,此时为html格式,因为这个时候还没走yii\rest\Controller中的behaviors中contentNegotiator

3,在模块文件中增加behaviors,抛出异常也是html格式,因为这个时候还没走yii\rest\Controller中的behaviors中contentNegotiator

那么问题来了怎么让他统一返回json?

答:那就是在response中增加format为json

'response' => [
            'class' => 'yii\web\Response',
            'format' => \yii\web\Response::FORMAT_JSON,
            'on beforeSend' => function ($event) {
                /** @var \yii\web\Response $response */
                $response = $event->sender;
                if (Yii::$app->controller->module->id == 'screen') {
                    // scree 模块不需要调整
                    return;
                }
                if ($response->data !== null && is_array($response->data)) {
                    $response->data = array_merge([
                        'status' => $response->statusCode,
                        'message' => $response->statusText,
                    ], $response->data);
                    $response->statusCode = 200;
                }
            },
        ],

还有抛出的异常是否返回json在哪判断的疑问?

此时就要看yii2的错误分析了,参考了https://www.yiichina.com/tutorial/1333?sort=desc

最后在 yii\web\ErrorHandler的renderException 方法中解惑

protected function renderException($exception)
    {
        if (Yii::$app->has('response')) {
            $response = Yii::$app->getResponse();
            // reset parameters of response to avoid interference with partially created response data
            // in case the error occurred while sending the response.
            $response->isSent = false;
            $response->stream = null;
            $response->data = null;
            $response->content = null;
        } else {
            $response = new Response();
        }

        $response->setStatusCodeByException($exception);

        $useErrorView = $response->format === Response::FORMAT_HTML && (!YII_DEBUG || $exception instanceof UserException);
// 下面是判断返回的数据是什么
        if ($useErrorView && $this->errorAction !== null) {
            $result = Yii::$app->runAction($this->errorAction);
            if ($result instanceof Response) {
                $response = $result;
            } else {
                $response->data = $result;
            }
// html格式的数据
        } elseif ($response->format === Response::FORMAT_HTML) {
            if ($this->shouldRenderSimpleHtml()) {
                // AJAX request
                $response->data = '
' . $this->htmlEncode(static::convertExceptionToString($exception)) . '
'; } else { // if there is an error during error rendering it's useful to // display PHP error in debug mode instead of a blank screen if (YII_DEBUG) { ini_set('display_errors', 1); } $file = $useErrorView ? $this->errorView : $this->exceptionView; $response->data = $this->renderFile($file, [ 'exception' => $exception, ]); } } elseif ($response->format === Response::FORMAT_RAW) { // 字符串形式的 $response->data = static::convertExceptionToString($exception); } else { // 数组形式的 $response->data = $this->convertExceptionToArray($exception); } $response->send(); }

 

你可能感兴趣的:(php,yii)