牛骨文教育服务平台(让学习变的简单)
博文笔记

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

创建时间:2016-12-01 投稿人: 浏览次数:3220
基于Yii2.0框架的Restful Api的对接以及跨域解决;


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


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下面;






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 apimodulesv1;


class Module extends yiiaseModule
{
   public $controllerNamespace = "apimodulesv1controllers";
   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" => "apicontrollers",
    "modules"=>[
        "v1"=>["class"=>"apimodulesv1Module"],
        "v2"=>["class"=>"apimodulesv2Module"],
    ],
    "components" => [
        "user" => [
            "identityClass" => "commonmodelsUser",
            "enableAutoLogin" => false,
            "enableSession"  =>false,
            "loginUrl"       =>null,
        ],
        "log" => [
            "traceLevel" => YII_DEBUG ? 3 : 0,
            "targets" => [
                [
                    "class" => "yiilogFileTarget",
                    "levels" => ["error", "warning"],
                ],
            ],
        ],
        "errorHandler" => [
            "errorAction" => "site/error",
        ],


        "urlManager" => [
            "enablePrettyUrl" => true,
            "enableStrictParsing" => true, // 是否执行严格的url解析
            "showScriptName" => false,
            "rules" => [
                [
                    "class"=>"yii
estUrlRule",
                    "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 apimodulesv1controllers;




use yii
estActiveController;


class NewsController extends ActiveController
{
   //响应数据为
   public $modelClass="apimodulesv1modelsGoods";


   public function behaviors()
    {
        $behaviors = parent::behaviors();
        $behaviors["contentNegotiator"]["formats"] = ["application/json" => yiiwebResponse::FORMAT_JSON];


        return $behaviors;
    }
	
?>




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




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








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


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


use Yii;
use yiiaseException;
use yiiaseUserException;
use yiiwebHttpException;




class ErrorAction extends yiiwebErrorAction
{
    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 yiiootstrapActiveForm;


	frontendassetsAppAsset::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"],yiiwebView::POS_END);?>
	<div class="container">


		 <div class="form-group">
			
			<?= yiihelpersHtml::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
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。