月度存档: 3 月 2018

js跨域获取自定义response-header

chrome会报这个错误:
Refused to get unsafe header
解决方案:

服务器脚本,比如php控制器init函数里面加入

header('Access-Control-Allow-Origin:http://xxx.com'); 
header('Access-Control-Allow-Methods:POST');  // 响应类型  
header('Access-Control-Allow-Headers:x-requested-with,content-type,authcode');  // 响应头设置
header('Access-Control-Expose-Headers: xxx自定义头字段');//important

前端代码

<!DOCTYPE html>
<html>
<head>
<script src="jquery-3.3.1.min.js">
</script>
<script>
$(document).ready(function(){
  $("button").click(function(){
    /*    $.ajax({
    type: "POST",
    url: "http://api.com/auth/sign-in",
    data:{"email":"xxx@qq.com","password":"123456"},
    dataType: "json",
    success: function(data,textStatus, request) {
        //console.log(data);
        console.log(textStatus);
        var obj = request.getResponseHeader("xxx自定义字段");//["content-type"]
        console.log(obj);
    }
        });*/

    var xhr = new XMLHttpRequest();
    xhr.withCredentials = true; //solution : Refused to get unsafe header in chrome,server setting : header('Access-Control-Expose-Headers: authcode');
                xhr.open('post', 'http://test.com/auth/sign-in',true);
                xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
                xhr.onload = function () {
                    console.log('load:', xhr.getAllResponseHeaders());
                    console.log(xhr.getResponseHeader('xxx自定义字段'));
                  console.log('=================================')};
                var data = "email=xxx@qq.com&password=123456";
                xhr.send(data);
});
})
</script>
</head>
<body>
<button>test</button>
</body>
</html>

yii2-restful-api-study6-api认证

api认证

1. 数据库里面把user表复制一份出来,新表名称为adminuser,里面新加一个字段access_token

2. 修改api/config/main.php

 'user' => [
            'identityClass' => 'common\models\Adminuser',
            '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',
        ],*/

3. 在api/controllers/下新建立AdminuserController.php类

<?php
namespace api\controllers;
use yii\rest\ActiveController;
use common\models\Article;
use yii\data\ActiveDataProvider;
use api\models\ApiLoginForm;
use api\models\SignupForm;
use common\models\Adminuser;
use yii\helpers\ArrayHelper;
use yii\filters\auth\QueryParamAuth;
use yii;

class AdminuserController extends ActiveController{
    public $modelClass = 'common\models\Adminuser';
    public function behaviors(){
        return ArrayHelper::merge(parent::behaviors(),[
            'authenticatior'=>[
                'class'=>QueryParamAuth::className(),
                 'optional' => [
                    'login',//客户端第一个访问login操作的时候哪来的token,yii\filters\auth\QueryParamAuth对外提供一个属性,用于过滤不需要验证的action
                    'signup'
                ],

            ]
            ]);
    }
    public function actionLogin(){
        $model = new ApiLoginForm();
        $model->username = \Yii::$app->request->post('username');
        $model->password = \Yii::$app->request->post('password');
        if($model->login()){
            return ['access_token'=>$model->login()];
        }else{
            $model->validate();//带错误信息的模型
            return $model;
        }
    }

    /*public function actionRegister(){
        $model = new Adminuser();
        $model->username="t";
        $model->password='t';
        $model->email="t";
        $model->save();
        return $model;
    }   */
     public function actionRegister(){
        $model = new SignupForm();
        $model->load(Yii::$app->getRequest()->getBodyParams(), '');
        if($model->signup()){
            return ['result'=>'注册成功'];
        }else{
            $model->validate();//带错误信息的模型
            return $model;
        }
    }  
}

4.在api/models/下新加入ApiLoginForm.php模型类(从common/models/LoginForm.php复制)

<?php
namespace api\models;

use Yii;
use yii\base\Model;
use common\models\Adminuser;

/**
 * Login form
 */
class ApiLoginForm extends Model
{
    public $username;
    public $password;
    public $rememberMe = true;

    private $_user;


    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            // username and password are both required
            [['username', 'password'], 'required'],
            // password is validated by validatePassword()
            ['password', 'validatePassword'],
        ];
    }

    /**
     * Validates the password.
     * This method serves as the inline validation for password.
     *
     * @param string $attribute the attribute currently being validated
     * @param array $params the additional name-value pairs given in the rule
     */
    public function validatePassword($attribute, $params)
    {
        if (!$this->hasErrors()) {
            $user = $this->getUser();
            if (!$user || !$user->validatePassword($this->password)) {
                $this->addError($attribute, 'Incorrect username or password.');
            }
        }
    }

    public function attributeLabels(){
        return[
            'username'=>'用户名',
            'password'=>'密码',
        ];
    }
    /**
     * Logs in a user using the provided username and password.
     *
     * @return bool whether the user is logged in successfully
     */
    public function login()
    {
        if ($this->validate()) {
            $accessToken = $this->_user->generateAccessToken();
            $this->_user->save();
            return $accessToken;
        }

        return false;
    }

    /**
     * Finds user by [[username]]
     *
     * @return User|null
     */
    protected function getUser()
    {
        if ($this->_user === null) {
            $this->_user = Adminuser::findByUsername($this->username);
        }

        return $this->_user;
    }
}

5.在common/models/下把User.php模型类复制一份命名为Adminuser.php,修改对应的类名及token验证的函数,新加一个生成令牌access-token的函数:

public function generateAccessToken(){
        $this->access_token = Yii::$app->security->generateRandomString();
        return $this->access_token;
    }

public static function findIdentityByAccessToken($token, $type = null)
    {
        return static::findOne(['access_token'=>$token]);
        //throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
    }

6.postman-post访问/adminusers/login获取access_token

7.修改api/models/ArticleController.php,加入阀门:

use yii\helpers\ArrayHelper;
use yii\filters\auth\QueryParamAuth;
......
public function behaviors(){
        return ArrayHelper::merge(parent::behaviors(),[
            'authenticatior'=>[
                'class'=>QueryParamAuth::className()
            ]
            ]);
    }

没有加入这个阀门之前,get访问/articles可以获取到所有文章列表,加入这个阀门后,需要带上第6步登录后获取的access_token才可以获取文章列表,需要注意的是,yii2系统默认的get请求上用access-token作为参数,为中划线

yii2-restful-api-study5-get&post

控制器中获取get,post请求的参数

Get:
Yii::$app->request->get($key, $default):
第一个参数($key)为用户get请求的key,第一个参数选填;第二个参数($default)是默认值,第二个参数选填;
不填参数得到的是get数据的数组。
Yii::$app->request->queryParams:得到的是数组,与Yii::$app->request->get()相等。

Post:
Yii::$app->request->post($key, $default):
第一个参数($key)为用户post请求的key,选填,
第二个参数($default)是默认值,第二个参数选填;
不填参数得到的是post数据的数组。
Yii::$app->request->bodyParams:得到的是数组,与Yii::$app->request->post()相等。

判断用户请求:
Yii::$app->request->isGet;
Yii::$app->request->isPost;

yii2-restful-api-study4-json&xml

http访问get链接得到一个xml的结果,而postman访问的get链接得到的是json的结果,怎么保持一致,

方法一.在输出之前,强制定义下输出格式

<?php
namespace api\controllers;

use yii\rest\Controller;
use api\models\Article;
use yii\db\Query;

//自定义资源

class Top3Controller extends Controller
{
    public function actionIndex()
    {
        //  \Yii::$app->response->format = \yii\web\Response::FORMAT_XML;
        \Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
         $top3 = (new Query())->from('article')->select(['*'])->orderBy('id desc')->limit(3)->all();
        return $top3;
    }
}

?>

方法二

api\config\main.php文件的components中增加对response的配置

'response' => [
    'class' => 'yii\web\Response',
    'on beforeSend' => function ($event) {
        $response = $event->sender;
        $response->format = yii\web\Response::FORMAT_JSON;
    },
],

yii2-restful-api-study3-自定义资源

自定义资源

1.新建控制器,注意继承自Controller,因为没有对应的表资源

<?php
namespace api\controllers;

use yii\rest\Controller;
use api\models\Article;
use yii\db\Query;

//自定义资源

class Top3Controller extends Controller
{
    public function actionIndex()
    {
         $top3 = (new Query())->from('article')->select(['*'])->orderBy('id desc')->limit(3)->all();
        return $top3;
    }
}

?>

2.修改api/config/main.php,rules加入规则

 ['class'=>'yii\rest\UrlRule',
                 'controller'=>'top3',
                 'except'=>['delete','create','update','view'],
                 'pluralize'=>false,//url不带复数
                 ],

Yii2-restful-api-study2-路由传参&发送邮件

路由传参

接上一节,修改api/main.php文件

'urlManager' => [
            'enablePrettyUrl' => true,
            'enableStrictParsing' => true,
            'showScriptName' => false,
            'rules' => [
                [
                'class' => 'yii\rest\UrlRule', 
                'controller' => ['user','test'],
                'except' => ['delete'], 
                'extraPatterns'=>[
                    'GET auth/sign-in' => 'login',
                    'POST query' => 'querydb',
                    'POST update' => 'updatedb',
                    'POST del' => 'deldb',
                    'GET sendm' => 'send1',
                    'POST add' => 'adddb',
                    'POST auth/sign-in/<name>'=>'loginp',
                ],
                ]
            ],
        ],

两种形式,一种直接写在link上,控制器的函数里面作为参数接收对应值,参考TestController.php里面的loginp的函数实现;另外一种,参考TestController.php里面的adddb函数实现;

<?php
namespace api\controllers;
use yii\rest\ActiveController;
use yii\data\ActiveDataProvider;
use api\models\test;
use Yii;
use yii\web\Controller;


class TestController extends ActiveController
{
    public $modelClass = 'api\models\Test';
    public function actionLogin()
    {
        $response = \Yii::$app->response;
       // $response->statusCode = '404';
        $headers = \Yii::$app->response->headers;

        // 增加一个 Pragma 头,已存在的Pragma 头不会被覆盖。
        $headers->add('Pragma', 'no-cache');
        $headers->add('authCode', '12312324567890');

        return ['statusCode'=>'S001','message'=>'success'];
    }

    public function actions(){
        $actions=parent::actions();
        unset($actions['index']);
        return $actions;
    }

    public function actionIndex(){
        $modelClass = $this->modelClass;
        return new ActiveDataProvider(['query'=>$modelClass::find()->asArray(),
        'pagination'=>['pageSize'=>20], //每页二十条记录,翻页实现http://t2.com/tests?page=2
        ]);
    }

    public function actionLoginp($name)
    {
        return 'postp'.$name;
    }

    public function actionQuerydb(){
        $test = Test::find()->one(); //取出一条记录
        $a = $this->modelClass;
        //return $a::find()->where(['like','title',$_POST['keyword']])->all();
        return $test;

    }

    public function actionUpdatedb(){
        $test = Test::findOne(8); //更新id=8的记录
        $test->name = "new12345";
        $test->save();
       return $test;
    }

    public function actionDeldb(){
        $test = Test::findOne(8); //del
        $test->delete();
        return $test;

    }

    public function actionAdddb(){  //add

        $tt=Yii::$app->request->post();  //获取post数据

        $t1=$tt['name'];//$t1=$_POST['name'];//这样也行,不过推荐上面的做法,框架进行了字符串过滤
        $t2=$tt['t2'];
        //  return $t1;
        $test = new Test(); //add
        $test->name = "code".$t1;
        $test->t2="c2".$t2;
        $test->save();
        return $test;

    }
//要在common/main-local.php里面添加mailer设置
    public function actionSend1(){
       // return "sendmail";
        $mail= Yii::$app->mailer->compose();
        $mail->setFrom('7138784@qq.com');   
        $mail->setTo('nbllq@qq.com'); 
        $mail->setSubject("mail-test");  
        //$mail->setTextBody('txtxtxtx');   //发布纯文字文本
        $mail->setHtmlBody("<br>askme");    //发布可以带html标签的文本
        if($mail->send())  
            return "success";  
        else  
            return "fail";   
        //die(); 
    }
}
?>

发送邮件,yii-advanced-app-2.0.13这个版本需要php7.0

在common/params.php配置mail对应参数:

<?php
return [
    'adminEmail' => '7138784@qq.com',
    'supportEmail' => '7138784@qq.com',
    'senderEmail' => '7138784@qq.com',
    'senderName' => 'qblog',
    'user.passwordResetTokenExpire' => 3600,
];

在common/main-local.php里面添加mailer设置:

'mailer' => [  
            'class' => 'yii\swiftmailer\Mailer',  
             'useFileTransport' =>false,//这句一定有,false发送邮件,true只是生成邮件在runtime文件夹下,不发邮件
            'transport' => [  
                'class' => 'Swift_SmtpTransport',  
                'host' => 'smtp.qq.com',  //每种邮箱的host配置不一样
                'username' => '7138784@qq.com',  
                'password' => 'your code',  
                'port' => '465',  
                'encryption' => 'ssl',

                ],   
         ], 

Yii2-restful-api-study1

准备工作

0.pre-install xampp,开启服务器和数据库,php版本号5.5+,将yourpath/xampp/bin写入环境变量

    //mac上配置环境变量
    vi ~/.bash_profile 
    export PATH=/Applications/xampp/xamppfiles/bin/:$PATH

windows 我的电脑 – 属性 – 更改设置 – 高级 – 环境变量 – 系统变量 – Path
添加 e:\xampp\php;e:\xampp\php\ext;
windows下面有时候会提示:api-ms-win-crt-runtime-l1-1-0.dll 丢失,这就需要去下载一个补丁
https://www.microsoft.com/zh-cn/download/confirmation.aspx?id=49077
有的系统提示补丁装不上,可能需要安装win7 sp1
1.yiiframework上下载advanced安装包,解压 tar zxvf yii-advanced-app-2.0.13.tgz
2.进入advanced 目录: 执行 php init
3.配置database , common/config/main-local.php,修改dbname为advanced_yii2
4.新建advanced_yii2数据库,执行 php yii migrate 自动会在新建的数据库里添加两张表,
5.访问http://www.xxxx.com/…frontend/web/ 注册一个用户,确保用户signup成功
到这里没出错的话,yii2框架安装完毕!
ps:web/index.php里面可以修改是否开启debug模式
ps:确保apache开启了rewrite_mod(httpd.conf里面修改), 入口文件夹web下建立好.htaccess文件(windows下cmd命令: ren 1.txt .htaccess)如下:

Options +FollowSymLinks
IndexIgnore */*

RewriteEngine on

# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

# otherwise forward it to index.php
RewriteRule . index.php

RewriteRule \.svn\/  /404.html
RewriteRule \.git\/  /404.html

接下来开始restful-api之旅:

1. 修改api/config/main.php

cd advanced
cp -r backend api //把backend拷贝一份成api
cd api/config
vim main.php //修改三个地方

第一个地方:改id为app-api,改控制器命名空间的路径为api\controllers

return [
    'id' => 'app-api',
    'basePath' => dirname(__DIR__),
    'controllerNamespace' => 'api\controllers',
]

第二个地方:修改路由规则,按restful风格来,以后加个控制器,这个地方都要改

`'urlManager' => [
    'enablePrettyUrl' => true,
    'enableStrictParsing' => true,
    'showScriptName' => false,
    'rules' => [
        ['class' => 'yii\rest\UrlRule', 
        // 'controller' => ['user','article'], //多个控制器支持restful的写法
        'controller' => 'user', //注意这个地方有个逗号,对应版本yii-advanced-app-2.0.13
        ] //注意这个地方没有标点符号,我找这个错误找了2个多小时,泪奔~~
    ],
]`

第三个地方:
common/config/bootstrap.php下面添加
Yii::setAlias('@api', dirname(dirname(__DIR__)) . '/api');

2.在api/controllers/下面新加个控制器文件UserController.php

<?php
namespace api\controllers;
use yii\rest\ActiveController;

class UserController extends ActiveController
{
    public $modelClass = 'common\models\User';//就定义了个模型指针,然后框架就把restful相关功能全部实现一遍了,是不是很爽
}

3.修改common\config\bootstrap.php

添加一个新的别名:
Yii::setAlias(‘应用名’, dirname(dirname(DIR)) . ‘/目录名’);
比如:Yii::setAlias(‘mobile’, dirname(dirname(DIR)) . ‘/mobile’);
ps:如果漏掉这个,页面会报错:’Unable to resolve the request “site/error”.’

4. postman 测试

get访问http://www.xxxx.com/…api/web/users
可以看到之前注册的用户信息,user表比较特殊,post/put暂时不起作用,可以自建test数据表及对应模型进行测试
建议弄个虚拟目录,/etc/hosts里面绑个开发用的域名,指向api/web,调试起来更方便

5.上面4步弄好,post只支持x-www-form-urlencoded格式数据,如果要支持post raw json数据,则需要修改api/config/main-local.php

$config = [
    'components' => [
        'request' => [
            // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
            'cookieValidationKey' => 'WWiWFkBqTXFYeC6e2gpe3ZRi-LxjkxnI',
            'parsers' =>[
                'application/json'=>'yii\web\JsonParser',
                //'text/json' => 'yii\web\JsonParser',
            ],//注意这个逗号不要忘记加了,啥也不说了,泪奔~
        ],
    ],
];

ps: 对应的数据model里面需要定义如下函数(即数据库字段对应的验证规则),否则post和put不起作用:

public function rules()
    {
        return [
            [['name', 'pwd'], 'string', 'max' => 11],
        ];
    }

As3去掉字符串空格

    //去掉字符串空格
            public static function trimStr(str:String):String{
             var _str:String=str;                
            while(_str.substr(0,1)==" "){
                _str=_str.substr(1);
             }
            while(_str.substr(-1,1)==" "){
                _str=_str.substr(0,_str.length-1);
             }
            return _str;
         }

php绕开ssl验证&获取包含headers的所有response

<?php
$curl = curl_init();
$url= "https://testapi.xxxx.author/signin";
$email = $_POST["email"];
$pwd = $_POST["password"];
curl_setopt_array($curl, array(
  CURLOPT_URL => $url,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "email=".$email."&password=".$pwd,
  CURLOPT_HTTPHEADER => array(
    "cache-control: no-cache",
    "content-type: application/x-www-form-urlencoded",
    "postman-token: 1ebc8292-5e01-a6fa-9f91-26d3126892e0"
  ),
));

curl_setopt($curl, CURLOPT_HEADER, true);//get all respose include headers & body
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); //pass https ssl verification

//$result = curl_getinfo($curl, CURLINFO_HTTP_CODE);
//$headers = apache_request_headers();
$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
?>

Ubuntu 16.04 上安装 MySQL 5.7 教程

Ubuntu 16.04 上安装 MySQL 5.7 教程

介绍
MySQL 是一种开源数据库管理系统,通常作为流行的LAMP(Linux,Apache,MySQL,PHP / Python / Perl)堆栈的一部分安装。它使用关系数据库和SQL(结构化查询语言)来管理其数据。

安装的方式很简单:更新软件包索引,安装mysql-server软件包,然后运行附带的安全脚本即可。

sudo apt-get update
sudo apt-get install mysql-server
sudo mysql_secure_installation
本教程将介绍如何在 Ubuntu 16.04 服务器上安装 MySQL 5.7 版本。但是,如果要将现有的 MySQL 安装更新为 5.7 版,可以阅读此 MySQL 5.7 更新指南。

步骤1 – 安装MySQL
在 Ubuntu 16.04 中,默认情况下,只有最新版本的 MySQL 包含在 APT 软件包存储库中。在撰写本文时,那是 MySQL 5.7

要安装它,只需更新服务器上的包索引并安装默认包 apt-get。

sudo apt-get update
sudo apt-get install mysql-server
系统将提示您在安装过程中创建 root 密码。选择一个安全的密码,并确保你记住它,因为你以后需要它。接下来,我们将完成 MySQL 的配置。

步骤2 – 配置MySQL
因为是全新安装,您需要运行附带的安全脚本。这会更改一些不太安全的默认选项,例如远程 root 登录和示例用户。在旧版本的 MySQL 上,您需要手动初始化数据目录,但 Mysql 5.7 已经自动完成了。

运行安全脚本。

sudo mysql_secure_installation
这将提示您输入您在步骤1中创建的 root 密码。您可以按 Y,然后 ENTER 接受所有后续问题的默认值,但是要询问您是否要更改 root 密码。您只需在步骤 1 中进行设置即可,因此无需现在更改。

最后,我们来测试MySQL安装。

步骤3 – 测试MySQL
按上边方式安装完成后,MySQL应该已经开始自动运行了。要测试它,请检查其状态。

systemctl status mysql.service
您将看到类似于以下内容的输出:

mysql.service – MySQL Community Server
Loaded: loaded (/lib/systemd/system/mysql.service; enabled; vendor preset: en Active: active (running) since Wed 2016-11-23 21:21:25 UTC; 30min ago Main PID: 3754 (mysqld) Tasks: 28 Memory: 142.3M CPU: 1.994s CGroup: /system.slice/mysql.service └─3754 /usr/sbin/mysqld
如果MySQL没有运行,您可以启动它:

sudo systemctl mysql start
如果额外的检查,您可以尝试使用该 mysqladmin 工具连接到数据库,该工具是允许您运行管理命令的客户端。例如,该命令表示以 root(-u root)方式连接到 MySQL ,提示输入密码(-p)并返回版本。

mysqladmin -p -u root version
你应该看到类似的输出:

mysqladmin Ver 8.42 Distrib 5.7.16, for Linux on x86_64
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners.
Server version 5.7.16-0ubuntu0.16.04.1 Protocol version 10 Connection Localhost via UNIX socket UNIX socket /var/run/mysqld/mysqld.sock Uptime: 30 min 54 sec
Threads: 1 Questions: 12 Slow queries: 0 Opens: 115 Flush tables: 1 Open tables: 34 Queries per second avg: 0.006
这意味着MySQL正在运行。

结论
现在您的服务器上已经安装了一个可以使用的 MySQL 5.7 了。