Yii2.0 探究五:基于Yii2.0框架的Restful Api的对接以及跨域解决

时间:2022-10-20 20:23:35
基于Yii2.0框架的Restful Api的对接以及跨域解决;


在yii2.0框架中集成了restful api的部分,封装在yii\rest下,我们要做的工作有:


1.apache或nginx中开启rewrite伪静态模式(可自行搜索)


2.新建api模块,专门用来存放api相关,并配置响应格式为json而不是xml;


3.改造请求错误提示为Json格式,而非html;


4.测试调试请求;


5.前端进行接收,这部分我们说下前端ajax如何跨域做出请求;




准备:


1.新建news表用来展示(也可以用命令行migration去建表):

DROP TABLE IF EXISTS `news`;
CREATE TABLE IF NOT EXISTS `news` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`time` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `title` (`title`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;




2.api的请求工具:chrome中建议安装postman、firefox中安装HttpRequest,其他浏览器自行解决;


3.Json的查看工具:chrome中建议安装JSONView,会显示格式化的json,firefox中安装jsonformater,其他浏览器自行解决;


3.开启伪静态rewrite、并且配置api的二级域名为api.news.com格式,绑定在api/web下面;




一、Api模块的建立:



1.copy一份frontend或backend目录下的文件重命名为api放置在同级下作为api的内容,在common>bootstrap.php文件中依赖注入声明api模块:


  Yii::setAlias('@api',dirname(dirname(__DIR__)).'/api');


2.在api下新建v1、v2作为我们不同版本的内容、并在里面新建controllers、models、views部分,并分别在v1、v2下新建Module.php声明命名空间;


<?php
/**
* @see https://github.com/craftsmann.
* @author craftsmann <m13993334619@163.com>
*/
namespace api\modules\v1;


class Module extends \yii\base\Module
{
public $controllerNamespace = 'api\modules\v1\controllers';
public function init()
{
parent::init(); // TODO: Change the autogenerated stub
}
}
?>



3.api文件夹相关命名空间可自行修改,这里我们要对配置文件作出修改:


<?php
$params = array_merge(
require(__DIR__ . '/../../common/config/params.php'),
require(__DIR__ . '/../../common/config/params-local.php'),
require(__DIR__ . '/params.php'),
require(__DIR__ . '/params-local.php')
);


return [
'id' => 'app-api',
'basePath' => dirname(__DIR__),
'bootstrap' => ['log'],
'language' =>'zh-CN',
'controllerNamespace' => 'api\controllers',
'modules'=>[
'v1'=>['class'=>'api\modules\v1\Module'],
'v2'=>['class'=>'api\modules\v2\Module'],
],
'components' => [
'user' => [
'identityClass' => 'common\models\User',
'enableAutoLogin' => false,
'enableSession' =>false,
'loginUrl' =>null,
],
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => [
[
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning'],
],
],
],
'errorHandler' => [
'errorAction' => 'site/error',
],


'urlManager' => [
'enablePrettyUrl' => true,
'enableStrictParsing' => true, // 是否执行严格的url解析
'showScriptName' => false,
'rules' => [
[
'class'=>'yii\rest\UrlRule',
'controller'=>['v1/users','v1/news'],
]
],
],
],
'params' => $params,
];
?>





4.新建api>modules>v1>controllers>NewsController控制器用来响应,并重写behaviors响应为json:

<?php
/**
* @see https://github.com/craftsmann.
* @author craftsmann <m13993334619@163.com>
*/
namespace api\modules\v1\controllers;




use yii\rest\ActiveController;


class NewsController extends ActiveController
{
//响应数据为
public $modelClass='api\modules\v1\models\Goods';


public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['contentNegotiator']['formats'] = ['application/json' => \yii\web\Response::FORMAT_JSON];


return $behaviors;
}

?>




5.新建model在api>modules>v1>modles>News.php,可采用gii生成;




以上就是api模块建立的过程;




二、设置请求错误提示为Json,而非html的404;





1.在common>controllers>ErrorAction.php去重写action错误的提示;


?php
/**
* @see https://github.com/craftsmann.
* @author craftsmann <m13993334619@163.com>
*/
namespace common\controllers;


use Yii;
use yii\base\Exception;
use yii\base\UserException;
use yii\web\HttpException;




class ErrorAction extends \yii\web\ErrorAction
{
public function run()
{
if (($exception = Yii::$app->getErrorHandler()->exception) === null) {
// action has been invoked not from error handler, but by direct route, so we display '404 Not Found'
$exception = new HttpException(404, Yii::t('yii', 'Page not found.'));
}


if ($exception instanceof HttpException) {
$code = $exception->statusCode;
} else {
$code = $exception->getCode();
}
if ($exception instanceof Exception) {
$name = $exception->getName();
} else {
$name = $this->defaultName ?: Yii::t('yii', 'Error');
}


if ($exception instanceof UserException) {
$message = $exception->getMessage();
} else {
$message = $this->defaultMessage ?: Yii::t('yii', 'An internal server error occurred.');
}


if (Yii::$app->getRequest()->getIsAjax()) {
echo json_encode(
[
'name'=>$name,


'msg' =>$message


]
);
} else {
echo json_encode(
[
'status'=>'error',
'code'=>$code,
'msg' =>$message,
]
);
}
}


}



这里我们做出错误请求,api.news.com/v3你会发现,提示Json数据为


{status: "error", code: 404, msg: "页面未找到。"}


这在api请求错误时很有帮助;


三:测试调试请求;



1.在数据表news中插入数据




2.在postman中我们输入http://api.news.com/v1/news会显示出news的数据;




3.格式:一般Restful Api的格式是固定的;当然也可以去重写;




  GET  http://api.news.com/v1/news 列出所有新闻
  
  GET  http://api.news.com/v1/news/1 列出id为1的新闻
  
  POST  http://api.news.com/v1/news 新建新闻
  
  DELETE  http://api.news.com/v1/news/1 删除id为1的新闻
  
  PUT/PATCH  http://api.news.com/v1/news/1 修改id为1的新闻


  我们可以在postman中输入以上url做测试,其他方式例如OPTIONS、HEAD可寻找资料或查看手册;
 
  以上我们就搭建完成了Restful api的接口;


四、 前端接收、并采取的跨域解决方案:



  前面:前端所有部分都通过jquery来做ajax请求;
  
  事实上我们只需要一个button按钮来测试我们所得到的数据;
  
  在frontend.news.com中新建一个action并render一个视图我们用来测试
  
 <?php
/**
* @see https://github.com/craftsmann.
* @author craftsmann <m13993334619@163.com>
*/
use yii\bootstrap\ActiveForm;


\frontend\assets\AppAsset::register($this);
?>


<?php $this->beginBlock('js')?>


$('#api').on('click',function(){


$.ajax({
type: "get",
url: "http://api.hotel.com/v1/news/1",
success: function(data){
$('#message').html('新增新闻名称:'+data.name);
},
error: function(news){
$('#message').html(news.responseJSON[0].message);
}
});


});
<?php $this->endBlock()?>
<?php $this->registerJs($this->blocks['js'],yii\web\View::POS_END);?>
<div class="container">


<div class="form-group">

<?= \yii\helpers\Html::Button('提交',['class'=>'btn btn-default','id'=>'api'])?>

</div>

<a id='message'></a>

</div>




  
  当你在frontend.news.com用ajax去请求我们api.news.com的api接口时候,你会发现:请求失败,并报错,原因是跨域,浏览器做出了限制;
  
  
  也就是说我们域名相同、端口相同、协议相同才不算做是跨域,那么该怎么办。。。。。。。。。。
  
  
  不用担心,自有跨域的解决办法、这里介绍两种,其他可google解决:
  
  
  1.JsonP跨域解决:ajax中设定datatype的类型为jsonp就行了,很简单;
  
  优点是:对于一些古老的浏览器很高的支持;例如万恶的ie。。。
  
  缺点是:只能发出GET请求,其他method无法操作,例如POST、DELETE等
  
  2.cros同源策略:设置方式,只要在action中设置header头部即可,也是最简单、最常用的一种(如果不考虑ie的话)
  
  优点:操作方便,只需添加header;支持新兴浏览器;所有方式都可采用:例如GET、POST、DELETE、PUT、PATCH、OPTIONS等
  
  Access-Control-Allow-Origin:*或者建立信任的url,例如:Access-control-Origin:http://frontend.news.com;
  
  Access-Control-Allow-Methods:*允许操作的方法为所有;
  
  缺点:对ie的支持不好,兼容性IE10以前的都不能用。
  
  
  
  The  END