mevdschee / php-crud-api
单一文件PHP脚本,为SQL数据库添加REST API。
Requires
- php: >=7.0.0
- ext-json: *
- ext-mbstring: *
- ext-pdo: *
- ext-zlib: *
- nyholm/psr7: *
- nyholm/psr7-server: *
- psr/http-factory: *
- psr/http-message: *
- psr/http-server-handler: *
- psr/http-server-middleware: *
Suggests
- ext-memcache: *
- ext-memcached: *
- ext-redis: *
- dev-main
- v2.14.28
- v2.14.27
- v2.14.26
- v2.14.25
- v2.14.24
- v2.14.23
- v2.14.22
- v2.14.21
- v2.14.20
- v2.14.19
- v2.14.18
- v2.14.17
- v2.14.16
- v2.14.15
- v2.14.14
- v2.14.13
- v2.14.12
- v2.14.11
- v2.14.10
- v2.14.9
- v2.14.8
- v2.14.7
- v2.14.6
- v2.14.5
- v2.14.4
- v2.14.3
- v2.14.2
- v2.14.1
- v2.14.0
- v2.13.7
- v2.13.6
- v2.13.5
- v2.13.4
- v2.13.3
- v2.13.2
- v2.13.1
- v2.13.0
- v2.12.6
- v2.12.5
- v2.12.4
- v2.12.3
- v2.12.2
- v2.12.1
- v2.12.0
- v2.11.5
- v2.11.4
- v2.11.3
- v2.11.2
- v2.11.1
- v2.11.0
- v2.10.1
- v2.10.0
- v2.9.44
- v2.9.43
- v2.9.42
- v2.9.41
- v2.9.40
- v2.9.39
- v2.9.38
- v2.9.37
- v2.9.36
- v2.9.35
- v2.9.34
- v2.9.33
- v2.9.32
- v2.9.31
- v2.9.30
- v2.9.29
- v2.9.28
- v2.9.27
- v2.9.26
- v2.9.25
- v2.9.24
- v2.9.23
- v2.9.22
- v2.9.21
- v2.9.20
- v2.9.19
- v2.9.18
- v2.9.17
- v2.9.16
- v2.9.15
- v2.9.14
- v2.9.13
- v2.9.12
- v2.9.11
- v2.9.10
- v2.9.9
- v2.9.8
- v2.9.7
- v2.9.6
- v2.9.5
- v2.9.4
- v2.9.3
- v2.9.2
- v2.9.1
- v2.9.0
- v2.8.1
- v2.8.0
- v2.7.3
- v2.7.2
- v2.7.1
- v2.7.0
- v2.6.8
- v2.6.7
- v2.6.6
- v2.6.5
- v2.6.4
- v2.6.3
- v2.6.2
- v2.6.1
- v2.6.0
- v2.5.7
- v2.5.6
- v2.5.5
- v2.5.4
- v2.5.3
- v2.5.2
- v2.5.1
- v2.5.0
- v2.4.8
- v2.4.7
- v2.4.6
- v2.4.5
- v2.4.4
- v2.4.3
- v2.4.2
- v2.4.1
- v2.4.0
- v2.3.23
- v2.3.22
- v2.3.21
- v2.3.20
- v2.3.19
- v2.3.18
- v2.3.17
- v2.3.16
- v2.3.15
- v2.3.14
- v2.3.13
- v2.3.12
- v2.3.11
- v2.3.10
- v2.3.9
- v2.3.8
- v2.3.7
- v2.3.6
- v2.3.5
- v2.3.4
- v2.3.3
- v2.3.2
- v2.3.1
- v2.3.0
- v2.2.3
- v2.2.2
- v2.2.1
- v2.2.0
- v2.1.7
- v2.1.6
- v2.1.5
- v2.1.4
- v2.1.3
- v2.1.2
- v2.1.1
- v2.1.0
- v2.0.20
- v2.0.19
- v2.0.18
- v2.0.17
- v2.0.16
- v2.0.15
- v2.0.14
- v2.0.13
- v2.0.12
- v2.0.11
- v2.0.10
- v2.0.9
- v2.0.8
- v2.0.7
- v2.0.6
- v2.0.5
- v2.0.4
- v2.0.3
- v2.0.2
- v2.0.1
- v2.0.0
- v2.0-BETA8
- v2.0-BETA7
- v2.0-BETA6
- v2.0-BETA5
- v2.0-BETA4
- v2.0-BETA3
- v2.0-BETA2
- v2.0-BETA
- v2.0-ALPHA
- v1.x-dev
- v1.2.18
- v1.2.17
- v1.2.16
- v1.2.15
- v1.2.14
- v1.2.13
- v1.2.12
- v1.2.11
- v1.2.10
- v1.2.9
- v1.2.8
- v1.2.7
- v1.2.6
- v1.2.5
- v1.2.4
- v1.2.3
- v1.2.2
- v1.2.1
- v1.2.0
- v1.1.10
- v1.1.9
- v1.1.8
- v1.1.7
- v1.1.6
- v1.1.5
- dev-middlewares
- dev-procedure
- dev-json
- dev-focussing
This package is auto-updated.
Last update: 2024-09-08 17:42:41 UTC
README
单一文件PHP脚本,为MySQL/MariaDB、PostgreSQL、SQL Server或SQLite数据库添加REST API。
使用方法:将"api.php
"上传到您的web服务器,配置它以连接到您的数据库,立即获得功能齐全的REST API。
注意:这是PHP中的TreeQL参考实现。
要求
- PHP 7.2或更高版本,并启用PDO驱动程序,用于以下数据库系统之一
- MySQL 5.7 / MariaDB 10.0或更高版本,以支持MySQL中的空间功能
- PostgreSQL 9.5或更高版本,与PostGIS 2.2或更高版本配合使用以支持空间功能
- SQL Server 2017或更高版本(2019也支持Linux)
- SQLite 3.16或更高版本(不支持空间功能)
安装
从最新版本下载"api.php
"文件
https://github.com/mevdschee/php-crud-api/releases/latest 或直接从
https://raw.githubusercontent.com/mevdschee/php-crud-api/main/api.php
这是一个单文件应用程序!将"api.php
"上传到某个位置,然后享受吧!
对于本地开发,您可以运行PHP内置的web服务器
php -S localhost:8080
通过打开以下URL来测试脚本
http://localhost:8080/api.php/records/posts/1
不要忘记修改文件底部的配置。
或者,您可以将此项目集成到您选择的Web框架中,请参阅
在这些集成中,使用Composer将此项目作为依赖项加载。
对于不使用Composer的人,提供了文件"api.include.php
"。此文件包含"api.php
"中的所有内容,除了"src/index.php
"中的配置,并可以使用PHP的"include"函数使用。
配置
编辑文件"api.php
"底部的以下行
$config = new Config([
'username' => 'xxx',
'password' => 'xxx',
'database' => 'xxx',
]);
这些是所有配置选项及其默认值(括号内)
- "driver":
mysql
,pgsql
,sqlsrv
或sqlite
(mysql
) - "address": 数据库服务器的名称(或文件名)(
localhost
) - "port": 数据库服务器的TCP端口(默认为驱动程序的默认值)
- "username": 连接到数据库的用户名(无默认值)
- "password": 连接到数据库的密码(无默认值)
- "database": 连接到的数据库(无默认值)
- "command": 初始化数据库连接的额外SQL(无)
- "tables": 要发布的表的逗号分隔列表(默认为'所有')
- "mapping": 表/列映射的逗号分隔列表(无映射)
- "geometrySrid": 从WKT转换为几何形状时假定的SRID(
4326
) - "middlewares": 要加载的中间件列表(
cors
) - "controllers": 要加载的控制器列表(
records,geojson,openapi,status
) - "customControllers": 要加载的用户自定义控制器列表(无默认值)
- "openApiBase": OpenAPI信息(《{"info":{"title":"PHP-CRUD-API","version":"1.0.0"}`)
- "cacheType":
TempFile
、Redis
、Memcache
、Memcached
或NoCache
(《TempFile) - "cachePath": 缓存路径(默认为系统的临时目录)
- "cacheTime": 缓存有效时长(秒)(默认值为
10
) - "jsonOptions": 编码JSON时使用的选项(《JSON_UNESCAPED_UNICODE》)
- "debug": 在“X-Exception”头中显示错误(《false》)
- "basePath": API的URI基本路径(默认使用PATH_INFO确定)
所有配置选项也作为环境变量可用。将配置选项大写,以“PHP_CRUD_API_”为前缀,以下划线为单词分隔符,例如
- PHP_CRUD_API_DRIVER=mysql
- PHP_CRUD_API_ADDRESS=localhost
- PHP_CRUD_API_PORT=3306
- PHP_CRUD_API_DATABASE=php-crud-api
- PHP_CRUD_API_USERNAME=php-crud-api
- PHP_CRUD_API_PASSWORD=php-crud-api
- PHP_CRUD_API_DEBUG=1
环境变量优先于PHP配置。
限制
以下限制和约束适用
- 主键应该是自增的(从1到2^53)或UUID
- 不支持复合主键和复合外键
- 不支持复杂写操作(事务)
- 不支持调用函数的复杂查询(如“concat”或“sum”)
- 数据库必须支持并定义外键约束
- SQLite不支持具有bigint类型自增主键
- SQLite不支持更改表列(结构)
特性
以下特性受支持
- Composer安装或单个PHP文件,易于部署。
- 代码量很少,易于适应和维护
- 支持作为输入的POST变量(x-www-form-urlencoded)
- 支持作为输入的JSON对象
- 支持作为输入的JSON数组(批量插入)
- 使用类型规则和回调函数对输入进行清理和验证
- 支持数据库、表、列和记录的权限系统
- 支持多租户单数据库和多数据库布局
- 支持多域名CORS,以支持跨域请求
- 支持从多个表读取连接结果
- 支持基于多个标准的搜索
- 支持分页、排序、前N列表和列选择
- 支持通过嵌套结果进行关系检测(belongsTo, hasMany和HABTM)
- 通过PATCH支持原子增量(用于计数器)
- 支持二进制字段,使用base64编码
- 支持使用WKT和GeoJSON支持空间/GIS字段和过滤器
- 支持将表和列名映射到支持旧系统
- 使用OpenAPI工具生成API文档
- 通过API密钥、JWT令牌或用户名/密码进行身份验证
- 数据库连接参数可能取决于身份验证
- 支持以JSON读取数据库结构
- 支持使用REST端点修改数据库结构
- 包含增强安全性的中间件
- 符合标准:PSR-4、PSR-7、PSR-12、PSR-15和PSR-17
相关项目和端口
相关项目
- PHP-CRUD-API 快速入门:一个可定制的、现成的、具有PHP-CRUD-API的docker compose文件。
- PHP-CRUD-API 过滤器生成器:一个JavaScript库,用于从表达式创建PHP-CRUD-API过滤器。
- JS-CRUD-API:PHP-CRUD-API API的JavaScript客户端库。
- PHP-API-AUTH:一个单文件PHP脚本,它是PHP-CRUD-API的身份验证提供程序。
- PHP-CRUD-UI:一个单文件PHP脚本,它为PHP-CRUD-API项目添加用户界面。
- PHP-CRUD-ADMIN:一个单文件PHP脚本,它为PHP-CRUD-API项目添加数据库管理界面。
- PHP-SP-API:一个单文件PHP脚本,它向SQL数据库添加REST API。
- dexie-mysql-sync:本地IndexedDB与MySQL数据库之间的同步。
- ra-data-treeql:为数据提供者提供NPM包,用于React Admin。
- scriptPilot/vueuse:在Vue组合式API的基础上,提供VueUse.org(支持PHP-CRUD-API)。
- scriptPilot/add-php-backend:将MySQL、phpMyAdmin和PHP-CRUD-API添加到您的开发环境中。
- VUE-CRUD-UI:单个文件Vue.js脚本,为PHP-CRUD-API项目添加UI。
此脚本的移植版本还包括以下:
- Go-CRUD-API(进行中)
- Ivan Kolchagov的Java JDBC(v1)
- Java Spring Boot + jOOQ(v2:进行中)
此脚本还支持以下语言的简单REST CRUD功能: PHP、Java、Go、C# .net core、Node.js 和 Python。
编译
您可以使用以下命令安装此项目的所有依赖项
php install.php
您可以使用以下命令将所有文件编译成单个"api.php
"文件
php build.php
注意,当您将此项目集成到另一个项目或框架中时,不要使用编译(使用Composer代替)。
开发
您可以在以下URL访问非编译代码
http://localhost:8080/src/records/posts/1
非编译代码位于"src
"和"vendor
"目录中。"vendor
"目录包含依赖项。
更新依赖项
您可以使用以下命令更新此项目的所有依赖项
php update.php
此脚本将安装并运行Composer以更新依赖项。
注意:更新脚本将修复vendor目录中的依赖项以支持PHP 7.0。
TreeQL,实用的GraphQL
TreeQL允许您根据SQL数据库结构(关系)和查询创建基于JSON对象的"树"。
它松散地基于REST标准,并受到json:api的启发。
CRUD + 列表
示例帖子表只有几个字段
posts
=======
id
title
content
created
以下CRUD + 列表操作作用于此表。
创建
如果您想创建记录,请求可以写成URL格式如下
POST /records/posts
您必须发送包含以下内容的正文
{
"title": "Black is the new red",
"content": "This is the second post.",
"created": "2018-03-06T21:34:01Z"
}
它将返回新创建记录的主键值
2
读取
要从此表读取记录,请求可以写成URL格式如下
GET /records/posts/1
其中"1"是要读取的记录的主键值。它将返回
{
"id": 1
"title": "Hello world!",
"content": "Welcome to the first post.",
"created": "2018-03-05T20:12:56Z"
}
在读取操作中,您可能需要应用连接。
更新
要更新此表中的记录,请求可以写成URL格式如下
PUT /records/posts/1
其中"1"是要更新的记录的主键值。作为正文发送
{
"title": "Adjusted title!"
}
这会调整帖子的标题。返回值是设置的行数
1
删除
如果您想从这张表中删除一条记录,请求可以写成URL格式,如下所示
DELETE /records/posts/1
它将返回被删除的行数
1
列表
要列出此表中的记录,请求可以写成URL格式如下
GET /records/posts
它将返回
{
"records":[
{
"id": 1,
"title": "Hello world!",
"content": "Welcome to the first post.",
"created": "2018-03-05T20:12:56Z"
}
]
}
在列表操作中,您可以应用筛选和连接。
筛选
筛选提供搜索功能,在列表调用中使用“filter”参数。您需要指定列名、逗号、匹配类型,另一个逗号和您要筛选的值。以下是被支持的匹配类型
- "cs": 包含字符串(字符串包含值)
- "sw": 以...开始(字符串以...开始)
- "ew": 以...结束(字符串以...结束)
- "eq": 等于(字符串或数字完全匹配)
- "lt": 小于(数字小于值)
- "le": 小于或等于(数字小于或等于值)
- "ge": 大于或等于(数字大于或等于值)
- "gt": 大于(数字大于值)
- "bt": 在...之间(数字在两个逗号分隔的值之间)
- "in": 在...中(数字或字符串在逗号分隔的值列表中)
- "is": 为空(字段包含“NULL”值)
您可以通过在前面添加“n”字符来否定所有筛选,例如,“eq”变为“neq”。筛选使用的示例包括
GET /records/categories?filter=name,eq,Internet
GET /records/categories?filter=name,sw,Inter
GET /records/categories?filter=id,le,1
GET /records/categories?filter=id,ngt,1
GET /records/categories?filter=id,bt,0,1
GET /records/categories?filter=id,in,0,1
输出
{
"records":[
{
"id": 1
"name": "Internet"
}
]
}
在下一节中,我们将深入探讨如何在单个列表调用中应用多个筛选。
多个筛选
可以通过在URL中重复“filter”参数来应用筛选。例如,以下URL
GET /records/categories?filter=id,gt,1&filter=id,lt,3
将请求所有类别“其中id > 1 且 id < 3”。如果您想要“其中id = 2 或 id = 4”,则应编写
GET /records/categories?filter1=id,eq,2&filter2=id,eq,4
如您所见,我们在“filter”参数中添加了一个数字,以表示应用“OR”而不是“AND”。请注意,您也可以重复“filter1”并在“OR”内创建一个“AND”。由于您还可以通过添加一个字母(a-f)来进一步深入一层,因此您可以创建几乎任何合理的复杂条件树。
注意:您只能筛选所请求的表(而不是其包含的表),并且筛选仅在列表调用中应用。
列选择
默认情况下选择所有列。使用“include”参数可以选择特定的列。您可以使用点来分隔表名和列名。多列应以逗号分隔。星号(“*”)可以用作通配符,表示“所有列”。类似于“include”,您可以使用“exclude”参数来删除某些列
GET /records/categories/1?include=name
GET /records/categories/1?include=categories.name
GET /records/categories/1?exclude=categories.id
输出
{
"name": "Internet"
}
注意:用于包含相关实体的列会自动添加,不能从输出中省略。
排序
使用“order”参数可以进行排序。默认情况下,排序是升序的,但通过指定“desc”可以将其反转
GET /records/categories?order=name,desc
GET /records/categories?order=id,desc&order=name
输出
{
"records":[
{
"id": 3
"name": "Web development"
},
{
"id": 1
"name": "Internet"
}
]
}
注意:您可以通过使用多个“order”参数在多个字段上排序。您不能在“连接”的列上排序。
限制大小
“size”参数限制了返回的记录数。这可以与“order”参数一起用于顶部N列表(使用降序)。
GET /records/categories?order=id,desc&size=1
输出
{
"records":[
{
"id": 3
"name": "Web development"
}
]
}
注意:如果您还想知道记录的总数,可能需要使用“page”参数。
分页
“page”参数包含请求的页面。默认页面大小为20,但可以进行调整(例如,50)。
GET /records/categories?order=id&page=1
GET /records/categories?order=id&page=1,50
输出
{
"records":[
{
"id": 1
"name": "Internet"
},
{
"id": 3
"name": "Web development"
}
],
"results": 2
}
元素“results”包含表中记录的总数,如果不使用分页,则将返回。
注意:由于未排序的页面无法进行分页,因此页面将按主键排序。
连接
假设您有一个包含评论(由用户创建)的帖子表,而帖子可以有标签。
posts comments users post_tags tags
======= ======== ======= ========= =======
id id id id id
title post_id username post_id name
content user_id phone tag_id
created message
当您想列出帖子及其评论用户和标签时,您可以请求两个“树”路径。
posts -> comments -> users
posts -> post_tags -> tags
这些路径具有相同的根,此请求可以以URL格式编写。
GET /records/posts?join=comments,users&join=tags
在这里,您可以省略将帖子与标签绑定在一起的中间表。在这个例子中,您可以看到所有三种表关系类型(hasMany、belongsTo和hasAndBelongsToMany)都有效。
- "post" 有许多 "comments"。
- "comment" 属于 "user"。
- "post" 有和属于许多 "tags"。
这可能会导致以下JSON数据。
{
"records":[
{
"id": 1,
"title": "Hello world!",
"content": "Welcome to the first post.",
"created": "2018-03-05T20:12:56Z",
"comments": [
{
id: 1,
post_id: 1,
user_id: {
id: 1,
username: "mevdschee",
phone: null,
},
message: "Hi!"
},
{
id: 2,
post_id: 1,
user_id: {
id: 1,
username: "mevdschee",
phone: null,
},
message: "Hi again!"
}
],
"tags": []
},
{
"id": 2,
"title": "Black is the new red",
"content": "This is the second post.",
"created": "2018-03-06T21:34:01Z",
"comments": [],
"tags": [
{
id: 1,
message: "Funny"
},
{
id: 2,
message: "Informational"
}
]
}
]
}
您可以看到“belongsTo”关系被检测到,外键值被替换为引用的对象。在“hasMany”和“hasAndBelongsToMany”的情况下,表名用作对象的新属性。
批量操作
当您想要创建、读取、更新或删除时,您可以在URL中指定多个主键值。对于创建和更新,您还需要在请求体中发送数组而不是对象。
要从此表读取记录,请求可以写成URL格式如下
GET /records/posts/1,2
结果可能是
[
{
"id": 1,
"title": "Hello world!",
"content": "Welcome to the first post.",
"created": "2018-03-05T20:12:56Z"
},
{
"id": 2,
"title": "Black is the new red",
"content": "This is the second post.",
"created": "2018-03-06T21:34:01Z"
}
]
同样,当您想要进行批量更新时,URL格式的请求如下编写。
PUT /records/posts/1,2
其中“1”和“2”是要更新的记录的主键值。正文应包含与URL中主键数量相同数量的对象。
[
{
"title": "Adjusted title for ID 1"
},
{
"title": "Adjusted title for ID 2"
}
]
这调整了帖子的标题。返回值是设置行的数量。
[1,1]
这意味着有两个更新操作,每个操作都设置了一行。批量操作使用数据库事务,因此它们要么全部成功,要么全部失败(成功操作将被回滚)。如果它们失败,则正文将包含错误文档的列表。在以下响应中,第一个操作成功,批量操作的第二个操作失败,原因是完整性违反。
[
{
"code": 0,
"message": "Success"
},
{
"code": 1010,
"message": "Data integrity violation"
}
]
如果批量操作中的任何一个失败,则响应状态代码将始终为424(失败的依赖关系)。
要将多条记录插入此表,请求可以按URL格式编写。
POST /records/posts
正文应包含要插入的记录数组。
[
{
"title": "Hello world!",
"content": "Welcome to the first post.",
"created": "2018-03-05T20:12:56Z"
},
{
"title": "Black is the new red",
"content": "This is the second post.",
"created": "2018-03-06T21:34:01Z"
}
]
返回值也是一个数组,包含新插入记录的主键。
[1,2]
请注意,DELETE的批量操作遵循与PUT相同的模式,但没有正文。
空间支持
对于空间支持,有一组额外的过滤器可以应用于几何列,并以“s”开头。
- "sco": 空间包含(几何包含另一个)
- "scr": 空间交叉(几何交叉另一个)
- "sdi": 空间不重叠(几何与另一个不重叠)
- "seq": 空间相等(几何等于另一个)
- "sin": 空间相交(几何相交另一个)
- "sov": 空间重叠(几何重叠另一个)
- "sto": 空间接触(几何接触另一个)
- "swi": 空间内(几何在另一个内部)
- "sic": 空间是封闭的(几何是封闭且简单的)
- "sis": 空间是简单的(几何是简单的)
- "siv": 空间是有效的(几何是有效的)
这些过滤器基于OGC标准,WKT规范也是如此,几何列在其中表示。请注意,从WKT转换为几何时假设的SRID由配置变量geometrySrid
指定,默认为4326(WGS 84)。
GeoJSON
GeoJSON支持是对表格和记录的只读视图,格式为GeoJSON。以下请求受支持。
method path - operation - description
----------------------------------------------------------------------------------------
GET /geojson/{table} - list - lists records as a GeoJSON FeatureCollection
GET /geojson/{table}/{id} - read - reads a record by primary key as a GeoJSON Feature
“/geojson
”端点在内部使用“/records
”端点,并继承所有功能,如连接和过滤。它还支持一个“geometry”参数,用于指示在表有多个列时几何列的名称。对于地图视图,它支持“bbox”参数,可以指定左上角和右下角的坐标(逗号分隔)。GeoJSON实现支持以下几何类型:
- 点
- 多点
- 线字符串
- 多线字符串
- 多边形
- 多边形
GeoJSON功能默认启用,但可以通过“controllers”配置禁用。
为旧系统映射名称
为了支持为(部分)旧系统(如Wordpress)创建API,您可能需要映射表和列名称,因为在不更改软件的情况下无法对其进行改进,而名称可能需要进行一些改进以提高一致性。配置允许您使用逗号分隔的映射列表重命名表和列,例如:
'mapping' => 'wp_posts=posts,wp_posts.ID=posts.id',
此特定示例将在“posts
”端点公开“wp_posts
”表(而不是“wp_posts
”)和该表中的“ID
”列作为“id
”属性(小写而不是大写)。
注意:由于这两个映射重叠,第一个(不那么具体)映射可能被省略。
中间件
您可以使用“middlewares”配置参数启用以下中间件:
- "firewall":限制对特定IP地址的访问
- "sslRedirect":强制使用HTTPS而不是HTTP进行连接
- "cors":支持CORS请求(默认启用)
- "xsrf":使用“双提交Cookie”方法阻止XSRF攻击
- "ajaxOnly":限制非AJAX请求以防止XSRF攻击
- "apiKeyAuth":支持“API密钥认证”
- "apiKeyDbAuth":支持“数据库API密钥认证”
- "dbAuth":支持“数据库认证”
- "wpAuth":支持“Wordpress认证”
- "jwtAuth":支持“JWT认证”
- "basicAuth":支持“基本认证”
- "reconnect":使用不同的参数重新连接到数据库
- "authorization":限制对某些表或列的访问
- "validation":返回自定义规则和默认类型规则的输入验证错误
- "ipAddress":在创建时填充带有IP地址的受保护字段
- "sanitation":在创建和更新时应用输入清理
- "multiTenancy":在多租户场景中限制租户的访问
- "pageLimits":限制列表操作以防止数据库抓取
- "joinLimits":限制连接参数以防止数据库抓取
- "textSearch":使用简单参数在所有文本字段中搜索
- "customization":提供请求和响应定制的处理程序
- "json":支持将JSON字符串作为JSON对象/数组进行读写
- "xml":将所有输入和输出从JSON转换为XML
“middlewares”配置参数是启用中间件的逗号分隔列表。您可以使用中间件特定的配置参数调整中间件的行为
- "firewall.reverseProxy":当使用反向代理时设置为“true”
- "firewall.allowedIpAddresses":允许连接的IP地址列表
- "cors.allowedOrigins":CORS头中允许的来源
- "cors.allowHeaders":CORS请求中允许的头部
- "cors.allowMethods":CORS请求中允许的方法
- "cors.allowCredentials":在CORS请求中允许凭据
- "cors.exposeHeaders":允许浏览器访问的标头白名单
- "cors.maxAge":CORS授权在秒中有效的时长
- "xsrf.excludeMethods": 不需要XSRF保护的请求方法("OPTIONS,GET")
- "xsrf.cookieName": XSRF保护cookie的名称("XSRF-TOKEN")
- "xsrf.headerName": XSRF保护头部的名称("X-XSRF-TOKEN")
- "ajaxOnly.excludeMethods": 不需要AJAX的请求方法("OPTIONS,GET")
- "ajaxOnly.headerName": 需要的头部名称("X-Requested-With")
- "ajaxOnly.headerValue": 需要的头部值("XMLHttpRequest")
- "apiKeyAuth.mode": 如果想允许匿名访问,设置为"optional"("required")
- "apiKeyAuth.header": API密钥头部的名称("X-API-Key")
- "apiKeyAuth.keys": 有效的API密钥列表("")
- "apiKeyDbAuth.mode": 如果想允许匿名访问,设置为"optional"("required")
- "apiKeyDbAuth.header": API密钥头部的名称("X-API-Key")
- "apiKeyDbAuth.usersTable": 用于存储用户的表("users")
- "apiKeyDbAuth.apiKeyColumn": 存储API密钥的用户表列("api_key")
- "dbAuth.mode": 如果想允许匿名访问,设置为"optional"("required")
- "dbAuth.usersTable": 用于存储用户的表("users")
- "dbAuth.loginTable": 用于检索登录用户信息的表或视图("users")
- "dbAuth.usernameColumn": 存储用户名的用户表列("username")
- "dbAuth.passwordColumn": 存储密码的用户表列("password")
- "dbAuth.returnedColumns": 登录成功时返回的列,空表示'全部'("")
- "dbAuth.usernameFormField": 存储用户名的表单字段名称("username")
- "dbAuth.passwordFormField": 存储密码的表单字段名称("password")
- "dbAuth.newPasswordFormField": 存储新密码的表单字段名称("newPassword")
- "dbAuth.registerUser": 注册端点启用时,JSON用户数据(或"1")("")
- "dbAuth.loginAfterRegistration": 注册用户注册后是否应该登录(1或0)("")
- "dbAuth.passwordLength": 密码必须的最小长度("12")
- "dbAuth.sessionName": 启动的PHP会话名称("")
- "wpAuth.mode": 如果想允许匿名访问,设置为"optional"("required")
- "wpAuth.wpDirectory": 可以找到Wordpress安装的文件夹/路径(".")
- "wpAuth.usernameFormField": 存储用户名的表单字段名称("username")
- "wpAuth.passwordFormField": 存储密码的表单字段名称("password")
- "jwtAuth.mode": 如果想允许匿名访问,设置为"optional"("required")
- "jwtAuth.header": 包含JWT令牌的头部名称("X-Authorization")
- "jwtAuth.leeway": 可接受的时钟偏移秒数("5")
- "jwtAuth.ttl": 令牌有效的秒数("30")
- "jwtAuth.secrets": 用于签名JWT令牌的共享密钥("")
- "jwtAuth.algorithms": 允许的算法,空表示'全部'("")
- "jwtAuth.audiences": 允许的受众,空表示'全部'("")
- "jwtAuth.issuers": 允许的发行者,空表示'全部'("")
- "jwtAuth.sessionName": 启动的PHP会话名称("")
- "basicAuth.mode": 如果想允许匿名访问,设置为"optional"("required")
- "basicAuth.realm": 显示登录时提示的文本("用户名和密码要求")
- "basicAuth.passwordFile": 读取用户名/密码组合的文件(".htpasswd")
- "basicAuth.sessionName": 启动的PHP会话名称("")
- "reconnect.driverHandler": 实现获取数据库驱动程序的处理器("")
- "reconnect.addressHandler": 实现获取数据库地址的处理器("")
- "reconnect.portHandler": 实现获取数据库端口的处理器("")
- "reconnect.databaseHandler": 实现获取数据库名称的处理器("")
- "reconnect.tablesHandler": 实现获取表名称的处理器("")
- "reconnect.mappingHandler": 实现获取名称映射的处理器("")
- "reconnect.usernameHandler": 实现数据库用户名检索的处理程序("")
- "reconnect.passwordHandler": 实现数据库密码检索的处理程序("")
- "authorization.tableHandler": 实现表授权规则的处理程序("")
- "authorization.columnHandler": 实现列授权规则的处理程序("")
- "authorization.pathHandler": 实现路径授权规则的处理程序("")
- "authorization.recordHandler": 实现记录授权筛选规则的处理程序("")
- "validation.handler": 实现输入值验证规则的处理程序("")
- "validation.types": 可启用类型验证的类型,空值表示'无'("所有")
- "validation.tables": 可启用类型验证的表,空值表示'无'("所有")
- "ipAddress.tables": 搜索以IP地址覆盖的列的表("")
- "ipAddress.columns": 在创建时保护并使用IP地址覆盖的列("")
- "sanitation.handler": 实现输入值清理规则的处理程序("")
- "sanitation.types": 可启用类型清理的类型,空值表示'无'("所有")
- "sanitation.tables": 可启用类型清理的表,空值表示'无'("所有")
- "multiTenancy.handler": 实现简单多租户规则的处理程序("")
- "pageLimits.pages": 列表操作允许的最大页数("100")
- "pageLimits.records": 列表操作返回的最大记录数("1000")
- "joinLimits.depth": 允许的连接路径的最大深度(长度)("3")
- "joinLimits.tables": 允许连接的最大表数("10")
- "joinLimits.records": 连接实体返回的最大记录数("1000")
- "textSearch.parameter": 用于搜索词的参数名称("搜索")
- "customization.beforeHandler": 实现请求定制的处理程序("")
- "customization.afterHandler": 实现响应定制的处理程序("")
- "json.controllers": 处理JSON字符串的控制器("records,geojson")
- "json.tables": 处理JSON字符串的表("所有")
- "json.columns": 处理JSON字符串的列("所有")
- "xml.types": 应添加到XML类型属性的JSON类型("null,array")
如果在配置中未指定这些参数,则使用默认值(括号内)。
在下文中,您可以找到有关内置中间件的更多信息。
身份验证
当前支持五种身份验证类型。它们都在$_SESSION
超级全局中存储已认证的用户。该变量可以在授权处理程序中使用,以决定某人是否应该具有读取或写入访问特定表、列或记录的权限。以下概述显示了您可以启用的身份验证中间件的类型。
下文将提供有关每种身份验证类型的更多信息。
API密钥身份验证
API密钥身份验证通过在请求头中发送API密钥来实现。默认的头部名称为"X-API-Key",可以使用'apiKeyAuth.header'配置参数进行配置。有效的API密钥必须使用'apiKeyAuth.keys'配置参数进行配置(以逗号分隔的列表)。
X-API-Key: 02c042aa-c3c2-4d11-9dae-1a6e230ea95e
已认证的API密钥将存储在$_SESSION['apiKey']
变量中。
请注意,API密钥身份验证不需要或使用会话cookie。
API密钥数据库身份验证
API密钥数据库身份验证通过在请求头中发送名为"X-API-Key"(名称可配置)的API密钥来实现。有效的API密钥从"users"表的"api_key"列中读取(两个名称都可配置)。
X-API-Key: 02c042aa-c3c2-4d11-9dae-1a6e230ea95e
已认证的用户(及其所有属性)将存储在$_SESSION['apiUser']
变量中。
请注意,API密钥数据库身份验证不需要或使用会话cookie。
数据库身份验证
数据库身份验证中间件定义了五个新路由
method path - parameters - description
---------------------------------------------------------------------------------------------------
GET /me - - returns the user that is currently logged in
POST /register - username, password - adds a user with given username and password
POST /login - username, password - logs a user in by username and password
POST /password - username, password, newPassword - updates the password of the logged in user
POST /logout - - logs out the currently logged in user
用户可以通过将用户名和密码发送到登录端点(JSON格式)来登录。经过认证的用户(包含所有属性)将被存储在$_SESSION['user']
变量中。用户可以通过向登出端点发送空体的POST请求来登出。密码以哈希形式存储在用户表中的密码列。您可以使用注册端点注册新用户,但此功能必须通过“dbAuth.registerUser”配置参数开启。
非常重要,必须使用“authorization”中间件来限制对用户表的访问,否则所有用户都可以自由添加、修改或删除任何账户!以下是最小配置示例
'middlewares' => 'dbAuth,authorization',
'authorization.tableHandler' => function ($operation, $tableName) {
return $tableName != 'users';
},
注意,此中间件使用会话cookie并在服务器上存储登录状态。
使用关联表视图进行登录
对于登录操作,可以使用视图作为usersTable。此类视图可以从用户表返回过滤后的结果,例如where active = true,或者也可以通过表连接返回多表的结果。至少,视图应包括username和password以及一个名为id的字段。
然而,关联表的视图不可插入(参见问题907)。作为解决方案,使用loginTable属性设置不同的引用表进行登录。此时,usersTable将设置为常规的、可插入的用户表。
Wordpress认证
Wordpress认证中间件定义了三条路由
method path - parameters - description
---------------------------------------------------------------------------------------------------
GET /me - - returns the user that is currently logged in
POST /login - username, password - logs a user in by username and password
POST /logout - - logs out the currently logged in user
用户可以通过将用户名和密码发送到登录端点(JSON格式)来登录。用户可以通过向登出端点发送空体的POST请求来登出。您需要使用“wpAuth.wpDirectory”配置参数指定Wordpress安装目录。中间件调用“wp-load.php”,这允许您在授权中间件中使用Wordpress函数,例如
- wp_get_current_user()
- is_user_logged_in()
- is_super_admin()
- user_can(wp_get_current_user(),'edit_posts');
注意,此中间件不使用$_SESSION
变量。
基本认证
基本类型支持一个文件(默认为'.htpasswd'),该文件包含用户及其(哈希过的)密码,用冒号(':')分隔。当以明文形式输入密码时,它们将被自动哈希。经过认证的登录名将被存储在$_SESSION['username']
变量中。您需要发送一个包含您的以冒号分隔的用户名和密码的base64 url编码版本的“Authorization”头,并在“Basic”之后。
Authorization: Basic dXNlcm5hbWUxOnBhc3N3b3JkMQ
此示例发送字符串“username1:password1”。
JWT认证
JWT类型需要另一个(SSO/Identity)服务器来签署包含声明的令牌。两个服务器共享一个密钥,这样它们可以签署或验证签名是否有效。声明存储在$_SESSION['claims']
变量中。您需要发送一个包含base64 url编码和点分隔的令牌头、主体和签名的“X-Authorization”头,在“Bearer”之后(在此处了解JWT的更多信息)。标准规定您需要使用“Authorization”头,但在Apache和PHP中这可能会出现问题。
X-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6IjE1MzgyMDc2MDUiLCJleHAiOjE1MzgyMDc2MzV9.Z5px_GT15TRKhJCTHhDt5Z6K6LRDSFnLj8U5ok9l7gw
此示例发送已签名的声明
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": "1538207605",
"exp": 1538207635
}
注意:JWT实现仅支持基于RSA和HMAC的算法。
使用Auth0配置和测试JWT认证
首先,您需要在Auth0上创建一个账户。登录后,您必须创建一个应用程序(其类型无关紧要)。收集Domain
和Client ID
,并保留以备后用。然后创建一个API:给它起一个名字,并在identifier
字段中填写您的API端点的URL。
然后您需要在您的api.php
文件中配置jwtAuth.secrets
配置。不要使用您在Auth0应用程序设置中找到的secret
,而是使用strong>一个公开证书
。要找到它,请转到您应用程序的设置,然后在“额外设置”中。您现在将在“证书”标签页中找到签名证书字段中的您的公钥。
为了测试您的集成,您可以复制auth0/vanilla.html文件。务必填写以下三个变量
authUrl
用您的Auth0域clientId
用您的客户端IDaudience
用您在Auth0中创建的API URL
注意,如果您不填写受众参数,它将不会工作,因为您将不会得到有效的JWT。另外,请注意,您应该填写jwtAuth.audiences
(使用audience
的值)以确保令牌被验证为为您的应用程序生成。
您还可以更改用于测试带有身份验证的API的url
变量。
配置和测试JWT身份验证与Firebase
首先,您需要在Firebase控制台上创建一个Firebase项目。向此项目添加一个Web应用程序,并抓取代码片段以备后用。
然后您需要在您的api.php
文件中配置jwtAuth.secrets
配置。可以按以下方式完成
a. 登录到您的基于Firebase的应用程序,为该用户获取身份验证令牌 b. 前往https://jwt.node.org.cn/并将令牌粘贴到解码字段中 c. 从令牌中读取解码的头部信息,它将给出正确的kid
d. 通过此URL获取公钥,该URL对应于上一步中的kid
e. 现在,只需在api.php
中将您的公钥填入jwtAuth.secrets
还需要配置jwtAuth.audiences
(填写Firebase项目ID)。
以下是配置示例
...,
'middlewares' => 'cors, jwtAuth, authorization',
'jwtAuth.secrets' => "ce5ced6e40dcd1eff407048867b1ed1e706686a0:-----BEGIN CERTIFICATE-----\nMIIDHDCCAgSgAwIBAgIIExun9bJSK1wwDQYJKoZIhvcNAQEFBQAwMTEvMC0GA1UE\nAxMmc2VjdXJldG9rZW4uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wHhcNMTkx\nMjIyMjEyMTA3WhcNMjAwMTA4MDkzNjA3WjAxMS8wLQYDVQQDEyZzZWN1cmV0b2tl\nbi5zeXN0ZW0uZ3NlcnZpY2VhY2NvdW50LmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD\nggEPADCCAQoCggEBAKsvVDUwXeYQtySNvyI1/tZAk0sj7Zx4/1+YLUomwlK6vmEd\nyl2IXOYOj3VR7FBA24A9//nnrp+mV8YOYEOdaWX7PQo0PIPFPqdA0r7CqBUWHPfQ\n1WVHVRQY3G0c7upM97UfMes9xOrMqyvecMRk1e5S6eT12Zh2og7yiVs8gP83M1EB\nGqseUaltaadjyT35w5B0Ny0/7NdLYiv2G6Z0S821SxvSo1/wfmilnBBKYYluP0PA\n9NPznWFP6uXnX7gKxyJT9//cYVxTO6+b1TT13Yvrpm1a4EuCOhLrZH6ErHQTccAM\nhAx8mdNtbROsp0dlPKrSfqO82uFz45RXZYmSeP0CAwEAAaM4MDYwDAYDVR0TAQH/\nBAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJ\nKoZIhvcNAQEFBQADggEBACNsJ5m00gdTvD6j6ahURsGrNZ0VJ0YREVQ5U2Jtubr8\nn2fuhMxkB8147ISzfi6wZR+yNwPGjlr8JkAHAC0i+Nam9SqRyfZLqsm+tHdgFT8h\npa+R/FoGrrLzxJNRiv0Trip8hZjgz3PClz6KxBQzqL+rfGV2MbwTXuBoEvLU1mYA\no3/UboJT7cNGjZ8nHXeoKMsec1/H55lUdconbTm5iMU1sTDf+3StGYzTwC+H6yc2\nY3zIq3/cQUCrETkALrqzyCnLjRrLYZu36ITOaKUbtmZhwrP99i2f+H4Ab2i8jeMu\nk61HD29mROYjl95Mko2BxL+76To7+pmn73U9auT+xfA=\n-----END CERTIFICATE-----\n",
'jwtAuth.audiences' => 'your-project-id',
'cors.allowedOrigins' => '*',
'cors.allowHeaders' => 'X-Authorization'
注意
kid:key
对以字符串格式表示- 在':'前后不要包含空格
- 用双引号(")包围字符串文本
- 字符串必须包含换行符(\n)
jwtAuth.audiences
应包含您的Firebase项目ID
为了测试您的集成,您可以复制firebase/vanilla.html文件和firebase/vanilla-success.html文件,用作“成功”页面和显示API结果。
在两个文件中,替换Firebase配置(firebaseConfig
对象)。
您还可以更改用于测试带有身份验证的API的url
变量。
授权操作
授权模型作用于“操作”。这里列出了最重要的几个
method path - operation - description
----------------------------------------------------------------------------------------
GET /records/{table} - list - lists records
POST /records/{table} - create - creates records
GET /records/{table}/{id} - read - reads a record by primary key
PUT /records/{table}/{id} - update - updates columns of a record by primary key
DELETE /records/{table}/{id} - delete - deletes a record by primary key
PATCH /records/{table}/{id} - increment - increments columns of a record by primary key
“/openapi
”端点将仅显示您的会话中允许的内容。它还有一个特殊的“document”操作,允许您从文档中隐藏表和列。
对于以“/columns
”开头的端点,有“reflect”和“remodel”操作。这些操作可以显示或更改数据库、表或列的定义。此功能默认禁用,并且有很好的理由(小心!)在配置中添加“columns”控制器以启用此功能。
授权表、列和记录
默认情况下,所有表格、列和路径都是可访问的。如果您想限制某些表格的访问,您可以添加“授权”中间件并定义一个返回“false”的“authorization.tableHandler”函数来处理这些表格。
'authorization.tableHandler' => function ($operation, $tableName) {
return $tableName != 'license_keys';
},
上述示例将限制对所有操作对“license_keys”表格的访问。
'authorization.columnHandler' => function ($operation, $tableName, $columnName) {
return !($tableName == 'users' && $columnName == 'password');
},
上述示例将限制对所有操作对“users”表格的“password”字段的访问。
'authorization.recordHandler' => function ($operation, $tableName) {
return ($tableName == 'users') ? 'filter=username,neq,admin' : '';
},
上述示例将不允许访问用户名为“admin”的用户记录。此结构将为每个执行的查询添加一个过滤器。
'authorization.pathHandler' => function ($path) {
return $path === 'openapi' ? false : true;
},
上述示例将禁用/openapi
路由。
注意:您需要使用验证(或清洁)处理程序处理无效记录的创建。
SQL GRANT 授权
您还可以使用数据库权限(SQL GRANT 语句)来定义授权模型。在这种情况下,您不应使用“授权”中间件,但您确实需要使用“reconnect”中间件。 “reconnect”中间件的处理程序允许您指定正确的用户名和密码,如下所示
'reconnect.usernameHandler' => function () {
return 'mevdschee';
},
'reconnect.passwordHandler' => function () {
return 'secret123';
},
这将使API连接到数据库,指定“mevdschee”为用户名,“secret123”为密码。当您使用数据库权限时,OpenAPI规范在允许和不允许的操作方面不那么具体,因为权限在反射步骤中未读取。
注意:您可能希望从会话(“$_SESSION”变量)中检索用户名和密码。
清洁输入
默认情况下,所有输入都被接受并发送到数据库。如果您想在存储之前删除(某些)HTML标签,您可以添加“sanitation”中间件并定义一个返回调整值的“sanitation.handler”函数。
'sanitation.handler' => function ($operation, $tableName, $column, $value) {
return is_string($value) ? strip_tags($value) : $value;
},
上述示例将从输入中的字符串中删除所有HTML标签。
类型清洁
如果您启用了“sanitation”中间件,那么您将(自动)启用类型清洁。启用此功能时,您可以
- 在非字符字段中发送首尾空白字符(它将被忽略)。
- 向整数或bigint字段发送浮点数(它将被四舍五入)。
- 发送base64url编码的字符串(它将被转换为常规base64编码)。
- 发送任何strtotime接受的格式的时间/日期/时间戳(它将被转换)。
您可以使用配置设置“sanitation.types
”和“sanitation.tables
”来定义要应用类型清洁的类型和表格(默认为“所有”)。例如
'sanitation.types' => 'date,timestamp',
'sanitation.tables' => 'posts,comments',
在这里,我们为posts和comments表中的日期和时间戳字段启用类型清洁。
验证输入
默认情况下,所有输入都被接受并发送到数据库。如果您想以自定义方式验证输入,您可以添加“validation”中间件并定义一个返回布尔值指示值是否有效的“validation.handler”函数。
'validation.handler' => function ($operation, $tableName, $column, $value, $context) {
return ($column['name'] == 'post_id' && !is_numeric($value)) ? 'must be numeric' : true;
},
当您使用以下方式编辑id为4的评论时
PUT /records/comments/4
并发送以下body
{"post_id":"two"}
然后服务器将返回一个“422”HTTP状态码和一个友好的错误消息
{
"code": 1013,
"message": "Input validation failed for 'comments'",
"details": {
"post_id":"must be numeric"
}
}
您可以解析此输出,以便表单字段显示红色边框和相应的错误消息。
类型验证
如果您启用了“validation”中间件,那么您将(自动)启用类型验证。这包括以下错误消息
您可以使用配置设置“validation.types
”和“validation.tables
”来定义要在哪些类型和表格中应用类型验证(默认为“所有”)。例如
'validation.types' => 'date,timestamp',
'validation.tables' => 'posts,comments',
在这里,我们为posts和comments表中的日期和时间戳字段启用类型验证。
注意:已启用的类型将在列非空时检查null值。
多租户支持
支持两种多租户形式
- 单个数据库,其中每个表都有一个租户列(使用“multiTenancy”中间件)。
- 多数据库,其中每个租户都有自己的数据库(使用“reconnect”中间件)。
以下是相应中间件的说明。
多租户中间件
当您有一个单独的多租户数据库时,您可以使用“multiTenancy”中间件。如果您的租户通过“customer_id”列进行识别,那么您可以使用以下处理器
'multiTenancy.handler' => function ($operation, $tableName) {
return ['customer_id' => 12];
},
此结构为每个操作(除“创建”外)添加了一个要求“customer_id”为“12”的过滤器。它还将“customer_id”列在“创建”时设置为“12”,并从任何其他写操作中删除该列。
注意:您可能需要从会话(即“$_SESSION”变量)中检索客户ID。
重新连接中间件
当您为每个租户有单独的数据库时,您可以使用“reconnect”中间件。如果租户有自己的名为“customer_12”的数据库,那么您可以使用以下处理器
'reconnect.databaseHandler' => function () {
return 'customer_12';
},
这将使API重新连接到指定“customer_12”作为数据库名称的数据库。如果您不想使用相同的凭据,则还应实现“usernameHandler”和“passwordHandler”。
注意:您可能希望从会话(即“$_SESSION”变量)中检索数据库名称。
防止数据库抓取
您可以使用“joinLimits”和“pageLimits”中间件来防止数据库抓取。“joinLimits”中间件限制了连接操作中的表深度、表数量和返回的记录数。如果您想允许最多5个直接连接,每个最多25条记录,您可以指定
'joinLimits.depth' => 1,
'joinLimits.tables' => 5,
'joinLimits.records' => 25,
“pageLimits”中间件限制了页面数和从列表操作返回的记录数。如果您想允许最多10页,每页最多25条记录,您可以指定
'pageLimits.pages' => 10,
'pageLimits.records' => 25,
注意:当请求中没有指定页面数时,也适用最大记录数。
搜索所有文本字段
您可以使用“textSearch”中间件在列出记录时简化(通配符)文本搜索。它允许您使用以下方式指定“search”参数
GET /records/posts?search=Hello
它将返回包含“Hello”的任何文本(键入)字段的所有“posts”记录
{
"records":[
{
"id": 1,
"title": "Hello world!",
"content": "Welcome to the first post.",
"created": "2018-03-05T20:12:56Z"
}
]
}
此示例在“title”或“content”字段中搜索子字符串“Hello”。
自定义处理器
您可以使用“customization”中间件来修改请求和响应并实现任何其他功能。
'customization.beforeHandler' => function ($operation, $tableName, $request, $environment) {
$environment->start = microtime(true);
},
'customization.afterHandler' => function ($operation, $tableName, $response, $environment) {
return $response->withHeader('X-Time-Taken', microtime(true) - $environment->start);
},
上面的示例将添加一个带有API调用所花费秒数的“X-Time-Taken”头。
JSON编码选项
您可以通过设置配置参数“jsonOptions”来更改JSON的编码方式。
'jsonOptions' => JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES,
上面的示例将JSON选项设置为128+256+64 = 448,如下面的选项列表所示
JSON_HEX_TAG (1)
All < and > are converted to \u003C and \u003E.
JSON_HEX_AMP (2)
All & are converted to \u0026.
JSON_HEX_APOS (4)
All ' are converted to \u0027.
JSON_HEX_QUOT (8)
All " are converted to \u0022.
JSON_FORCE_OBJECT (16)
Outputs an object rather than an array when a non-associative array is used.
Especially useful when the recipient of the output is expecting an object and
the array is empty.
JSON_NUMERIC_CHECK (32)
Encodes numeric strings as numbers.
JSON_UNESCAPED_SLASHES (64)
Don't escape /.
JSON_PRETTY_PRINT (128)
Use whitespace in returned data to format it.
JSON_UNESCAPED_UNICODE (256)
Encode multibyte Unicode characters literally (default is to escape as \uXXXX).
JSON_PARTIAL_OUTPUT_ON_ERROR (512)
Substitute some unencodable values instead of failing.
JSON_PRESERVE_ZERO_FRACTION (1024)
Ensures that float values are always encoded as a float value.
JSON_UNESCAPED_LINE_TERMINATORS (2048)
The line terminators are kept unescaped when JSON_UNESCAPED_UNICODE is supplied.
It uses the same behaviour as it was before PHP 7.1 without this constant.
Available as of PHP 7.1.0.
来源:PHP的JSON常量文档
JSON中间件
您可以使用“json”中间件将JSON字符串作为JSON对象和数组进行读写。
当启用“json”中间件时,JSON字符串将自动检测。
您可以通过指定特定的表和/或字段名称来限制扫描
'json.tables' => 'products',
'json.columns' => 'properties',
这将改变输出
GET /records/products/1
没有“json”中间件时,输出将是
{
"id": 1,
"name": "Calculator",
"price": "23.01",
"properties": "{\"depth\":false,\"model\":\"TRX-120\",\"width\":100,\"height\":null}",
}
带有“json”中间件时,输出将是
{
"id": 1,
"name": "Calculator",
"price": "23.01",
"properties": {
"depth": false,
"model": "TRX-120",
"width": 100,
"height": null
},
}
这同样适用于创建或修改JSON字符串字段(在批量操作中使用时也是如此)。
请注意,JSON字符串字段不能部分更新,并且此中间件默认禁用。您可以使用“middlewares”配置设置启用“json”中间件。
XML中间件
您可以使用“xml”中间件将输入和输出从JSON转换为XML。此请求
GET /records/posts/1
输出(当“pretty printed”时)
{
"id": 1,
"user_id": 1,
"category_id": 1,
"content": "blog started"
}
注意(注意“format”查询参数)
GET /records/posts/1?format=xml
输出
<root>
<id>1</id>
<user_id>1</user_id>
<category_id>1</category_id>
<content>blog started</content>
</root>
此功能默认禁用,必须使用“middlewares”配置设置启用。
文件上传
文件上传通过FileReader API支持,请查看示例。
OpenAPI规范
在 "/openapi" 终端点提供 OpenAPI 3.0(以前称为“Swagger”)规范。这是您 API 的机器可读即时文档。要了解更多信息,请查看以下链接
- Swagger 编辑器可用于查看和调试生成的规范。
- OpenAPI 规范是创建 OpenAPI 规范的指南。
- Swagger Petstore是使用 OpenAPI 生成的示例文档。
缓存
可以通过“cacheType”配置参数配置 4 种缓存引擎
- TempFile(默认)
- Redis
- Memcache
- Memcached
您可以通过运行以下命令安装最后三种引擎的依赖项
sudo apt install php-redis redis
sudo apt install php-memcache memcached
sudo apt install php-memcached memcached
默认引擎没有依赖项,并将使用系统“temp”路径中的临时文件。
您可以使用“cachePath”配置参数指定临时文件的文件系统路径,或者在您使用非默认“cacheType”的情况下,指定缓存服务器的计算机名(可选带端口)。
类型
以下是以它们的长度、类别、JSON 类型和格式支持的类型
请注意,几何类型是非 jdbc 类型,因此支持有限。
JavaScript 中的数据类型
JavaScript 和 JavaScript 对象表示法(JSON)并不非常适合读取数据库记录。十进制、日期/时间、二进制和几何类型必须在 JSON 中表示为字符串(二进制以 base64 编码,几何类型以 WKT 格式表示)。以下是另外两个更严重的问题。
64 位整数
JavaScript 不支持 64 位整数。所有数字都存储为 64 位浮点值。64 位浮点数的尾数只有 53 位,这就是为什么所有大于 53 位的整数在 JavaScript 中都可能导致问题。
Inf 和 NaN 浮点数
有效的浮点值 'Infinite'(用 '1/0' 计算)和 'Not a Number'(用 '0/0' 计算)不能在 JSON 中表示,因为它们不受 JSON 规范 支持。当这些值存储在数据库中时,您无法像此脚本输出数据库记录为 JSON 那样读取它们。
错误
可能会报告以下错误
以下 JSON 结构被使用
{
"code":1002,
"message":"Argument count mismatch in '1'"
}
NB:任何非错误响应都将具有状态:200 OK
状态
要连接到您的监控,有一个“ping”终端点
GET /status/ping
它应该返回状态 200 和数据
{
"db": 42,
"cache": 9
}
这些可以用来衡量连接数据库和缓存读取数据的时间(以微秒为单位)。
自定义控制器
您可以通过编写自己的自定义控制器类来添加自己的自定义 REST API 终端点。该类必须提供一个接受五个参数的构造函数。使用这些参数,您可以注册自己的终端点到现有的路由器。此终端点可以使用数据库和/或数据库的反射类。
以下是一个自定义控制器类的示例
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Tqdev\PhpCrudApi\Cache\Cache;
use Tqdev\PhpCrudApi\Column\ReflectionService;
use Tqdev\PhpCrudApi\Controller\Responder;
use Tqdev\PhpCrudApi\Database\GenericDB;
use Tqdev\PhpCrudApi\Middleware\Router\Router;
class MyHelloController {
private $responder;
public function __construct(Router $router, Responder $responder, GenericDB $db, ReflectionService $reflection, Cache $cache)
{
$router->register('GET', '/hello', array($this, 'getHello'));
$this->responder = $responder;
}
public function getHello(ServerRequestInterface $request): ResponseInterface
{
return $this->responder->success(['message' => "Hello World!"]);
}
}
然后您可以像这样在配置对象中注册您的自定义控制器类
$config = new Config([
...
'customControllers' => 'MyHelloController',
...
]);
customControllers
配置支持逗号分隔的自定义控制器类列表。
测试
我主要在 Ubuntu 上进行测试,并且有以下测试设置
- (Docker) Debian 10 with PHP 7.3, MariaDB 10.3, PostgreSQL 11.4 (PostGIS 2.5) and SQLite 3.27
- (Docker) Debian 11 with PHP 7.4, MariaDB 10.5, PostgreSQL 13.4 (PostGIS 3.1) and SQLite 3.34
- (Docker) Debian 12 with PHP 8.2, MariaDB 10.11, PostgreSQL 15.3 (PostGIS 3.3) and SQLite 3.40
- (Docker) RockyLinux 8 with PHP 7.2, MariaDB 10.3 and SQLite 3.26
- (Docker) RockyLinux 9 with PHP 8.0, MariaDB 10.5 and SQLite 3.34
- (Docker) Ubuntu 18.04 with PHP 7.2, MySQL 5.7, PostgreSQL 10.4 (PostGIS 2.4) and SQLite 3.22
- (Docker) Ubuntu 20.04 with PHP 7.4, MySQL 8.0, PostgreSQL 12.15 (PostGIS 3.0) and SQLite 3.31 and SQL Server 2019
- (Docker) Ubuntu 22.04 with PHP 8.1, MySQL 8.0, PostgreSQL 14.2 (PostGIS 3.2) and SQLite 3.37
- (Docker) Ubuntu 24.04 with PHP 8.3, MySQL 8.0, PostgreSQL 16.2 (PostGIS 3.4) and SQLite 3.45
这并不涵盖所有环境(尚未涵盖),所以请通知我失败的测试并报告你的环境。我将尝试在项目的“docker”文件夹中涵盖大多数相关设置。
运行
要本地运行功能测试,您可以运行以下命令
php build.php
php test.php
这将在“tests”目录下运行功能测试。它使用相应子目录中的数据库转储(固定装置)和数据库配置(配置)。
漂亮的URL
您可以将URL“重写”以从URL中删除“api.php”。
Apache配置示例
启用mod_rewrite并在您的“.htaccess”文件中添加以下内容
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ api.php/$1 [L,QSA]
“.htaccess”文件需要放在与“api.php”相同的文件夹中。
Nginx配置示例
使用以下配置在Nginx和PHP-FPM下提供API服务
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/html;
index index.php index.html index.htm index.nginx-debian.html;
server_name server_domain_or_IP;
location / {
try_files $uri $uri/ =404;
}
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
try_files $fastcgi_script_name =404;
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;
fastcgi_index index.php;
include fastcgi.conf;
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
}
location ~ /\.ht {
deny all;
}
}
Docker测试
使用以下命令安装docker,然后注销并登录以使更改生效
sudo apt install docker.io docker-buildx
sudo usermod -aG docker ${USER}
要运行docker测试,请从docker目录运行“build_all.sh”和“run_all.sh”。输出应该是
================================================
Debian 10 (PHP 7.3)
================================================
[1/4] Starting MariaDB 10.3 ..... done
[2/4] Starting PostgreSQL 11.4 .. done
[3/4] Starting SQLServer 2017 ... skipped
[4/4] Cloning PHP-CRUD-API v2 ... skipped
------------------------------------------------
mysql: 120 tests ran in 921 ms, 1 skipped, 0 failed
pgsql: 120 tests ran in 1058 ms, 1 skipped, 0 failed
sqlsrv: skipped, driver not loaded
sqlite: 120 tests ran in 752 ms, 13 skipped, 0 failed
================================================
Debian 11 (PHP 7.4)
================================================
[1/4] Starting MariaDB 10.5 ..... done
[2/4] Starting PostgreSQL 13.4 .. done
[3/4] Starting SQLServer 2017 ... skipped
[4/4] Cloning PHP-CRUD-API v2 ... skipped
------------------------------------------------
mysql: 120 tests ran in 914 ms, 1 skipped, 0 failed
pgsql: 120 tests ran in 997 ms, 1 skipped, 0 failed
sqlsrv: skipped, driver not loaded
sqlite: 120 tests ran in 735 ms, 13 skipped, 0 failed
================================================
Debian 12 (PHP 8.2)
================================================
[1/4] Starting MariaDB 10.11 .... done
[2/4] Starting PostgreSQL 15.3 .. done
[3/4] Starting SQLServer 2019 ... skipped
[4/4] Cloning PHP-CRUD-API v2 ... skipped
------------------------------------------------
mysql: 120 tests ran in 1016 ms, 1 skipped, 0 failed
pgsql: 120 tests ran in 1041 ms, 1 skipped, 0 failed
sqlsrv: skipped, driver not loaded
sqlite: 120 tests ran in 733 ms, 13 skipped, 0 failed
================================================
RockyLinux 8 (PHP 7.2)
================================================
[1/4] Starting MariaDB 10.3 ..... done
[2/4] Starting PostgreSQL 11 .... skipped
[3/4] Starting SQLServer 2017 ... skipped
[4/4] Cloning PHP-CRUD-API v2 ... skipped
------------------------------------------------
mysql: 120 tests ran in 935 ms, 1 skipped, 0 failed
pgsql: skipped, driver not loaded
sqlsrv: skipped, driver not loaded
sqlite: 120 tests ran in 746 ms, 13 skipped, 0 failed
================================================
RockyLinux 9 (PHP 8.0)
================================================
[1/4] Starting MariaDB 10.5 ..... done
[2/4] Starting PostgreSQL 12 .... skipped
[3/4] Starting SQLServer 2017 ... skipped
[4/4] Cloning PHP-CRUD-API v2 ... skipped
------------------------------------------------
mysql: 120 tests ran in 928 ms, 1 skipped, 0 failed
pgsql: skipped, driver not loaded
sqlsrv: skipped, driver not loaded
sqlite: 120 tests ran in 728 ms, 13 skipped, 0 failed
================================================
Ubuntu 18.04 (PHP 7.2)
================================================
[1/4] Starting MySQL 5.7 ........ done
[2/4] Starting PostgreSQL 10.4 .. done
[3/4] Starting SQLServer 2017 ... skipped
[4/4] Cloning PHP-CRUD-API v2 ... skipped
------------------------------------------------
mysql: 120 tests ran in 1296 ms, 1 skipped, 0 failed
pgsql: 120 tests ran in 1056 ms, 1 skipped, 0 failed
sqlsrv: skipped, driver not loaded
sqlite: 120 tests ran in 772 ms, 13 skipped, 0 failed
================================================
Ubuntu 20.04 (PHP 7.4)
================================================
[1/4] Starting MySQL 8.0 ........ done
[2/4] Starting PostgreSQL 12.2 .. done
[3/4] Starting SQLServer 2019 ... done
[4/4] Cloning PHP-CRUD-API v2 ... skipped
------------------------------------------------
mysql: 120 tests ran in 1375 ms, 1 skipped, 0 failed
pgsql: 120 tests ran in 868 ms, 1 skipped, 0 failed
sqlsrv: 120 tests ran in 5713 ms, 1 skipped, 0 failed
sqlite: 120 tests ran in 733 ms, 13 skipped, 0 failed
================================================
Ubuntu 22.04 (PHP 8.1)
================================================
[1/4] Starting MySQL 8.0 ........ done
[2/4] Starting PostgreSQL 14.2 .. done
[3/4] Starting SQLServer 2019 ... skipped
[4/4] Cloning PHP-CRUD-API v2 ... skipped
------------------------------------------------
mysql: 120 tests ran in 1372 ms, 1 skipped, 0 failed
pgsql: 120 tests ran in 1064 ms, 1 skipped, 0 failed
sqlsrv: skipped, driver not loaded
sqlite: 120 tests ran in 727 ms, 13 skipped, 0 failed
================================================
Ubuntu 24.04 (PHP 8.3)
================================================
[1/4] Starting MySQL 8. ........ done
[2/4] Starting PostgreSQL 16.2 .. done
[3/4] Starting SQLServer 2019 ... skipped
[4/4] Cloning PHP-CRUD-API v2 ... skipped
------------------------------------------------
mysql: 120 tests ran in 1344 ms, 1 skipped, 0 failed
pgsql: 120 tests ran in 856 ms, 1 skipped, 0 failed
sqlsrv: skipped, driver not loaded
sqlite: 120 tests ran in 722 ms, 13 skipped, 0 failed
上述测试运行(包括启动数据库)在我的慢速笔记本电脑上不到5分钟。
$ ./run.sh
1) debian10
2) debian11
3) debian12
4) rockylinux8
5) rockylinux9
6) ubuntu18
7) ubuntu20
8) ubuntu22
> 6
================================================
Ubuntu 18.04 (PHP 7.2)
================================================
[1/4] Starting MySQL 5.7 ........ done
[2/4] Starting PostgreSQL 10.4 .. done
[3/4] Starting SQLServer 2017 ... skipped
[4/4] Cloning PHP-CRUD-API v2 ... skipped
------------------------------------------------
mysql: 120 tests ran in 1296 ms, 1 skipped, 0 failed
pgsql: 120 tests ran in 1056 ms, 1 skipped, 0 failed
sqlsrv: skipped, driver not loaded
sqlite: 120 tests ran in 772 ms, 13 skipped, 0 failed
root@b7ab9472e08f:/php-crud-api#
如您所见,“run.sh”脚本为您提供了选择docker环境的提示。在此环境中,本地文件被挂载。这允许在不同的环境中轻松调试。完成操作后,您可以输入“exit”。
Docker镜像
在存储库中有一个Dockerfile
,用于在以下位置构建镜像
https://hub.docker.com/r/mevdschee/php-crud-api
它将在每个发布时自动构建。最新的标签指向最后一个发布。
Docker镜像接受配置中的环境变量参数。
Docker Compose
此存储库还包含一个docker-compose.yml
文件,您可以使用以下方式安装/构建/运行它
sudo apt install docker-compose
docker-compose build
docker-compose up
这将设置数据库(MySQL)和web服务器(Apache),并使用测试中使用的示例数据运行应用程序。
通过打开以下URL测试脚本(在容器中运行)
http://localhost:8080/records/posts/1
星历史记录
享受!