第一次嘗試寫文章,文筆可能很差,請見諒;
php
自學過一個月的PHP(從零開始);
自學過一個月的Web前端知識(從零開始,html+css+js);
自學一個月的Centos操做系統(從零開始,本人英語極差,常常須要有道翻譯~);
由於即將頂崗實習,因此決定擼一個實際上線的小程序(即刻翻譯);
css
嗯......想說明的一點是,因爲我審美觀確實不咋地,因此UI界面很是醜(請不要介意!);
html
小程序動畫界面:前端
1. onLoad: 關閉調試接口,用戶點擊是否受權我的信息;json
2. onShow:判斷本地緩存是否存在Token,如未存在則調用 user 接口獲取Token並緩存於本地;小程序
3.作完動畫跳轉到翻譯界面;
api
小程序翻譯界面:數組
框架使用的ThinkPHP5.1(第一次接觸的php框架);緩存
1.結構:
php框架
2.代碼:
BaseValidate.php:
use app\lib\exception\ParameterException;
use think\Validate;
class BaseValidate extends Validate
{
public function goCheck()
{
//對傳入的全部參數作校驗
獲取請求的全部參數;
$params = \request()->param();
全部請求作驗證
$result = $this->batch()->check($params);
若是不爲true,拋出參數錯誤異常
if(!$result)
{throw new ParameterException(['msg'=>$this->error*]);}
else
{return true;}
}
public function isNoEmpty($value,$rule='',$data='',$field='')
{
若是值爲空,直接返回false;
if(empty($value))
{return false;}
else
{return true;}
}
}
定義一個BaseValidate繼承自TP的的Validate;
定義一個方法用來作requset請求(GET,POST,DELETE....)的參數驗證;
定義方法 isNoEmpty(驗證規則,驗證參數不爲空);
複製代碼
CacheClean.php:
class CacheCleaner extends BaseValidate
{
protected $rule=[ 'AdministratorKey'=>'require|isNoEmpty' ];
protected $message=[ 'AdministratorKey'=>'請攜帶正確的管理員密鑰' ];
}
定義類 CacheClearner繼承自BaseValidate,管理員用來清除Token文件;複製代碼
Token.php:
class Token extends BaseValidate
{
protected $rule =[ 'code'=>'require|isNoEmpty' ];
protected $message=[ 'code'=>'code必須輸入且不能爲空' ];
}
定義類 Token 繼承自BaseValidate;
這裏的Code須要傳入的是 wx.login返回的Code碼
(用戶登陸憑證(有效期五分鐘)。
開發者須要在開發者服務器後臺調用 api,
使用 code 換取 openid 和 session_key 等信息);複製代碼
Translate.php:
class Translate extends BaseValidate
{
protected $rule=[ 'token'=>'require|isNoEmpty', 'q'=>'require|isNoEmpty', 'from'=>'require|isNoEmpty', 'to'=>'require|isNoEmpty', ];
protected $message=[ 'token'=>'token必須輸入且不能爲空', 'q'=>'q必須輸入且不能爲空', 'from'=>'from必須輸入且不能爲空', 'to'=>'to必須輸入且不能爲空', ];
}
定義類Translate 繼承自BaseValidate;
驗證token,驗證傳遞來的q(被翻譯文本),from(來源語種),to(目標語種)複製代碼
1.結構:
1.代碼:
Token.php:
use app\lib\exception\TokenException;
class Token
{
/**
* 根據token_salt和隨機字符串生成32位MD5字符串;
* @return string
*/
public static function generaeToken()
{
//32個字符組成一組隨機字符串
生成一段隨機字符串(getRandChar定義在common公共文件裏面);
$randChars = getRandChar(32);
鹽寫在配置文件裏.
$salt = config('token_salt');
返回MD5(隨機字符串+鹽)的 Token;
return md5($randChars . $salt);
}
/**
* 檢驗cache緩存中是否存在傳遞來的token
* @throws TokenException
*/
public static function needToken()
{
獲取本次請求的token;
$token = request()->param('token');
若是緩存文件中不存在傳來的token,拋異常
$result = cache($token);
if (!$result){
throw new TokenException();
}
}
}
複製代碼
Translate.php:
偷懶使用百度寫好的php文件(很容易理解,不過爲了快偷懶了......);
use app\lib\exception\BaiduException;
class Translate
{
protected $query;
protected $from;
protected $to;
/**
* 構造 傳遞q ,from .to
* Translate constructor.
* @param $query
* @param $from
* @param $to
*/
public function __construct($query,$from,$to)
{
$this->query=$query;
$this->from=$from;
$this->to=$to;
}
/**
* 主方法
* @return bool|mixed
*/
public function translate()
{
$args = array('q' => $this->query,'appid' => config('Bapp_id'),'salt' => config('B_salt'), 'from' => $this->from,'to' => $this->to,);
$args['sign'] = $this->buildSign($this->query, config('Bapp_id'), $args['salt'], config('Bapp_secret'));
$ret = $this->call(config('Blogin_url'), $args);
$ret = json_decode($ret,true);
$this->checkApiHasError($ret);
return $this->RecombinedData($ret);
}
/**
* 檢測接口是否調用失敗
* @param array $array
* @throws BaiduException
*/
public function checkApiHasError(array $array)
{
if(array_key_exists('error_code',$array)){throw new BaiduException(['msg'=>$array['error_msg'],'errorCode'=>$array['error_code']]);
}
}
/**
* 生成簽名
* @param $query
* @param $appID
* @param $salt
* @param $secKey
* @return string
*/
public function buildSign($query, $appID, $salt, $secKey)
{
$str = $appID . $query . $salt . $secKey;
$ret = md5($str);
return $ret;
}
/**
* 發起請求
* @param $url
* @param null $args
* @param string $method
* @param int $testflag
* @param int $timeout
* @param array $headers
* @return bool|mixed
*/
public function call($url, $args=null, $method="post", $testflag = 0, $timeout = 10, $headers=array())
{
$ret = false;
$i = 0;
while($ret === false)
{
if($i > 1) break;
if($i > 0){sleep(1);}
$ret = $this->callOnce($url, $args, $method, false, $timeout, $headers); $i++;}
return $ret;
}
/**
* 發起請求,判斷是post方式仍是get方式
* @param $url
* @param null $args
* @param string $method
* @param bool $withCookie
* @param int $timeout
* @param array $headers
* @return mixed
*/
public function callOnce($url, $args = null, $method = "post", $withCookie = false, $timeout = 10, $headers = array())
{
$ch = curl_init();
if ($method == "post")
{
$data = $this->convert($args);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_POST, 1);
} else{
$data = $this->convert($args);
if ($data)
{
if (stripos($url, "?") > 0) {$url .= "&$data";} else{$url .= "?$data";}
}
}
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
if (!empty($headers))
{
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
}
if ($withCookie)
{
curl_setopt($ch, CURLOPT_COOKIEJAR, $_COOKIE);
}
$r = curl_exec($ch);
curl_close($ch);
return $r;
}
/**
* URL encode 各個字段
* @param $args
* @return string
*/
public function convert(&$args)
{
$data = '';
if (is_array($args))
{
foreach ($args as $key => $val)
{
if (is_array($val))
{
foreach ($val as $k => $v)
{
$data .= $key . '[' . $k . ']=' . rawurlencode($v) . '&';
}
} else{
$data .= "$key=" . rawurlencode($val) . "&";
}
}
return trim($data, "&");
}
return $args;
}
/**
* 重組數據
* @param $array
* @return array
*/
public function RecombinedData($array)
{
$trans_result = $array['trans_result'];
$trans_result = $trans_result[0];
$src = $trans_result['src'];
$dst = $trans_result['dst'];
unset($trans_result);
return $data = ['from'=>$array['from'],'to'=>$array['to'],'src'=>$src,'dst'=>$dst];
}
}複製代碼
UserToken.php:
use app\lib\exception\WeChatException;
class UserToken extends Token{
protected $code;
protected $wxAppID;
protected $wxAppSecret;
protected $wxLoginUrl;
/**
* 構造 傳入小程序code
* UserToken constructor.
* @param $code
*/
public function __construct($code)
{
wx.login返回的Code
$this->code = $code;
小程序開發者的app_id
$this->wxAppID = config('app_id');
小程序開發者的app_secret
$this->wxAppSecret = config('app_secret');
把三個變量依次寫入login_url中
$this->wxLoginUrl = sprintf(config('login_url'),
$this->wxAppID, $this->wxAppSecret, $this->code);
}
/**
* 主方法
* @return string
* @throws WeChatException
*/
public function get()
{
封裝curl在common.php公共文件中(這裏就不貼出來);
$result = curl_get($this->wxLoginUrl);
返回結果json轉數組;
$wxResult = json_decode($result, true);
若是結果爲空,拋異常
if (!$wxResult){throw new Exception('獲取session_key及openID時異常,微信內部錯誤');}
若是數組索引存在errcode,拋異常,不然生成令牌寫入緩存並返回令牌
if (array_key_exists('errcode', $wxResult))
{$this->processLoginError($wxResult);}
else
{return $this->grantToken($wxResult);}
}
/**
* 生成令牌寫入緩存並返回令牌
* @param $wxResult
* @return string
*/
private function grantToken($wxResult)
{
//生成令牌 準備緩存數據 寫入緩存
//把令牌返回給客戶端
//key :令牌
//value:wxResult;
$token=$this->savaToCache($wxResult);
return $token;
}
/**
* 寫入緩存
* @param $wxResult
* @return string
*/
private function savaToCache($wxResult)
{
鍵爲token;
$key=self::generaeToken();
值爲openid等等
$value=json_encode($wxResult);
緩存文件過時時間爲0(永不過時);
$expire_in=0;
寫入緩存
$requst=cache($key,$value,$expire_in);
寫入不成功拋異常
if(!$requst){throw new TokenException(['msg'=>'服務器緩存異常','errorCode'=>10005]);}
返回key(token);
return $key;
}
/**
* 拋出code已被使用或是無效的code 的錯誤
* @param $wxResult
* @throws WeChatException
*/
private function processLoginError($wxResult)
{
throw new WeChatException(['msg' => $wxResult['errmsg'],'errorCode' => $wxResult['errcode']]);
}
}複製代碼
1.結構:
1.代碼:
BaseController.php:
use think\Controller;use app\api\service\Token as TokenService;
class BaseController extends Controller
{
驗證Token
public function checkHaveToken()
{
TokenService::needToken();
}
}
定義BaseController繼承自TP5的Controller
複製代碼
Token.php:
use app\api\service\UserToken;
use app\api\validate\CacheCleaner;
use app\api\validate\Token as TokenValidate;
use app\lib\exception\ParameterException;
use think\facade\Cache;use think\facade\Request;
class Token extends BaseController{
//根據客戶端傳來的code 生成token並返回
public function getToken()
{
//驗證
(new TokenValidate())->goCheck();
//邏輯
$token = (new UserToken(Request::param('code')))->get();
//返回
return ['token' => $token];
}
//測試接口
public function cleanAllCache()
{
//驗證
(new CacheCleaner())->goCheck();
//邏輯
if(!(Request::get('AdministratorKey')===config('app.adminKey')))
{throw new ParameterException(['msg'=>'請攜帶正確的管理員密鑰']);}
Cache::clear();
//返回
return '清除緩存文件成功';
}
}複製代碼
Translate.php:
use app\api\validate\Translate as TranslateValidate;
use app\api\service\Translate as TranslateService;
use think\facade\Request;
class Translate extends BaseController
{
//前置操做
protected $beforeActionList=['checkHaveToken'=>['only'=>'translate']];
//根據客戶端傳來的token作檢驗,檢驗成功則調用百度翻譯接口並返回接口數據給客戶端
public function translate()
{
//驗證
(new TranslateValidate())->goCheck();
//邏輯
$res=(new TranslateService(Request::param('q'),Request::param('from'),Request::param('to')))->translate();
//返回
return $res;
}
}複製代碼
1.結構:
2.代碼:
ExceptionHandler.php:
use think\exception\Handle;
use think\Log;
class ExceptionHandler extends Handle
{
/**
* 自定義全局異常處理
*/
private $code;
private $msg;
private $errorCode;
/**
* 根據異常類型判斷是否寫入日誌 並返回客戶端當前請求的url路徑
* @param \Exception $e
* @return \think\Response|\think\response\Json
*/
public function render(\Exception $e)
{
if ($e instanceof BaseException)
{
//若是是自定義的異常
$this->code = $e->code;
$this->msg = $e->msg;
$this->errorCode = $e->errorCode;
} else{
return parent::render($e);
}
$result = ['msg' => $this->msg,'error_code' => $this->errorCode,'request_url' => \request()->url()];
return json($result, $this->code);
}
/**
* 寫入錯誤日誌
* @param \Exception $e
*/
public function recordErrorLog(\Exception $e)
{
Log::init(['type' => 'File','path' => LOG_PATH,'level' => ['error']]);
Log::record($e->getMessage(), 'error');
}
}複製代碼
BaseException.php:
use think\Exception;
class BaseException extends Exception{
//HTTP狀態碼
public $code = 400;
//錯誤信息
public $msg = '錯誤';
//自定義錯誤碼
public $errorCode = 10000;
/**
* 根據錯誤生成錯誤http狀態碼,錯誤信息,和錯誤碼
* BaseException constructor.
* @param array $params
*/
public function __construct($params = [])
{
if(!is_array($params))
{
return ;
}
if(array_key_exists('code',$params)){$this->code=$params['code']; }
if(array_key_exists('errorCode',$params)){$this->errorCode=$params['errorCode'];}
if(array_key_exists('msg',$params)){$this->msg=$params['msg'];}
}
}
定義BaseException繼承自TP5的Exception複製代碼
BaiduException.php:
class BaiduException extends BaseException
{
public $code=401;
public $msg='接口調用錯誤啊個人天';
public $errorCode=10000;
}複製代碼
WeChatException.php:
class WeChatException extends BaseException
{
public $code = 400;
public $msg = '微信服務接口調用失敗';
public $errorCode = 999;
}複製代碼
TokenException.php:
class TokenException extends BaseException
{
public $code=401;
public $msg='Token過時或無效Token,請從新獲取';
public $errorCode=10000;
}複製代碼
ParameterException.php:
class ParameterException extends BaseException
{
public $code = 400;
public $msg = '參數錯誤';
public $errorCode = 10000;
}複製代碼
/**
* 封裝http請求
* @param $url
* @param int $httpCode
* @return mixed
*/
function curl_get($url, $httpCode = 0)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
//不作證書校驗,部署在Linux環境下請改成true
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
$file_contents = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return $file_contents;
}
/**
* 生成隨機字符串
* @param $length
* @return null|string
*/
function getRandChar($length)
{
$str = null;
$strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
$max = strlen($strPol) - 1;
for ($i = 0; $i < $length; $i++)
{
$str .= $strPol[rand(0, $max)];
}
return $str;
}複製代碼
最後把項目部署到服務器上(我在阿里雲購買的學生機和域名);
中間還遇到各類坑.(搭建環境,配置ssh證書等等);
反正很麻煩就對啦.
嗯...差很少就是這樣,獻醜了,嘻嘻.
_______ 陳三好