博客
Yii2.0 RESTful API 认证教程 隔了怎么长时间,终于到了 Yii2.0 RESTful API 认证介绍了. 废话不多说,直接正文开始 认证介绍 和Web应用不同,RESTful APIs 通常是无状态的, 也就意味着不应使用 sessions 或 cookies, 因此每个请求应附带某种授权凭证,因为用户授权状态可能没通过 sessions 或 cookies 维护, 常用的做法是每个请求都发送一个秘密的 access token 来认证用户, 由于 access token 可以唯一识别和认证用户,API 请求应通过 HTTPS 来防止man-in-the-middle (MitM) 中间人攻击. 认证方式 HTTP 基本认证 :access token 当作用户名发送,应用在access token可安全存在API使用端的场景, 例如,API使用端是运行在一台服务器上的程序。 请求参数: access token 当作API URL请求参数发送,例如 https://example.com/users?access-token=xxxxxxxx, 由于大多数服务器都会保存请求参数到日志, 这种方式应主要用于JSONP 请求,因为它不能使用HTTP头来发送 access token OAuth 2 : 使用者从认证服务器上获取基于 OAuth2 协议的 access token, 然后通过 HTTP Bearer Tokens 发送到 API 服务器。 上方进行简单介绍,内容来自 Yii Framework 2.0 权威指南 实现步骤 我们都知道 Yii2.0 默认的认证类都是 User,前后台都是共用一个认证类,因此我们要把API 认证类 单独分离出来,达到前、后、API都分离, 继上一章:(这里暂时使用默认User数据表,正式环境请分离不同的数据表来进行认证) 准备条件 继上篇的 User 数据表,我们还需要增加一 个access_token 的字段, 1. 直接在你的数据库中新增 access_token 字段。 2. 使用数据迁移的方式 * 进入项目根目录打开控制台输入以下命令: php yii migrate/create add_access_token_to_user * 打开 你的项目目录/console/migrations/m180704_054630_add_access_token_to_user.php 修改如下内容: ``` public function safeUp() { $this->addColumn('user', 'access_token', $this->string()); } public function safeDown() { $this->dropColumn('user', 'access_token'); } ``` * 执行迁移命令 ``` php yii migrate ``` 浏览器打开前台目录 frontend 页面,点击注册账号,先注册一个账号 配置 打开 api\config\main.php 配置 user 应用组件: * 设置 `identityClass` 属性为哪个认证类 * 设置 `enableSession` 属性为 `false` * 设置 `enableAutoLogin` 属性为 `true` 将 session 组件注释掉,或删掉 'user' => [ 'identityClass' => 'api\models\User', 'enableAutoLogin' => true, 'enableSession'=>false, //'identityCookie' => ['name' => '_identity-backend', 'httpOnly' => true], ], //'session' => [ // this is the name of the session cookie used for login on the backend // 'name' => 'advanced-backend', // ], 编写 api\models\User.php 实现认证类,继承 IdentityInterface 将 common\models\User 类拷贝到 api\models\目录下,修改命名空间为api\models <?php namespace api\models; use Yii; use yii\base\NotSupportedException; use yii\behaviors\TimestampBehavior; use yii\db\ActiveRecord; use yii\web\IdentityInterface; ... class User extends ActiveRecord implements IdentityInterface { ... ... } 将 common\models\LoginForm.php 类拷贝到api\models\目录下,修改命名空间,并重写login方法: <?php namespace api\models; use Yii; use yii\base\Model; ... ... public function login() { if ($this->validate()) { $access_token=$this->_user->generateAccessToken(); $this->_user->save(); return $access_token; } else { return false; } } 上方代码给User模型添加了一个generateAccessToken()方法,因此我们到api\models\User.php中添加此方法 namespace api\models; use Yii; use yii\base\NotSupportedException; use yii\behaviors\TimestampBehavior; use yii\db\ActiveRecord; use yii\web\IdentityInterface; ... ... class User extends ActiveRecord implements IdentityInterface { ... ... /** * 生成accessToken字符串 * @return string * @throws \yii\base\Exception */ public function generateAccessToken() { $this->access_token=Yii::$app->security->generateRandomString(); return $this->access_token; } } 接下来打开 之前的User 控制器编写登录方法 use api\models\LoginForm; ... ... //省略一些代码 /** * 登陆 * @return array * @throws \yii\base\Exception * @throws \yii\base\InvalidConfigException */ public function actionLogin() { $model = new LoginForm(); if ($model->load(Yii::$app->getRequest()->getBodyParams(), '') && $model->login()) { return [ 'access_token' => $model->login(), ]; } else { return $model->getFirstErrors(); } } ... 最后新增一条URL规则 打开 api\config\main.php 修改 components属性,添加下列代码: 'urlManager' => [ 'enablePrettyUrl' => true, 'enableStrictParsing' => true, 'showScriptName' => false, 'rules' => [ ['class' => 'yii\rest\UrlRule', 'controller' => 'user', 'extraPatterns'=>[ 'POST login'=>'login', ], ], ], ] 使用一个调试工具来进行测试 http://youdomain/users/login 记住是POST 请求发送,假如用POSTMAN有问题的话指定一下 Content-Type:application/x-www-form-urlencoded。 ok,不出意外的话,相信你已经可以收到一个access_token了,接下来就是如何使用这个token,如何维持认证状态,达到不携带这个token将无法访问,返回401 维持认证状态 实现认证只需两步: 在你的 REST 控制器类中配置 authenticator 行为来指定使用哪种认证方式 在你的 user identity class 类中实现 yii\web\IdentityInterface::findIdentityByAccessToken() 方法. 接下来我们围绕这两步来实现: 添加一个REST控制器 因我这里暂未设计其他数据表 所以我们暂且还使用User 数据表吧 在api\controllers\新加一个控制器 命名为 ArticleController 并继承 yii\rest\ActiveController,配置认证方式代码:代码如下: <?php namespace api\controllers; use yii\rest\ActiveController; use Yii; use yii\filters\auth\CompositeAuth; use yii\filters\auth\HttpBasicAuth; use yii\filters\auth\HttpBearerAuth; use yii\filters\auth\QueryParamAuth; class ArticleController extends ActiveController { public $modelClass = 'api\models\User'; public function behaviors() { $behaviors = parent::behaviors(); $behaviors['authenticator'] = [ 'class' => CompositeAuth::className(), 'authMethods' => [ HttpBasicAuth::className(), HttpBearerAuth::className(), QueryParamAuth::className(), ], ]; return $behaviors; } } 注意:这个控制器并非真正的Article,实则还是User 实现 findIdentityByAccessToken() 方法: 打开 api\models\User.php 重写 findIdentityByAccessToken() 方法 ... ... class User extends ActiveRecord implements IdentityInterface { ... ... public static function findIdentityByAccessToken($token, $type = null) { return static::findOne(['access_token' => $token]); } ... } 为刚才新加的控制器添加路由规则(ps:好像多了一步......) 修改 api\config\main.php 'urlManager' => [ 'enablePrettyUrl' => true, 'enableStrictParsing' => true, 'showScriptName' => false, 'rules' => [ ['class' => 'yii\rest\UrlRule', 'controller' => 'user', 'extraPatterns'=>[ 'GET send-email'=>'send-email' 'POST login'=>'login', ], ], ['class' => 'yii\rest\UrlRule', 'controller' => 'article', 'extraPatterns'=>[ ], ], ], ] 接下来访问一下你的域名 http://youdomain/articles,不携带任何参数是不是返回 401了? ok,这里介绍两种访问方式,一种是URL访问,另一种是通过header 来进行携带 http://youdomain/articles?access-token=y3XWtwWaxqCEBDoE-qzZk0bCp3UKO920 传递 header头信息 Authorization:Bearer y3XWtwWaxqCEBDoE-qzZk0bCp3UKO920 注意 Bearer 和你的token中间是有 一个空格的,很多同学在这个上面碰了很多次 好啦,基于YII2.0 RESTful 认证就此结束了, 更过完整的功能 请移步官方文档 授权验证 另外还有速率验证,就自行发觉吧 另外,如果看不懂,或者写的不好,请移步 魏曦 老师的视频教程,本人所有内容都是跟随 魏曦老师 学的 魏曦教你学 写完认证发现我们的接口返回的数据不是很直观,现实生活中通常也不是这样子的,我们可能会返回一些特定的格式 自定义响应内容 打开 api\config\main.php 在 components数组里面添加如下内容分 'response' => [ 'class' => 'yii\web\Response', 'on beforeSend' => function ($event) { $response = $event->sender; $response->data = [ 'success' => $response->isSuccessful, 'code' => $response->getStatusCode(), 'message' => $response->statusText, 'data' => $response->data, ]; $response->statusCode = 200; }, ], 这里的状态码统一设为 200 ,具体的可另行配置,假如登陆操作 密码错误或者其他,我们可以在控制器中这样使用: $response = Yii::$app->response; $response->setStatusCode(422); return [ 'errmsg' => '用户名或密码错误!' ]; 水平有限,难免有纰漏,请不吝赐教,在下会感激不尽
1月前 喜欢(0) 浏览(212) 评论(0)
博客
Yii2 RESTful API 快速搭建教程 这篇说下yii2.0开发 API 吧,使用 RESTful API模式 安装Yii2.0 通过 Composer 安装 这是安装Yii2.0的首选方法。如果你还没有安装 Composer,你可以按照这里的说明进行安装。 安装完 Composer,运行下面的命令来安装 Composer Asset 插件: php composer.phar global require "fxp/composer-asset-plugin:^1.2.0" 安装高级的应用程序模板,运行下面的命令: php composer.phar create-project yiisoft/yii2-app-advanced advanced 2.0.13 初始化高级模板 cd advanced init 修改数据库连接属性 打开 common\config\main-local.php,配置数据库连接信息 'db' => [ 'class' => 'yii\db\Connection', 'dsn' => 'mysql:host=127.0.0.1;dbname=yiiapi', 'username' => 'root', 'password' => 'root', 'charset' => 'utf8', ], 执行 migrate 数据库迁移 yii migrate 拷贝backend目录,命名为api 打开api\config\main.php 修改id,controllerNamespace: return [ 'id' => 'app-api', 'basePath' => dirname(__DIR__), 'controllerNamespace' => 'api\controllers', ] 打开common\config\main.php开启url路由美化规则 'urlManager' => [ 'enablePrettyUrl' => true, 'showScriptName' => false, 'rules' => [ ], ], 打开common\config\bootstrap.php添加以下别名 Yii::setAlias('@api', dirname(dirname(__DIR__)) . '/api'); 为什么要单独创建API应用 单独创建API应用,目的是便于维护,可以避免以下问题 配置的冲突 控制器的命名不便 url美化规则冲突 分工明确frontend为前台目录;backend为后台目录;api为api目录 接下来打开 api\controllers 新建一个User控制器,继承 yii\rest\ActiveController,命名为 UserController,代码如下: <?php namespace api\controllers; use yii\rest\ActiveController; class UserController extends ActiveController { public $modelClass = 'common\models\User'; } 这里创建 user控制器继承 yii\rest\ActiveController 并指定要操作的模型 启用JSON 输入 配置 request 应用程序组件的 parsers 属性使用 yii\web\JsonParser 用于 JSON 输入 打开配置文件 api\config\main-local.php 修改为如下代码: <?php $config = [ 'components' => [ 'request' => [ // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation 'cookieValidationKey' => 'P0r2XoT9LCUnyVlSgxBbJOqQxdCJ3i29', 'parsers' => [ 'application/json' => 'yii\web\JsonParser', ], ], ], ]; if (!YII_ENV_TEST) { // configuration adjustments for 'dev' environment $config['bootstrap'][] = 'debug'; $config['modules']['debug'] = [ 'class' => 'yii\debug\Module', ]; $config['bootstrap'][] = 'gii'; $config['modules']['gii'] = [ 'class' => 'yii\gii\Module', ]; } return $config; 配置URL规则 为刚才的 user控制器添加url美化规则 打开 api\config\main.php 修改 components属性,添加下列代码: ... 'urlManager' => [ 'enablePrettyUrl' => true, 'enableStrictParsing' => true, 'showScriptName' => false, 'rules' => [ ['class' => 'yii\rest\UrlRule', 'controller' => 'user' ], ], ] ... ok,到此就成了一个 符合 RESTful 风格的API 看起来在控制器了什么也没有写,只是指定了一个模型,但是她的背后完成了很多的功能哦,列表如下: GET /users: 逐页列出所有用户 HEAD /users: 显示用户列表的概要信息 POST /users: 创建一个新用户 GET /users/123: 返回用户 123 的详细信息 HEAD /users/123: 显示用户 123 的概述信息 PATCH /users/123: and PUT /users/123: 更新用户123 DELETE /users/123: 删除用户123 OPTIONS /users: 显示关于末端 /users 支持的动词 OPTIONS /users/123: 显示有关末端 /users/123 支持的动词 如何访问呢 你可以使用 curl命令进行访问,命令如下: curl -i -H "Accept:application/json" "http://localhost/users" 命令行下还是比较麻烦的,也不方便测试,推荐使用 API测试工具 这类的工具有很多,我就不一一列举了,这里推荐 Postman,很好很强大,Chorme也有插件,可以安装,这里我推荐直接下载软件安装调试,比较方便 你可能发现了 访问任何路由地址都是加的s,users , 为什么呢? 资源,你要理解 资源二字,既然是资源肯定是个集合,肯定有一大堆,所以要加上复数,我是这么理解的。 你说我就是不想加上s,我就想采用http://localhost/user 这种方式来进行访问,好吧,可以,满足你,只是不推荐 继续打开配置文件api\config\main.php修改刚才添加的 urlManager 如下: 'urlManager' => [ 'enablePrettyUrl' => true, 'enableStrictParsing' => true, 'showScriptName' => false, 'rules' => [ ['class' => 'yii\rest\UrlRule', 'controller' => 'user', 'pluralize' => false, //设置为false 就可以去掉复数形式了 ], ], ] 加入 'pluralize' => false, 就表示去掉复数形式了,再次强调不推荐 ok,在控制器中我们没有写任何一句代码,他就给我们生成许多方法,但是有时候我们可能需要修改一些代码,来达到我们想要的效果,比如连表查询,然后再返回数据 接下来我们就实现这样的功能: 打开刚才新建的user控制器, 重写 action方法: <?php namespace api\controllers; use yii\rest\ActiveController; class UserController extend extends ActiveController { public $modelClass = 'common\models\User'; public function actions() { $action= parent::actions(); // TODO: Change the autogenerated stub unset($action['index']); unset($action['create']); unset($action['update']); unset($action['delete']); } public function actionIndex() { //你的代码 } } 这样我们就可以重写他的代码了。哈哈 我们再新建一个自己的 action <?php namespace api\controllers; use yii\rest\ActiveController; class UserController extends ActiveController { public $modelClass = 'common\models\User'; public function actions() { $action= parent::actions(); // TODO: Change the autogenerated stub unset($action['index']); unset($action['create']); unset($action['update']); unset($action['delete']); } public function actionIndex() { //你的代码 } public function actionSendEmail() //假如是get请求 { //业务逻辑 } } 然后试着访问一下 http://localhost/users/send-email,报错?找不到? 报错就对了,那是因为我们没有设置其他路由访问 修改 api\config\main.php 'urlManager' => [ 'enablePrettyUrl' => true, 'enableStrictParsing' => true, 'showScriptName' => false, 'rules' => [ ['class' => 'yii\rest\UrlRule', 'controller' => 'user', //'pluralize' => false, //设置为false 就可以去掉复数形式了 'extraPatterns'=>[ 'GET send-email'=>'send-email' ], ], ], ] 接下来重新访问就没有问题了,ps:你自己编写的任何 action 都要在 extraPatterns 进行配置 差点忘了 状态码 这个东西,我们现在所有的东西返回来的都是一个 JSON,加入没有数据局返回的是空的数组,所以这肯定不行啊,我们得加上 一些特定的状态码 来标识这些数据啊,怎么加? 继续修改 api\config\main.php 在 components 添加如下代码: 'response' => [ 'class' => 'yii\web\Response', 'on beforeSend' => function ($event) { $response = $event->sender; $response->data = [ 'success' => $response->isSuccessful, 'code' => $response->getStatusCode(), 'message' => $response->statusText, 'data' => $response->data, ]; $response->statusCode = 200; }, ], 这里统一使用 200来表示,当然并不是所有的都是 200,你应该具体情况具体对待,切记不要乱使用 任意加各种标识,请 遵循这些 规范 状态码 是不是觉得还少了点什么?认证 对就是 认证,就差 认证 就完美了,篇幅有限,内容多了反而影响阅读兴趣,下篇进行 认证介绍 感谢以下,特别是 魏曦老师的视频教程 魏曦教你学 Yii Framework 2.0 权威指南 不足之处,欢迎指正
1月前 喜欢(0) 浏览(209) 评论(0)
博客
Yii2 获取模块名、控制器名、方法名在视图中: 模块名 $this->context->module->id 控制器名 $this->context->id 方法名 $this->context->action->id 在控制器中 模块名 Yii::$app->controller->module->id; 控制器名 Yii::$app->controller->id; 方法名 Yii::$app->controller->action->id; 或 模块名 $this->module->id; 控制器名 $this->id; 方法名 $this->action->id; 在控制器的 beforeAction 方法中(方法接收$action参数) 模块名 $action->controller->module->id; 控制器名 $action->controller->id; 方法名 $action->id;
3月前 喜欢(0) 浏览(434) 评论(0)
博客
在处理一个文章内容时,需要在指定标签后面插入 指定内容 1.查找到指定位置(例如 我要在第三个标签<\p>后插入广告代码): /** * 选择到标签指定位置后 字节长度 * $find 标签名字 * $number 第几个标签位置 * $body 被查找的内容 * @param unknown $number * @param unknown $find * @param unknown $body * @return number * @author kangjy */ public function position($number,$find,$body) { //获取标签长度 $findLen = strlen($find); //初始化位置 $position =0; for ($number=1; $number<=3; $number++) { $position = stripos($body, $find, $position); //var_dump('第'.$number.'个 位置'.$position); $position += $findLen; // var_dump('第'.$number.'个 位置加寻找词的长度'); } return $position ; } 2.插入指定字符串代码 /** * 指定位置插入字符串 * @param $str 原字符串 * @param $i 插入位置 * @param $substr 插入字符串 * @return string 处理后的字符串 * @author kangjy */ public function insertToStr($str, $i,$substr){ //指定插入位置前的字符串 $startstr=""; for($j=0; $j<$i; $j++){ $startstr .= $str[$j]; } //指定插入位置后的字符串 $laststr=""; for ($j=$i; $j<strlen($str); $j++){ $laststr .= $str[$j]; } //将插入位置前,要插入的,插入位置后三个字符串拼接起来 $str = $startstr . $substr . $laststr; //返回结果 return $str; } 这样就可以了
3月前 喜欢(0) 浏览(371) 评论(0)
博客
yii2是一个快速开发的框架,其中gii扩展不得不说是一个很大的助力,通过gii自动生成代码,把一些通用的代码交给程序去生成,很大程度上减少开发者的时间成本。但gii也有一些弊端,那就是生成的代码是yii2自带的模板,每次生成的代码并不是我们想要的,因而每次都要去对应的做调整。 那么如何才能让程序生成我们想要的代码呢?没错,yii2的gii扩展是支持自定义模板的,通过自定义模板我们就可以让gii生成我们想要的代码,又一次节约了去修改生成模板的时间成本。 配置 gii生成器所用到的模板文件位于目录 vendor\yiisoft\yii2-gii\generators\crud\default,我们既然要自定义模板,最好是在原有的模板基础下做调整。 1.复制一份模板,拷贝default目录,放在任意位置,此处我们放置在根目录 /backend/giitpl/crud 中。 2.修改模板(本文为教程不做细说,此处自行修改即可) 3.打开配置文件 /backend/config/main-local.php 修改$config['modules']['gii']的配置(如下): $config['modules']['gii'] = [ 'class' => 'yii\gii\Module', 'allowedIPs' => ['127.0.0.1', '::1'], 'generators' => [ 'crud' => [ //生成器名称 'class' => 'yii\gii\generators\crud\Generator', 'templates' => [ //设置我们自己的模板 //模板名 => 模板路径 'myCrud' => '@backend/giitpl/crud/default', ] ] ], ]; 4.通过gii生成代码,打开gii界面,使用crud generator生成代码(注:此处配置要修改code template模板) 选择我们自定义的模板,然后点击生成,那么gii自定义的模板代码就生成好了。 总结 通过自定义gii模板,我们能够更加灵活快速的完成需要的功能,把许多相似度高,应用场景多,功能类似(比如:列表页,详情页等)的代码做成gii的模板,通过程序去生成。还是那句话yii2就是一个快速开发的框架。
3月前 喜欢(0) 浏览(374) 评论(0)
博客
Gii 这个扩展无疑是 yii2 快速开发的一大助力,通过使用gii生成代码很大程序上节约了开发的时间成本,那么如何使用gii这个组件呢?本文为你们简单介绍一下yii2中gii的一些常用功能。 生成模型(Model) 在数据库新建一张test的测试表 CREATE TABLE `test` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增ID', `name` varchar(255) DEFAULT NULL COMMENT '名称', `desc` varchar(255) DEFAULT NULL COMMENT '描述', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 1.打开gii的界面 /index.php?r=gii或者 /gii(url美化之后) 2.创建Model 首先,点击Model generator下面的start按钮,进入model生成界面,输入数据表名test,输入model class(一般会自动生成一个,也可以自定义model class名称) 然后,点击下方的preview,会列出要生成的模型 最后,点击generate生成model文件,如下提示测生成成功 生成CRUD 同样,点击CRUD Generator,去创建CRUD也就是增删改查的操作界面及功能,输入对应的参数(注:view path 不填,即生成在默认的位置) 然后,点击preview,查看要生成的文件列表(注:如果已经存在对应的控制会显示下方diff,可以选择覆盖或者保留) 最后点击Generate生成相应的CRUD文件,然后就可以查看对应的页面了,如此简单就完成了一个数据表的增删改成
3月前 喜欢(0) 浏览(420) 评论(0)
博客
package main import ( "context" "fmt" "go.etcd.io/etcd/clientv3" "time" ) func main() { var ( config clientv3.Config client *clientv3.Client lease clientv3.Lease leaseResp *clientv3.LeaseGrantResponse leaseId clientv3.LeaseID leaseRespChan <-chan *clientv3.LeaseKeepAliveResponse err error ) //客户端配置 config = clientv3.Config{ Endpoints: []string{"127.0.0.1:2379"}, DialTimeout: 5 * time.Second, } //建立连接 if client, err = clientv3.New(config); err != nil { fmt.Println(err) return } //上锁(创建租约,自动续租) lease = clientv3.NewLease(client) //设置一个ctx取消自动续租 ctx,cancleFunc := context.WithCancel(context.TODO()) //设置10秒租约(过期时间) if leaseResp,err = lease.Grant(context.TODO(),10);err != nil { fmt.Println(err) return } //拿到租约id leaseId = leaseResp.ID //自动续租(不停地往管道中扔租约信息) if leaseRespChan,err =lease.KeepAlive(ctx,leaseId);err != nil { fmt.Println(err) } //启动一个协程去监听 go listenLeaseChan(leaseRespChan) //业务处理 kv := clientv3.NewKV(client) //创建事务 txn := kv.Txn(context.TODO()) txn.If(clientv3.Compare(clientv3.CreateRevision("/cron/lock/job9"),"=",0)). Then(clientv3.OpPut("/cron/lock/job9","xxx",clientv3.WithLease(leaseId))). Else(clientv3.OpGet("/cron/lock/job9"))//否则抢锁失败 //提交事务 if txtResp,err :=txn.Commit();err != nil { fmt.Println(err) return } else { //判断是否抢锁 if !txtResp.Succeeded { fmt.Println("锁被占用:",string(txtResp.Responses[0].GetResponseRange().Kvs[0].Value)) return } } fmt.Println("处理任务") //释放锁(停止续租,终止租约) defer cancleFunc()//函数退出取消自动续租 defer lease.Revoke(context.TODO(),leaseId) //终止租约(去掉过期时间) time.Sleep(10 * time.Second) } func listenLeaseChan(leaseRespChan <-chan *clientv3.LeaseKeepAliveResponse) { var ( leaseKeepResp *clientv3.LeaseKeepAliveResponse ) for { select { case leaseKeepResp = <-leaseRespChan: if leaseKeepResp == nil { fmt.Println("租约失效了") goto END } else { fmt.Println(leaseKeepResp.ID) } } } END: }
3月前 喜欢(0) 浏览(601) 评论(0)
博客
少比比直接代码(你可以理解为给key设置过期时间,但是比redis要强大的是它可以自动续租) package main import ( "context" "fmt" "go.etcd.io/etcd/clientv3" "go.etcd.io/etcd/mvcc/mvccpb" "time" ) func main() { var ( config clientv3.Config client *clientv3.Client err error kv clientv3.KV keepResp *clientv3.LeaseKeepAliveResponse keepRespChan <-chan *clientv3.LeaseKeepAliveResponse ) //创建租约 lease := clientv3.NewLease(client) //设置10秒租约(过期时间为10秒) if leaseRes,err := lease.Grant(context.TODO(),10);err != nil { fmt.Println(err) return } else { //得到租约id leaseId := leaseRes.ID //定义一个上下文使得租约5秒过期 ctx,_:= context.WithTimeout(context.TODO(),5*time.Second) //自动续租(底层会每次讲租约信息扔到 <-chan *clientv3.LeaseKeepAliveResponse 这个管道中) if keepRespChan,err = lease.KeepAlive(ctx,leaseId);err != nil { fmt.Println(err) return } //启动一个新的协程来select这个管道 go func() { for { select { case keepResp = <- keepRespChan: if keepResp == nil { fmt.Println("租约失效了") goto END//失效跳出循环 } else { //每秒收到一次应答 fmt.Println("收到租约应答",keepResp.ID) } } } END: }() //得到操作键值对的kv kv = clientv3.NewKV(client) //进行写操作 if putResp,err = kv.Put(context.TODO(),"/cron/lock/job1","",clientv3.WithLease(leaseId)/*高速etcd这个key对应的租约*/);err != nil { fmt.Println(err) return } else { fmt.Println("写入成功",putResp.Header.Revision/*这东西你可以理解为每次操作的id*/) } } //监听这个key的租约是否过期 for { if getResp,err = kv.Get(context.TODO(),"/cron/lock/job1");err != nil { fmt.Println(err) return } if getResp.Count == 0 { fmt.Println("kv过期了") break } fmt.Println("kv没过期",getResp.Kvs) time.Sleep(2 * time.Second) }
3月前 喜欢(0) 浏览(575) 评论(0)
博客
etcd做注册服务使用 类似java生态的zookeeper,最近在学学习 1、下载etcd包 //直接下载即可(因为包比较大有翻墙,可以直接去 https://golangtc.com/download/package 下载) go get go.etcd.io/etcd/clientv3 2、使用记录 packge main import ( "context" "fmt" "go.etcd.io/etcd/clientv3" //"log/syslog" "time" ) func main() { var ( config clientv3.Config client *clientv3.Client err error kv clientv3.KV putResp *clientv3.PutResponse getResp *clientv3.GetResponse delResp *clientv3.DeleteResponse keepResp *clientv3.LeaseKeepAliveResponse keepRespChan <-chan *clientv3.LeaseKeepAliveResponse ) } //客户端配置 config = clientv3.Config{ Endpoints:[]string{"127.0.0.1:2379"}, DialTimeout:5 * time.Second, } //建立连接 if client,err = clientv3.New(config);err != nil { fmt.Println(err) return } //得到操作etcd键值对的kv kv = clientv3.NewKV(client) //写入etcd if putResp,err = kv.Put(context.TODO(),"/cron/jobs/job2","....",clientv3.WithPrevKV()/*可选参数,得到上次操作的值*/);err != nil { fmt.Println(err) } else { fmt.Println(putResp.Header.Revision) if putResp.PrevKv != nil { fmt.Println(string(putResp.PrevKv.Value)) } } //读取某个key的value值 getResp,err = kv.Get(context.TODO(),"/cron/jobs/job1"/*,clientv3.WithCountOnly()可选参数,得到数量*/) if err != nil { fmt.Println(err) return } else { fmt.Println(getResp.Kvs[0].Value/*得到的是一个切片*/) } //读取前缀为XXX的所有的key的value(需要加上参数clientv3.WithPrefix()) if getResp,err = kv.Get(context.TODO(),"/cron/jobs/",clientv3.WithPrefix());err != nil { fmt.Println(err) return } else { fmt.Println(getResp.Kvs) } //删除操作 if delResp,err = kv.Delete(context.TODO(),"/cron/jobs/job2",clientv3.WithPrevKV()/*得到删除之前的值*/);err != nil { fmt.Println(err) return } else { if len(delResp.PrevKvs) != 0 { fmt.Println(delResp.PrevKvs) } }
3月前 喜欢(0) 浏览(522) 评论(0)
博客
教程有两种,一种是别人家的教程和撩妹的教程。撩妹家的教程只是看看就好了。至于运用得看你们项目经理脾气好不好。 做了一个很是杀马特的网站 http://foxhome.top/ , SiteController actionCategory($video_type = VideoType::All) 按照美化教程生成了以下http://foxhome.top/site/sategory.html?video_type=999 对比别人家的视频网站发现一般都是 /category/999.html ,/category/122.html,/category/122.html 之类的。瞬间感觉拉低了一个档次。善于搬砖的撩妹最后折腾成了 http://foxhome.top/category/1000.html http://foxhome.top/category/1001.html http://foxhome.top/category/1002.html 详情页页弄成了 http://foxhome.top/videoinfo/3.html 。 下面是重点 对config下的 web.php进行以下修改 列如'category/<video_type:\d+>.html' => 'site/category' 中 'category/<video_type:\d+>.html' 指的是我们在浏览器中的请求<video_type:\d+>表示参数video_type,并且是数字。 当我们请求/category/1000.html 相当于将video_type转由 site/category?video_type=1000 路由去处理而category/<video_type:\d+>.html中 category和.html你想怎么写就怎么写主要是把参数video_type传递给后面的控制器即可。 如果只是想稍微修饰以下url 也可以这样子操作'/signup'=>'/user/signup' 即请求/signup相当于就是在请求/user/signup,/signup可以瞎写,但是后面的一定需要是【/控制器名称/动作】
4月前 喜欢(0) 浏览(443) 评论(0)
博客
YII2为我们提供了很多widget用来快速构建前端HTML代码,但是很多属性我们并不知道。经过一阵子瞎摸索找到了以下技巧。 YII2大部分widget都是构建了Bootstrap类标签,所以widget分为普通widget和Bootstrap widget,Bootstrap widget位于yii\bootstrap下,而一般常用的widgets位于yii\widgets或者通过yii\helpers\Html构建; 我们很多时候需要定义一个HTML元素的属性,style,id,或者class。例如 yii\helpers\Html::submitButton($content = 'Submit', $options = []); 作者为方便开发者如果需要改变widget的属性时定义options参数即可,比如我需要定义一个html标签的class则只需要定义 Html::submitButton('登录', ['class' => 'btn btn-primary', 'name' => 'login-button']),['属性'=>'属性值']; 很惊奇的发现一个widget只要有options参数则都是一样,列如定义 class ['class' => 'btn btn-primary',...], 定义html的ID ['id' => 'mView',...] 如果需要强大而优美的Bootstrap widget则只需要研究以下Bootstrap V3下的样式即可参考文档 https://v3.bootcss.com/ 以上代码生成了三个Bootstrap风格的按钮,以为YII2集成了Bootstrap css所以只要按钮中有Bootstrap的类样式就会被应用上去,至于需要哪些样式则只需要研究 https://v3.bootcss.com/css/ 可以了。 另外需要强调的是YII中有很多组合控件比如ActiveForm中的$form->field,这类控件会有一个div,再套一个input或者别的元素form->field 中的options和别处不同form->field中的options参数并不是直接定义div中的属性form->field对应的是 ActiveForm::begin中的fieldConfig变量,不同的是ActiveForm::begin是定义全部的子条目,而form->field是定义当前的一个条目。所以form->field定义外套的div的各种属性是 [...,'options'=>['class' => 'input-group'],..]因为form->field不仅能定义div元素的各种标签属性options还能定义template等。我们很多时候的需求是既要定义组合表达的div还要定义子HTML元素的各种属性 passwordInput和普通的Input区别就是多了一个type="password"属性,我们通过了上面的代码给这个input添加了一个style属性并且属性值是width: 25px; 至于options能定义什么得HTML支持什么属性,就算不支持也能加上去,至于效果嘛得看浏览器得眼色了。不管什么view一定得先找到它的options能在哪里传
4月前 喜欢(0) 浏览(543) 评论(0)
社区公告
[公告] Yii中文网为优化用户体验进行大版本升级,老版网站会维持一段时间,可以点击顶部"旧版"链接访问旧版网站。
沟通交流

:492175201(技术1群)

:183620600(技术2群)

:291010569(技术3群)

订阅号 | 更多精彩内容推送
本周推荐