第一次尝试写文章,文笔可能很差,请见谅;
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证书等等);
反正很麻烦就对啦.
嗯...差很少就是这样,献丑了,嘻嘻.
_______ 陈三好