栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > PHP

PHP开发API小技巧

PHP 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

PHP开发API小技巧

restful api

传统api
  • 获取用户信息 get /api/user/read
  • 更新用户信息 post /api/user/updat
  • 新增用户信息 post/api/user/add
  • 删除用户信息 post/api/user/delete

restful api
  • 获取用户信息 get /api/user/1
  • 更新用户信息put /api/user/1
  • 新增用户信息 post /api/user
  • 删除用户信息deletean/api/user/1

API数据结构格式
  • HTTP状态码使用TK自带的json实现
  • status 业务状态码
  • message 提示信息
  • data 数据层
function show($status, $message, $data=[], $httpCode=200) {

    $data = [
 'status' => $status,
 'message' => $message,
 'data' => $data,
    ];

    return json($data, $httpCode);
}

不可预知的内部异常api数据输出解决方案
  • TP5是通过thinkphp-library-think-exception-Handle.php-render来呈现异常数据(但是客户端识别不到这种异常)
  • config配置exception_handle填写异常类路径
    • 'exception_handle' => 'appcommonlibexceptionApiHandleException',
class ApiHandleException extends  Handle {

    
    public $httpCode = 500;

    public function render(Exception $e) {
 // 还原正常报错,上线后为flase(服务端开发)
 if(config('app_debug') == true) {
     return parent::render($e);
 }
 if ($e instanceof ApiException) {
     $this->httpCode = $e->httpCode;
 }
 return  show(0, $e->getMessage(), [], $this->httpCode);
    }
}

  • 支持状态码修改
class ApiException extends Exception {

    public $message = '';
    public $httpCode = 500;
    public $code = 0;
    
    public function __construct($message = '', $httpCode = 0, $code = 0) {
 $this->httpCode = $httpCode;
 $this->message = $message;
 $this->code = $code;
    }
}

API数据安全

  • 背景
    • 接口请求地址和参数暴露
    • 重要接口返回数据明文暴露
    • APP登录态请求的数据完全性问题
    • 代码层的数据完全问题
  • 解决方式就是各种的加密:MD5 AES(对称加密) RSA(非对称,效率较低)
    • 基本参数放入header
    • 每次http请求都携带sign (有效时间,唯一性)
    • sign唯一性保证
    • 请求参数、返回数据按安全性适当加密
    • access token
    • 客户端和服务器端时间不一致性解决方案
      • 解决:获取服务端时间,客户端拿到服务端正确时间进行对比。然后如果有差值,然后把差值在计算的时候补上
    public static function setSign($data = []) {
 // 1 按字段排序
 ksort($data);
 // 2拼接字符串数据  &
 $string = http_build_query($data);
 // 3通过aes来加密
 $string = (new Aes())->encrypt($string);

 return $string;
    }
    public static function checkSignPass($data) {
 $str = (new Aes())->decrypt($data['sign']);

 if(empty($str)) {
     return false;
 }

 // diid=xx&app_type=3
 parse_str($str, $arr);
 if(!is_array($arr) || empty($arr['did'])
     || $arr['did'] != $data['did']
 ) {
     return false;
 }
 // 有效时间:时间间隔不能超过60s (时间使用13位时间戳,这样唯一性会比较强)
 if(!config('app_debug')) {
     if ((time() - ceil($arr['time'] / 1000)) > config('app.app_sign_time')) {
  return false;
     }
     //echo Cache::get($data['sign']);exit;
     // 唯一性判定
     if (Cache::get($data['sign'])) {
  return false;
     }
 }
 return true;
    }
    public function checkRequestAuth() {
 // 首先需要获取headers
 $headers = request()->header();
 // todo

 // sign 加密需要 客户端工程师 , 解密:服务端工程师
 // 1 headers body 仿照sign 做参数的加解密
 // 2
 //  3

 // 基础参数校验
 if(empty($headers['sign'])) {
     throw new ApiException('sign不存在', 400);
 }
 if(!in_array($headers['app_type'], config('app.apptypes'))) {
     throw new ApiException('app_type不合法', 400);
 }

 // 需要sign
 if(!IAuth::checkSignPass($headers)) {
     throw new ApiException('授权码sign失败', 401);
 }
 Cache::set($headers['sign'], 1, config('app.app_sign_cache_time'));

 // 存储方式 :1、文件  2、mysql 3、redis
 $this->headers = $headers;
    }

API接口文档编写(API入参,出参的格式)
  • 有利于客户端工程师熟悉接口
  • 有利于服务端工程师接手项目
  • API接口地址
  • 请求方式
  • 入参格式
  • 出参格式
  • http code

APP版本升级
  • APP更新迭代快
  • APP是安装在用户手机设备上
  • APP版本升级的类型
    • 用户自主选择更新
    • 强制更新
  • 开设API接口
  • APP识别接口做相关判定3
    
    public function init() {
 // app_type 去ent_version 查询
 $version = model('Version')->getLastNormalVersionByAppType($this->headers['app_type']);

 if(empty($version)) {
     return new ApiException('error', 404);
 }

 if($version->version > $this->headers['version']) {
     $version->is_update = $version->is_force == 1 ? 2 : 1;
 }else {
     $version->is_update = 0;  // 0 不更新 , 1需要更新, 2强制更新
 }

 // 记录用户的基本信息 用于统计
 $actives = [
     'version' => $this->headers['version'],
     'app_type' => $this->headers['app_type'],
     'did' => $this->headers['did'],
 ];
 try {
     model('AppActive')->add($actives);
 }catch (Exception $e) {
     // todo
     //Log::write();
 }

 return show(config('code.success'), 'OK', $version, 200);
    }

APP端异常、性能监控及定位分析
  • 常见的移动端异常
    • Crash在使用APP的过程中突然发生闪退现象
    • 卡顿出现画面的卡顿
    • Exception 程序被catch起来的exception
    • ANR 出现提示无响应弹框(android)
  • 端异常数据收集方案解剖(统计数据)
    • Crash 卡顿Exception ANR次数
    • 影响用户数
  • 第三方成熟服务
    • 听云
    • oneapm
    • 端APP只需要集成第三方平台提供的sdk

APP推送
  • 原始-轮训法处理
    • APP每隔一段时间向后端发送请求
  • 第三方:极光推送
    • PHP->第三方推送消息凭条jpush->APP

点赞功能
    public function save() {
 $id = input('post.id', 0 , 'intval');
 if(empty($id)) {
     return show(config('code.error'), 'id不存在', [], 404);
 }
 // 判定这个 id的新闻文章  -> ent_news 小伙伴自行加入
 $data = [
     'user_id' => $this->user->id,
     'news_id' => $id,
 ];
 // 查询库里面是否存在 点赞
 $userNews = model('UserNews')->get($data);
 if($userNews) {
     return show(config('code.error'), '已经点赞过,不能再次点赞', [], 401);
 }

 try {
     $userNewsId = model('UserNews')->add($data);
     if($userNewsId) {
  model('News')->where(['id' => $id])->setInc('upvote_count');
  return show(config('code.success'), 'OK', [], 202);
     }else {
  return show(config('code.error'), '内部错误 点赞失败', [], 500);
     }
 }catch (Exception $e) {
     return show(config('code.error'), '内部错误 点赞失败', [], 500);
 }

    }

    
    public function delete() {
 $id = input('delete.id', 0, 'intval');
 if(empty($id)) {
     return show(config('code.error'), 'id不存在', [], 404);
 }

 // 判定这个 id的新闻文章  -> ent_news 小伙伴自行加入
 $data = [
     'user_id' => $this->user->id,
     'news_id' => $id,
 ];
 // 查询库里面是否存在 点赞
 $userNews = model('UserNews')->get($data);
 if(empty($userNews)) {
     return show(config('code.error'), '没有被点赞过,无法取消', [], 401);
 }

 try {
     $userNewsId = model('UserNews')
  ->where($data)
  ->delete();
     if($userNewsId) {
  model('News')->where(['id' => $id])->setDec('upvote_count');
  return show(config('code.success'), 'OK', [], 202);
     }else {
  return show(config('code.error'), '取消失败', [], 500);
     }
 }catch (Exception $e) {
     return show(config('code.error'), '内部错误 点赞失败', [], 500);
 }
    }

小知识点:
  • 后台权限控制使用封装base类库来达到统一管理的目的,_initialize
  • 调试小技巧 : $this->getLastSql(); halt(); echo ** exit;
  • mysql default null影响索引性能。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/226908.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号