2

一、接口编程

1、背景

随着互联网的发展, 尤其是移动互联为代表的Web3.0时代. 客户端层出不穷, 以APP、微信、PC浏览器为代表, 服务端业务逻辑是基本一致的。那么有没有一种方式可以做到”服务端一次编写, 客户端随时接入”呢?

流行的方案就是"接口编程"

wpsC93C.tmp.jpg

2、接口编程

API(Application Programming Interface,应用程序编程接口),程序之间交互数据的一种方式。

以 浏览器和服务器之间的数据交互(ajax请求) 为例:

wpsC96C.tmp.jpg

请求方式:get方式 post方式

请求协议:http协议 https协议

返回参数格式:普通字符串、json格式、xml格式,现在通用的是json格式

这里的接口,不是面向对象中的interface接口。

数据交互中的接口:实际上,就是通过url请求到的地址中的代码(框架中就是一个控制器的方法)

二、RESTFul设计风格

1、Rest与RestFul

REST(英文:Representational State Transfer,简称REST),表述性状态转移,指的是一组架构原则。

Restful: 遵守了rest 原则 的web服务或web应用。

2、API设计六要素

资源路径(URI)、HTTP动词(Method)、过滤信息(query-string)、状态码(Status-code)、错误信息(Error)、返回结果(Result)

wpsB102.tmp.png

1)资源路径URI

资源:所有在服务器保存的数据(如:音乐/视频/文章/个人信息...)都是服务器端资源.(项目中资源通常都是指的数据表的一条数据)

URI(Uniform Resource Identifier):统一资源标志符,包含URL和URN。

URL(Uniform Resource Locator):统一资源定位符

URN(Uniform Resource Name):统一资源命名

在HTTP协议中, URI的组成如下:Schema://host[:port]/path[?query-string]

Schema: 使用的协议类型, 如http/https/ftp等

host: 主机域名或IP

port: 端口号(可选)

path: 路径

query-string: 查询参数(可选)

例子:

http://www.tpshop.com/users

https://www.tpshop.com:8080/users?id=100

2)HTTP动词(请求方式)

对于资源, 一般有4个操作, CURD(增/删/改/查)

GET: 从服务器获取资源(一项或多项)

POST: 在服务器新建一个资源

PUT: 在服务器更新资源, 服务器返回完整的属性

DELETE: 从服务器删除资源

3)过滤信息

通常也叫做请求参数或查询字符串。

4)响应状态码

服务端返回的信息, 用来告诉客户端操作结果。

常见状态码:

状态码含义说明
200OK操作成功, 并返回数据
201CREATED新建成功
204NO CONTENT删除成功
400BAD REQUEST请求语法错误
403Forbidden请求没有权限的资源
404NOT FOUND没有找到请求的资源

GET

200(OK) - 表示已在响应中发出 
204(无内容) - 资源有空表示 
301(Moved Permanently) - 资源的URI已被更新 
303(See Other) - 其他(如,负载均衡) 
304(not modified)- 资源未更改(缓存) 
400 (bad request)- 指代坏请求(如,参数错误) 
404 (not found)- 资源不存在 
406 (not acceptable)- 服务端不支持所需表示 
500 (internal server error)- 通用错误响应 
503 (Service Unavailable)- 服务端当前无法处理请求 

POST

200(OK)- 如果现有资源已被更改 
201(created)- 如果新资源被创建 
202(accepted)- 已接受处理请求但尚未完成(异步处理) 
301(Moved Permanently)- 资源的URI被更新 
303(See Other)- 其他(如,负载均衡) 
400(bad request)- 指代坏请求 
404 (not found)- 资源不存在 
406 (not acceptable)- 服务端不支持所需表示 
409 (conflict)- 通用冲突 
412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突) 
415 (unsupported media type)- 接受到的表示不受支持 
500 (internal server error)- 通用错误响应 
503 (Service Unavailable)- 服务当前无法处理请求 

PUT

200 (OK)- 如果已存在资源被更改 
201 (created)- 如果新资源被创建 
301(Moved Permanently)- 资源的URI已更改 
303 (See Other)- 其他(如,负载均衡) 
400 (bad request)- 指代坏请求 
404 (not found)- 资源不存在 
406 (not acceptable)- 服务端不支持所需表示 
409 (conflict)- 通用冲突 
412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突) 
415 (unsupported media type)- 接受到的表示不受支持 
500 (internal server error)- 通用错误响应 
503 (Service Unavailable)- 服务当前无法处理请求 

DELETE

200 (OK)- 资源已被删除 
301 (Moved Permanently)- 资源的URI已更改 
303 (See Other)- 其他,如负载均衡 
400 (bad request)- 指代坏请求 
404 (not found)- 资源不存在 
409 (conflict)- 通用冲突 
500 (internal server error)- 通用错误响应 
503 (Service Unavailable)- 服务端当前无法处理请求 

5)错误信息

如果状态码是4xx或者5xx, 需要告诉客户端对应的错误信息. 通常以Json格式返回:

{

    "error": "错误信息",

}

6)返回结果

针对不同的操作, 服务需要返回的结果应该符合这样的规范

GET /collections -- 返回资源列表(数组)

GET /collections/:id -- 返回单个资源 eg. /collections/1

POST /collections -- 返回新生成的资源

PUT /collections/:id -- 返回资源的完整属性

DELETE /collections/:id -- 返回204状态码+空文档

实际开发中,通常会将 状态码、错误信息、返回数据,都放到返回结果中。比如

{"code":200, "msg":"success", "data":{"id":1,"goods_name":"tp"}}

3、RestFul接口设计风格

RESTFul是一种软件设计风格, 主要用于有客户端与服务端交互的软件.

RESTFul 是目前最流行的 API 设计规范,用于 Web 数据接口的设计。

RESTFul风格的数据接口示例:

以新闻资源为例:URI及HTTP动词设计如下

HTTP动词URI路径说明
GEThttp://域名/news获取列表数据
GEThttp://域名/news/:id根据id获取一条数据
POSThttp://域名/news添加一条数据
PUThttp://域名/news/:id根据id修改一条数据
DELETEhttp://域名/news/:id根据id删除一条数据

三、RestFul实战

1、TP框架中的资源路由

  1. 创建api模块
php think build --module api
  1. 创建news控制器
php think make:controller api/News
  1. 设置路由(application/route.php)
\think\Route::resource('news','api/news');

相当于分别设置了以下路由:

\think\Route::get('news','api/news/index');
\think\Route::get('news/create','api/news/create');
\think\Route::post('news','api/news/save');
\think\Route::get('news/:id','api/news/read');
\think\Route::get('news/:id/edit','api/news/edit');
\think\Route::put('news/:id','api/news/update');
\think\Route::delete('news/:id','api/news/delete');

设置后会自动注册7个路由规则,如下:

标识请求类型生成路由规则对应操作方法(默认)
indexGETnewsindex
createGETnews/createcreate
savePOSTnewssave
readGETnews/:idread
editGETnews/:id/editedit
updatePUTnews/:idupdate
deleteDELETEnews/:iddelete
  1. 修改News控制器,返回json格式数据
<?php

namespace app\api\controller;

use think\Controller;
use think\Request;

class News extends Controller
{
    /**
     * 显示资源列表
     *
     * @return \think\Response
     */
    public function index()
    {
        return json(['code' => 200, 'msg' => 'success', 'data'=>'index']);
    }

    /**
     * 显示创建资源表单页.
     *
     * @return \think\Response
     */
    public function create()
    {
        return json(['code' => 200, 'msg' => 'success', 'data'=>'create']);
    }

    /**
     * 保存新建的资源
     *
     * @param  \think\Request  $request
     * @return \think\Response
     */
    public function save(Request $request)
    {
        return json(['code' => 200, 'msg' => 'success', 'data'=>'save']);
    }

    /**
     * 显示指定的资源
     *
     * @param  int  $id
     * @return \think\Response
     */
    public function read($id)
    {
        return json(['code' => 200, 'msg' => 'success', 'data'=>'read']);
    }

    /**
     * 显示编辑资源表单页.
     *
     * @param  int  $id
     * @return \think\Response
     */
    public function edit($id)
    {
        return json(['code' => 200, 'msg' => 'success', 'data'=>'edit']);
    }

    /**
     * 保存更新的资源
     *
     * @param  \think\Request  $request
     * @param  int  $id
     * @return \think\Response
     */
    public function update(Request $request, $id)
    {
        return json(['code' => 200, 'msg' => 'success', 'data'=>'update']);
    }

    /**
     * 删除指定资源
     *
     * @param  int  $id
     * @return \think\Response
     */
    public function delete($id)
    {
        return json(['code' => 200, 'msg' => 'success', 'data'=>'delete']);
    }
}

通过postman 分别访问以下七个地址:

请求方式     请求地址
get            http://www.tpshop.com/news
get            http://www.tpshop.com/news/create
post        http://www.tpshop.com/news
get            http://www.tpshop.com/news/33
get            http://www.tpshop.com/news/33/edit
put            http://www.tpshop.com/news/33
delete        http://www.tpshop.com/news/33

2、ajax请求restful接口

public目录下,创建测试文件 api.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax请求restful接口</title>
    <script src="/static/admin/js/jquery-1.8.1.min.js"></script>
</head>
<body>
<input type="button" id="index" value="index">
<input type="button" id="create" value="create">
<input type="button" id="save" value="save">
<input type="button" id="read" value="read">
<input type="button" id="edit" value="edit">
<input type="button" id="update" value="update">
<input type="button" id="delete" value="delete">
<script>
    $(function(){
        $('#index').click(function(){
            $.ajax({
                "url":"/news",
                "type":"get",
                "data":"",
                "dataType":"json",
                "success":function(res){
                    console.log(res);
                }
            });
        });
        $('#create').click(function(){
            $.ajax({
                "url":"/news/create",
                "type":"get",
                "data":"",
                "dataType":"json",
                "success":function(res){
                    console.log(res);
                }
            });
        });
        $('#save').click(function(){
            $.ajax({
                "url":"/news",
                "type":"post",
                "data":"",
                "dataType":"json",
                "success":function(res){
                    console.log(res);
                }
            });
        });
        $('#read').click(function(){
            $.ajax({
                "url":"/news/33",
                "type":"get",
                "data":"",
                "dataType":"json",
                "success":function(res){
                    console.log(res);
                }
            });
        });
        $('#edit').click(function(){
            $.ajax({
                "url":"/news/33/edit",
                "type":"get",
                "data":"",
                "dataType":"json",
                "success":function(res){
                    console.log(res);
                }
            });
        });
        $('#update').click(function(){
            $.ajax({
                "url":"/news/33",
                "type":"put",
                "data":"",
                "dataType":"json",
                "success":function(res){
                    console.log(res);
                }
            });
        });
        $('#delete').click(function(){
            $.ajax({
                "url":"/news/33",
                "type":"delete",
                "data":"",
                "dataType":"json",
                "success":function(res){
                    console.log(res);
                }
            });
        });
    });
</script>
</body>
</html>

3、请求伪装

部分客户端(比如低版本浏览器)可能仅支持get请求、post请求,不支持delete请求和put请求。

TP框架提供了对“请求伪装”的支持,可以使用post请求携带_method参数,伪装成其他请求。

<img src="https://gitee.com/li_ke321/Image/raw/master/img/Restful/1562685800583.png" alt="1562685800583" style="zoom:200%;" />

比如 使用ajax的post请求伪装put请求

public/api.html中 添加以下代码

<input type="button" id="post_to_put" value="伪装put">
<input type="button" id="post_to_delete" value="伪装delete">
<script>
    $(function(){
        $('#post_to_put').click(function(){
            $.ajax({
                "url":"/news/33",
                "type":"post",
                "data":"_method=put",
                "dataType":"json",
                "success":function(res){
                    console.log(res);
                }
            });
        });
        $('#post_to_delete').click(function(){
            $.ajax({
                "url":"/news/33",
                "type":"post",
                "data":"_method=delete",
                "dataType":"json",
                "success":function(res){
                    console.log(res);
                }
            });
        });
    })
</script>

4、Restful常用的资源路由

新增页面页面展示 create方法 和 修改页面页面展示 edit方法 一般可以不用。

标识请求类型生成路由规则对应操作方法(默认)备注
indexGETnewsindex查询多条数据(列表)
readGETnews/:idread查询一条数据(详情、修改页面展示)
savePOSTnewssave新增一条数据
updatePUTnews/:idupdate修改一条数据
deleteDELETEnews/:iddelete删除一条数据

5、实际开发中的Restful

Restful接口通常返回的是完整的数据模型,粒度过于“粗”,对客户端不友好(客户端可能只需要其中一小部分字段)。

Restful典型使用场景:

开放API(各种开放平台的数据api)。

开放API之所以开放,就是因为不知道也不关心客户端需要什么返回结果,直接返回完整的数据,好处是通用。

实际开发中,通常都是内部接口开发,需求非常明确,

所以一般都是灵活借鉴Restful中的优点,结合自己的实际情况,来设计自己的内部api,在基本的增删改查接口之外,

通常会设计一些(反restful)业务接口(根据业务逻辑需要,一个接口中对多个资源的数据进行整合再返回)。

四、服务端CURL请求

服务端与服务端之间,也存在接口编程。

比如我们网站服务端,需要发送短信、发送邮件、查询快递等,都需要调用第三方平台的接口。

1、php中发送请求

  • file_get_contents函数 :传递完整的url参数 通常是get请求,有返回值(地址中的输出)
  • curl请求方式(PHP的curl函数库):php中比较规范的一种发送请求方式。

2、CURL函数库

Curl函数库的使用流程:

  1. 使用curl_init初始化请求会话
  2. 使用curl_setopt设置请求一些选项
  3. 使用curl_exec执行,发送请求
  4. 使用curl_close关闭请求会话

使用curl函数库的前提:

  1. 在php.ini中开启php_curl扩展(必须开启)

image-20200901235853293

  1. 建议在php.ini中开启php_openssl扩展(本身不是curl必须的,是调用一些第三方接口需要的)

image-20200902000020336

  1. 如果以上操作重启apache后,curl还是不能使用,需要将php安装目录设置到环境变量。

3、封装请求函数

封装一个函数用来发送curl请求

代码封装:application/common.php

1562687655831

1562687679627

4、本地模拟测试

先准备好一个接口地址:用于请求调用

1562741318211

再写一个方法,发送请求,调用之前准备好的接口

1562741352459

测试结果:

访问:http://www.tpshop.com/index.p...

1562741376351

5、curl请求错误调试

curl_errno函数 返回错误码

curl_error函数 返回错误信息

1562741913769

1562741943467

五、快递查询

1、接口说明

提供快递查询的第三方平台很多,比如快递100、聚合数据、百度云apistore、阿里云、腾讯云、京东万象等等。

这里以聚合数据平台为例:接口页面:https://www.juhe.cn/docs/api/...

接口地址:http://v.juhe.cn/exp/index

返回格式:json/xml

请求方式:http post/get

必传请求参数: com 快递公司编号; no 快递单号; key 授权key

请求示例:http://v.juhe.cn/exp/index?co...

1562689955604

2、项目使用

    public function kuaidi()
    {
        //接口地址
        $url = "http://v.juhe.cn/exp/index?com=zto&no=73115984252335&key=ac2dde994cc76d4f15738f7f97af7ca4";
        //请求方式 get post
        //请求参数
        $params = [
            'com' => 'zto',
            'no' => '73115984252335',
            'key' => 'ac2dde994cc76d4f15738f7f97af7ca4'
        ];
        //发送请求
        $res = curl_request($url, true, $params);
        if(!$res){
            echo '请求失败';die;
        }
        //解析结果
        $arr = json_decode($res, true);
        //查询失败
        if($arr['resultcode'] != 200){
            echo $arr['reason'];die;
        }
        //查询成功,展示信息
        $list = $arr['result']['list'];
        echo '时间 ------------------------ 物流信息<br>';
        foreach($list as $v){
            echo $v['datetime'], '------------------------', $v['remark'], '<br>';
        }
    }

展示效果:

1562690621242

六、接口文档

1、接口内容

接口名称

场景说明

接口说明

请求参数

响应参数

错误码

2、请求参数内容

字段名
是否必填
类型
示例值
描述

3、响应参数内容

字段名称

描述

返回示例

4、错误码内容

名称
描述
原因
解决方案

5、示例

1562691340888

1562691361135

6、项目搭建

1、安装部署tp框架(解压压缩包、配置虚拟站点)(主域名、子域名,比如 www.pyg.com 和 adminapi.pyg.com)

2、模块划分(后台模块admin、前台模块home、后台接口模块adminapi、公共模块common)

3、admin模块、home模块、adminapi模块 有默认的控制器方法,可以访问。 common模块下只需要model模型目录

4、路由配置: adminapi模块 配置域名路由

5、对adminapi模块,创建一个Base控制器,定义ok方法和fail方法,用于快捷返回结果数据

比如:

失败时,以下两行代码 效果一样
return json(['code' => 401, 'msg' => '参数错误', 'data' => []]);
//$this->fail(401, '参数错误');

成功时,以下两行代码 效果一样
return json(['code' => 200, 'msg' => 'success', 'data' => $data]);
//$this->ok($data);

like
42 声望1 粉丝

« 上一篇
Redis高级