'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 = '<pre>' . $this->htmlEncode(static::convertExceptionToString($exception)) . '</pre>';
} 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();
}