Script PHP 7 de un solo archivo que agrega una API REST a una base de datos InnoDB MySQL 5.6. PostgreSQL 9.1 y MS SQL Server 2012 son totalmente compatibles.
NB: Esta es la implementación de referencia de TreeQL en PHP.
NB: ¿Estás buscando v1? Está aquí: https://github.com/Feliphegomez/api-rest-php/tree/v1.0.0
- PHP 7.0 o superior con controladores PDO para MySQL, PgSQL o SqlSrv habilitados
- MySQL 5.6 / MariaDB 10.0 o superior para características espaciales en MySQL
- PostGIS 2.0 o superior para características espaciales en PostgreSQL 9.1 o superior
- SQL Server 2012 o superior (2017 para soporte de Linux)
- ¿Ver los enteros como cadenas de texto? Asegúrese de habilitar la extensión nd_pdo_mysql y deshabilite pdo_mysql .
Esta es una aplicación de un solo archivo! Cargue "api.php
" en algún lugar y disfrute!
Para el desarrollo local, puede ejecutar el servidor web incorporado de PHP:
php -S localhost:8080/api/
Pruebe el script abriendo la siguiente URL:
http://localhost:8080/api/api.php/records/posts/1
http://localhost:8080/api/posts/1 (si se incluye el archivo .htaccess)
No olvide modificar la configuración en la parte inferior del archivo.
Edite las siguientes líneas en la parte inferior del archivo "api.php
":
$config = new Config([
'username' => 'xxx',
'password' => 'xxx',
'database' => 'xxx',
]);
Estas son todas las opciones de configuración y su valor predeterminado entre paréntesis:
"driver": mysql , pgsql o sqlsrv ( mysql ) "address": Nombre de host del servidor de base de datos ( localhost ) "port": puerto TCP del servidor de la base de datos (predeterminado en el controlador predeterminado) "username": nombre de usuario del usuario que se conecta a la base de datos (no predeterminado) "password": contraseña del usuario que se conecta a la base de datos (no predeterminada) "database": Base de datos a la que se realiza la conexión (no predeterminada) "middlewares": Lista de middlewares para cargar ( cors ) "controllers": lista de controladores para cargar ( records,openapi ) "openApiBase": información de OpenAPI ( {"info":{"title":"PHP-CRUD-API","version":"1.0.0"}} ) "cacheType": TempFile , Redis , Memcache , Memcached o NoCache ( TempFile ) "cachePath": ruta / dirección del caché (por defecto al directorio temporal del sistema) "cacheTime": número de segundos que la memoria caché es válida ( 10 ) "debug": muestra los errores en el encabezado "X-Debug-Info" ( false )
El código reside en el directorio "src
". Puedes acceder a ella en la URL:
http://localhost:8080/api/src/records/posts/1
Puede compilar todos los archivos en un solo archivo "api.php
" usando:
php build.php
NB: La secuencia de comandos agrega las clases en orden alfabético (directorios primero).
Estas limitaciones también estaban presentes en v1:
- Las claves primarias deben ser de incremento automático (de 1 a 2 ^ 53) o UUID
- Las claves primarias o externas compuestas no son compatibles
- No se admiten escrituras complejas (transacciones)
- Las consultas complejas que llaman a funciones (como "concat" o "suma") no son compatibles
- La base de datos debe soportar y definir restricciones de clave externa
Estas características coinciden con las características en v1 (ver rama "v1"):
- Un solo archivo PHP, fácil de implementar.
- Muy poco código, fácil de adaptar y mantener.
-
Transmisión de datos, bajo consumo de memoria. - Admite variables POST como entrada (x-www-form-urlencoded)
- Admite un objeto JSON como entrada
- Admite una matriz JSON como entrada (inserción por lotes)
-
Admite la carga de archivos desde formularios web (multipart / form-data) -
Salida JSON condensada: la primera fila contiene nombres de campo - Desinfectar y validar la entrada utilizando devoluciones de llamada
- Sistema de permisos para bases de datos, tablas, columnas y registros.
- Los diseños de bases de datos multiusuario son compatibles
- Soporte CORS multi-dominio para peticiones de dominio cruzado
- Soporte para la lectura de resultados combinados de varias tablas.
- Soporte de búsqueda en múltiples criterios
- Paginación, búsqueda, clasificación y selección de columnas.
- Detección de relaciones con resultados anidados (belongsTo, hasMany y HABTM)
-
Relación "transforma" (de JSON condensado) para PHP y JavaScript - Soporte de incremento atómico mediante PATCH (para contadores)
- Campos binarios soportados con codificación base64
- Campos y filtros espaciales / SIG compatibles con WKT
-
Soporte de datos no estructurados a través de JSON / JSONB - Generar documentación de API utilizando herramientas OpenAPI
- Autenticación a través de token JWT o nombre de usuario / contraseña
-
Soporte de SQLite
NB: Sin marca significa: aún no implementado. Striken significa: no será implementado.
Estas características son nuevas y no se incluyeron en v1.
- No refleja en cada solicitud (mejor rendimiento)
- Los filtros complejos (con "y" y "o") son compatibles
- Soporte para salida de estructura de base de datos en JSON
- Soporte para datos booleanos y binarios en todos los motores de bases de datos
- Soporte para datos relacionales en lectura (no solo en operación de lista)
- Soporte para middleware para modificar todas las operaciones (también lista)
- Informe de errores en JSON con el estado HTTP correspondiente
- Soporte para autenticación básica y vía proveedor de autenticación (JWT)
- Soporte para funcionalidad básica de firewall.
Puede habilitar el siguiente middleware utilizando el parámetro de configuración "middlewares":
- "firewall": limita el acceso a direcciones IP específicas
- "cors": soporte para solicitudes CORS (habilitado por defecto)
- "xsrf": Bloquee los ataques XSRF usando el método 'Double Submit Cookie'
- "ajaxOnly": restringe las solicitudes que no sean AJAX para evitar ataques XSRF
- "jwtAuth": soporte para "autenticación JWT"
- "basicAuth": Soporte para "Autenticación básica"
- "authorization": restringe el acceso a ciertas tablas o columnas
- "validation": devuelve errores de validación de entrada para reglas personalizadas
- "sanitation": aplicar saneamiento de entrada en crear y actualizar
- "multiTenancy": restringe el acceso de los inquilinos en un escenario con múltiples inquilinos
- "customization": proporciona controladores para la personalización de solicitudes y respuestas
El parámetro de configuración "middlewares" es una lista separada por comas de middlewares habilitados. Puede ajustar el comportamiento del middleware utilizando parámetros de configuración específicos del middleware:
- "firewall.reverseProxy": se establece en "true" cuando se utiliza un proxy inverso ("")
- "firewall.allowedIpAddresses": lista de direcciones IP que pueden conectarse ("")
- "cors.allowedOrigins": los orígenes permitidos en los encabezados CORS ("*")
- "cors.allowHeaders": los encabezados permitidos en la solicitud CORS ("Content-Type, X-XSRF-TOKEN")
- "cors.allowMethods": los métodos permitidos en la solicitud CORS ("OPTIONS, GET, PUT, POST, DELETE, PATCH")
- "cors.allowCredentials": para permitir credenciales en la solicitud CORS ("true")
- "cors.exposeHeaders": encabezados de lista blanca a los que los navegadores pueden acceder ("")
- "cors.maxAge": el tiempo que la concesión de CORS es válida en segundos ("1728000")
- "xsrf.excludeMethods": los métodos que no requieren la protección XSRF ("OPTIONS, GET")
- "xsrf.cookieName": el nombre de la cookie de protección XSRF ("XSRF-TOKEN")
- "xsrf.headerName": el nombre del encabezado de protección XSRF ("X-XSRF-TOKEN")
- "ajaxOnly.excludeMethods": los métodos que no requieren AJAX ("OPTIONS, GET")
- "ajaxOnly.headerName": el nombre del encabezado requerido ("X-Requested-With")
- "ajaxOnly.headerValue": el valor del encabezado requerido ("XMLHttpRequest")
- "jwtAuth.mode": configúrelo como "opcional" si desea permitir el acceso anónimo ("requerido")
- "jwtAuth.header": nombre del encabezado que contiene el token JWT ("X-Autorización")
- "jwtAuth.leeway": el número aceptable de segundos de inclinación del reloj ("5")
- "jwtAuth.ttl": el número de segundos que el token es válido ("30")
- "jwtAuth.secret": el secreto compartido utilizado para firmar el token JWT con ("")
- "jwtAuth.algorithms": los algoritmos permitidos, vacío significa 'todos' ("")
- "jwtAuth.audiences": las audiencias permitidas, vacío significa "todos" ("")
- "jwtAuth.issuers": los emisores que están permitidos, vacío significa 'todos' ("")
- "basicAuth.mode": configúrelo como "opcional" si desea permitir el acceso anónimo ("requerido")
- "basicAuth.realm": texto que se le solicitará al mostrar el inicio de sesión ("Se requieren nombre de usuario y contraseña")
- "basicAuth.passwordFile": el archivo a leer para las combinaciones de nombre de usuario / contraseña (".htpasswd")
- "permission.tableHandler": controlador para implementar las reglas de autorización de tablas ("")
- "permission.columnHandler": controlador para implementar las reglas de autorización de columna ("")
- "permission.recordHandler": controlador para implementar reglas de filtro de autorización de registros ("")
- "validation.handler": controlador para implementar reglas de validación para valores de entrada ("")
- "sanitation.handler": controlador para implementar reglas de saneamiento para valores de entrada ("")
- "multiTenancy.handler": controlador para implementar reglas simples de tenencia múltiple ("")
- "customization.beforeHandler": Controlador para implementar la personalización de la solicitud ("")
- "customization.afterHandler": controlador para implementar la personalización de la respuesta ("")
Si no especifica estos parámetros en la configuración, se utilizan los valores predeterminados (entre paréntesis).
TreeQL permite crear un "árbol" de objetos JSON en función de la estructura de la base de datos SQL (relaciones) y su consulta.
Se basa libremente en el estándar REST y también está inspirado en json: api.
La tabla de publicaciones de ejemplo tiene solo unos pocos campos:
posts
=======
id
title
content
created
Las siguientes operaciones de la lista CRUD + actúan en esta tabla.
Si desea crear un registro, la solicitud se puede escribir en formato de URL como:
POST /records/posts
Tienes que enviar un cuerpo que contiene:
{
"title": "Black is the new red",
"content": "This is the second post.",
"created": "2018-03-06T21:34:01Z"
}
Y devolverá el valor de la clave principal del registro recién creado:
2
Para leer un registro de esta tabla, la solicitud se puede escribir en formato de URL como:
GET /records/posts/1
Donde "1" es el valor de la clave principal del registro que desea leer. Volverá
{
"id": 1
"title": "Hello world!",
"content": "Welcome to the first post.",
"created": "2018-03-05T20:12:56Z"
}
En las operaciones de lectura puede aplicar uniones.
Para actualizar un registro en esta tabla, la solicitud se puede escribir en formato de URL como:
PUT /records/posts/1
Donde "1" es el valor de la clave principal del registro que desea actualizar. Enviar como un cuerpo:
{
"title": "Adjusted title!"
}
Esto ajusta el título del post. Y el valor de retorno es el número de filas que se establecen:
1
Si desea eliminar un registro de esta tabla, la solicitud se puede escribir en formato de URL como:
DELETE /records/posts/1
Y devolverá el número de filas eliminadas:
1
Para listar los registros de esta tabla, la solicitud se puede escribir en formato de URL como:
GET /records/posts
It will return:
{
"records":[
{
"id": 1,
"title": "Hello world!",
"content": "Welcome to the first post.",
"created": "2018-03-05T20:12:56Z"
}
]
}
En las operaciones de lista puede aplicar filtros y uniones.
Los filtros proporcionan la funcionalidad de búsqueda, en la lista de llamadas, usando el parámetro "filtro". Debe especificar el nombre de la columna, una coma, el tipo de coincidencia, otra coma y el valor que desea filtrar. Estos son tipos de concordancia soportados:
- "cs": contiene cadena (cadena contiene valor)
- "sw": comienza con (la cadena comienza con el valor)
- "ew": termina con (final de cadena con valor)
- "eq": igual (la cadena o el número coinciden exactamente)
- "lt": menor que (el número es menor que el valor)
- "le": menor o igual (el número es menor o igual al valor)
- "ge": mayor o igual (el número es mayor o igual que el valor)
- "gt": mayor que (el número es mayor que el valor)
- "bt": entre (el número está entre dos valores separados por comas)
- "in": in (el número o la cadena está en una lista de valores separados por comas)
- "is": es nulo (el campo contiene el valor "NULL")
Puede negar todos los filtros al anteponer un carácter "n", de modo que "eq" se convierta en "neq". Ejemplos de uso del filtro son:
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,2
GET /records/categories?filter=id,bt,1,1
Salida:
{
"records":[
{
"id": 1
"name": "Internet"
}
]
}
En la siguiente sección profundizaremos en cómo puede aplicar varios filtros en una sola llamada de lista.
Los filtros pueden aplicarse aplicando el parámetro "filtro" en la URL. Por ejemplo la siguiente URL:
GET /records/categories?filter=id,gt,1&filter=id,lt,3
solicitará todas las categorías "donde id> 1 e id <3". Si desea "where id = 2 o id = 4" debe escribir:
GET /records/categories?filter1=id,eq,2&filter2=id,eq,4
Como ve, agregamos un número al parámetro "filtro" para indicar que se debe aplicar "OR" en lugar de "AND". Tenga en cuenta que también puede repetir "filter1" y crear un "AND" dentro de un "OR". Dado que también puede ir un nivel más profundo al agregar una letra (af), puede crear casi cualquier árbol de condiciones razonablemente complejo.
NOTA: solo puede filtrar en la tabla solicitada (no incluida en ella) y los filtros solo se aplican en la lista de llamadas.
Por defecto, todas las columnas están seleccionadas. Con el parámetro "incluir" puede seleccionar columnas específicas. Puede usar un punto para separar el nombre de la tabla del nombre de la columna. Las columnas múltiples deben estar separadas por comas. Se puede utilizar un asterisco ("*") como comodín para indicar "todas las columnas". Similar a "incluir" puede usar el parámetro "excluir" para eliminar ciertas columnas:
GET /records/categories/1?include=name
GET /records/categories/1?include=categories.name
GET /records/categories/1?exclude=categories.id
Salida:
{
"name": "Internet"
}
NOTA: las columnas que se utilizan para incluir entidades relacionadas se agregan automáticamente y no se pueden dejar fuera de la salida.
Con el parámetro "orden" se puede ordenar. Por defecto, la clasificación está en orden ascendente, pero al especificar "desc" esto se puede revertir:
GET /records/categories?order=name,desc
GET /records/categories?order=id,desc&order=name
Salida:
{
"records":[
{
"id": 3
"name": "Web development"
},
{
"id": 1
"name": "Internet"
}
]
}
NOTA: puede ordenar en múltiples campos utilizando múltiples parámetros de "orden". No se puede ordenar en columnas "unidas".
El parámetro "tamaño" limita el número de registros devueltos. Esto se puede usar para las N listas principales junto con el parámetro "orden" (usar orden descendente).
GET /records/categories?order=id,desc&size=1
Salida:
{
"records":[
{
"id": 3
"name": "Web development"
}
]
}
NOTA: si también desea conocer el número total de registros, puede utilizar el parámetro "página".
El parámetro "página" contiene la página solicitada. El tamaño de página predeterminado es 20, pero se puede ajustar (por ejemplo, a 50).
GET /records/categories?order=id&page=1
GET /records/categories?order=id&page=1,50
Salida:
{
"records":[
{
"id": 1
"name": "Internet"
},
{
"id": 3
"name": "Web development"
}
],
"results": 2
}
NOTA: como las páginas que no están ordenadas no pueden paginarse, las páginas se ordenarán por clave principal.
Digamos que usted tiene una tabla de publicaciones que tiene comentarios (realizados por los usuarios) y que las publicaciones pueden tener etiquetas.
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
Cuando desee enumerar las publicaciones con sus usuarios y etiquetas de comentarios, puede solicitar dos rutas de "árbol":
posts -> comments -> users
posts -> post_tags -> tags
Estas rutas tienen la misma raíz y esta solicitud se puede escribir en formato de URL como:
GET /records/posts?join=comments,users&join=tags
Aquí puede omitir la tabla intermedia que vincula las publicaciones a las etiquetas. En este ejemplo, verá los tres tipos de relaciones de tabla (hasMany, belongsTo y hasAndBelongsToMany) en efecto:
- "post" tiene muchos "comments"
- "comment" pertenece a "user"
- "post" tiene y pertenece a muchas "tags"
Esto puede llevar a los siguientes datos 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"
}
]
}
]
}
Verá que se detectan las relaciones "belongsTo" y el valor al que se hace referencia reemplaza el valor de la clave externa. En el caso de "hasMany" y "hasAndBelongsToMany", el nombre de la tabla se utiliza una nueva propiedad en el objeto.
Cuando desee crear, leer, actualizar o eliminar, puede especificar varios valores de clave principal en la URL. También debe enviar una matriz en lugar de un objeto en el cuerpo de la solicitud para crear y actualizar.
Para leer un registro de esta tabla, la solicitud se puede escribir en formato de URL como:
GET /records/posts/1,2
El resultado puede ser:
[
{
"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"
}
]
De forma similar, cuando desea realizar una actualización por lotes, la solicitud en formato de URL se escribe como:
PUT /records/posts/1,2
Donde "1" y "2" son los valores de las claves primarias de los registros que desea actualizar. El cuerpo debe contener el mismo número de objetos, ya que hay claves primarias en la URL:
[
{
"title": "Adjusted title for ID 1"
},
{
"title": "Adjusted title for ID 2"
}
]
Esto ajusta los títulos de los mensajes. Y los valores de retorno son el número de filas que se establecen:
1,1
Lo que significa que había dos operaciones de actualización y cada una de ellas había establecido una fila. Las operaciones por lotes utilizan transacciones de la base de datos, por lo que o bien todas tienen éxito o todas fallan (las exitosas se devuelven).
Para el soporte espacial hay un conjunto adicional de filtros que se pueden aplicar en columnas de geometría y que comienzan con una "s":
- "sco": contiene espacial (la geometría contiene otra)
- "scr": cruces espaciales (la geometría cruza otra)
- "sdi": separación espacial (la geometría es diferente de otra)
- "seq": espacial igual (la geometría es igual a otra)
- "sin": intersecciones espaciales (la geometría se interseca con otra)
- "sov": superposiciones espaciales (la geometría se superpone a otra)
- "sto": toques espaciales (la geometría toca a otra)
- "swi": espacial dentro (la geometría está dentro de otra)
- "sic": el espacio está cerrado (la geometría es cerrada y simple)
- "sis": espacial es simple (la geometría es simple)
- "siv": el espacio es válido (la geometría es válida)
Estos filtros se basan en los estándares OGC y también lo es la especificación WKT en la que se representan las columnas de geometría.
El soporte de GeoJSON es una vista de solo lectura en las tablas y registros en formato GeoJSON. Estas solicitudes son compatibles:
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
El "/geojson
" punto final utiliza el "/records
" punto interno y hereda toda la funcionalidad, como las combinaciones y los filtros.
También admite un parámetro de "geometría" para indicar el nombre de la columna de geometría en caso de que la tabla tenga más de una.
Para las vistas de mapa, admite el parámetro "bbox" en el que puede especificar las coordenadas superior izquierda y inferior derecha (separadas por comas).
Los siguientes tipos de geometría son compatibles con la implementación de GeoJSON:
- Point - Punto
- MultiPoint
- LineString
- MultiLineString
- Polygon - Polígono
- MultiPolygon
La funcionalidad GeoJSON está habilitada de forma predeterminada, pero se puede deshabilitar usando la configuración de "controladores".
Actualmente hay tres tipos de autenticación soportados. Todos ellos almacenan el usuario autenticado en el $_SESSION
súper global.
Esta variable se puede usar en los controladores de autorización para decidir si o no sombeody debería tener acceso de lectura o escritura a ciertas tablas, columnas o registros.
La siguiente descripción general muestra los tipos de middleware de autenticación que puede habilitar.
Nombre | Middleware | Autenticado a través de | Los usuarios se almacenan en | Variable de sesion |
---|---|---|---|---|
Database | dbAuth | '/login' punto final | tabla de base de datos | $_SESSION['user'] |
Basic | basicAuth | 'Authorization' Encabezado | '.htpasswd' archivo | $_SESSION['username'] |
JWT | jwtAuth | 'Authorization' Encabezado | proveedor de identidad | $_SESSION['claims'] |
A continuación encontrará más información sobre cada uno de los tipos de autenticación.
El middleware de autenticación de base de datos define dos nuevas rutas:
method path - parameters - description
----------------------------------------------------------------------------------------
POST /login - username + password - logs a user in by username and password
POST /logout - - logs out the currently logged in user
Un usuario puede iniciar sesión enviando su nombre de usuario y contraseña al punto final de inicio de sesión (en formato JSON).
El usuario autenticado (con todas sus propiedades) se almacenará en la $_SESSION['user']
variable.
El usuario puede cerrar la sesión enviando una solicitud POST con un cuerpo vacío al punto final de cierre de sesión.
Las contraseñas se almacenan como hashes en la columna de contraseña en la tabla de usuarios.
Para generar el valor de hash para la contraseña 'pass2' puede ejecutar en la línea de comando:
php -r 'echo password_hash("pass2", PASSWORD_DEFAULT)."\n";'
¡Es IMPORTANTE restringir el acceso a la tabla de usuarios utilizando el middleware de "authorization", de lo contrario, todos los usuarios pueden agregar, modificar o eliminar libremente cualquier cuenta! La configuración mínima se muestra a continuación:
'middlewares' => 'dbAuth,authorization',
'authorization.tableHandler' => function ($operation, $tableName) {
return $tableName != 'users';
},
Tenga en cuenta que este middleware utiliza cookies de sesión y almacena el estado de inicio de sesión en el servidor.
El tipo Básico es compatible con un archivo (por defecto '.htpasswd') que contiene a los usuarios y sus contraseñas (con hash) separadas por dos puntos (':').
Cuando las contraseñas se ingresan en texto sin formato, se llenan automáticamente.
El nombre de usuario autenticado se almacenará en la $_SESSION['username']
variable.
Debe enviar un encabezado de "Autorización" que contenga un nombre de usuario y una contraseña codificados en base64 url y separados por dos puntos después de la palabra "Basic".
Authorization: Basic dXNlcm5hbWUxOnBhc3N3b3JkMQ
Este ejemplo envía la cadena "username1: password1".
El tipo JWT requiere que otro servidor (SSO / Identity) firme un token que contiene notificaciones.
Ambos servidores comparten un secreto para que puedan firmar o verificar que la firma es válida.
Las reclamaciones se almacenan en la variable $_SESSION['claims']
.
Debe enviar un encabezado "Autorización X" que contenga un encabezado, cuerpo y token de token con codificación url base64, cuerpo y firma después de la palabra "Bearer" (lea más acerca de JWT aquí).
El estándar dice que necesita usar el encabezado "Authorization", pero esto es problemático en Apache y PHP.
X-Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6IjE1MzgyMDc2MDUiLCJleHAiOjE1MzgyMDc2MzV9.Z5px_GT15TRKhJCTHhDt5Z6K6LRDSFnLj8U5ok9l7gw
Este ejemplo envía los reclamos firmados:
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": "1538207605",
"exp": 1538207635
}
NOTA: la implementación de JWT solo admite los algoritmos basados en RSA y HMAC.
Primero necesitas crear una cuenta en Auth0.
Una vez que haya iniciado sesión, debe crear una aplicación (su tipo no importa).
Recoger el Domain
y el Client ID
IDy mantenerlos para un uso posterior. Luego, cree una API: asígnele un nombre y complete el identifiercampo con la URL de su punto final de API.
Luego tienes que configurar la jwtAuth.secret
onfiguración en tu archivo api.php
.
DNo lo llene con secret
lo que encontrará en la configuración de la aplicación Auth0, sino con un certificado público.
Para encontrarlo, vaya a la configuración de su aplicación, luego en "Configuración adicional".
Ahora encontrará una pestaña "Certificados" donde encontrará su clave pública en el campo Certificado de firma.
Asegúrese de llenar estas tres variables:
authUrl
con su dominio Auth0clientId
con su ID de clienteaudience
con la URL de la API que creó en Auth0
También puede cambiar la url
variable, utilizada para probar la API con autenticación.
Primero debe crear un proyecto de Firebase en la Consola Firebase. Agregue una aplicación web a este proyecto y capture el fragmento de código para su uso posterior.
Luego tienes que configurar la jwtAuth.secret
configuración en tu archivo api.php
.
Agarra la clave pública a través de esta URL.
Puede haber varios certificados, solo toma el correspondiente a tu kid
(si no sabes qué es, solo pruébalos todos hasta que ingreses).
Ahora, simplemente llene jwtAuth.secretcon su clave pública.
También puede cambiar la urlvariable, utilizada para probar la API con autenticación.
El modelo de Autorización actúa sobre "operations". Los más importantes se enumeran aquí:
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
El "/openapi
" punto final solo mostrará lo que está permitido en su sesión.
También tiene una operación especial de "documento" que le permite ocultar tablas y columnas de la documentación.
Para los puntos finales que comienzan con "/columns
" están las operaciones "reflect" y "remodel".
Estas operaciones pueden mostrar o cambiar la definición de la base de datos, tabla o columna.
Esta funcionalidad está desactivada por defecto y por una buena razón (¡cuidado!).
Agregue las "columns" al controlador en la configuración para habilitar esta funcionalidad.
Por defecto, todas las tablas y columnas son accesibles. Si desea restringir el acceso a algunas tablas, puede agregar el middleware de "authorization" y definir una función de '"authorization.tableHandler"' "autorización.tabla de lectura" que devuelva '"false"' para estas tablas.
'authorization.tableHandler' => function ($operation, $tableName) {
return $tableName != 'license_keys';
},
El ejemplo anterior restringirá el acceso a la tabla 'license_keys' para todas las operaciones.
'authorization.columnHandler' => function ($operation, $tableName, $columnName) {
return !($tableName == 'users' && $columnName == 'password');
},
El ejemplo anterior restringirá el acceso al campo 'contraseña' de la tabla 'usuarios' para todas las operaciones.
'authorization.recordHandler' => function ($operation, $tableName) {
return ($tableName == 'users') ? 'filter=username,neq,admin' : '';
},
El ejemplo anterior no permitirá el acceso a los registros de usuario donde el nombre de usuario es "admin". Esta construcción agrega un filtro a cada consulta ejecutada.
NOTA: debe manejar la creación de registros no válidos con un controlador de validación (o saneamiento).
Por defecto, todas las entradas son aceptadas y enviadas a la base de datos. Si desea eliminar (ciertas) etiquetas HTML antes de almacenarlas, puede agregar el middleware 'sanitation' y definir una función 'sanitation.handler' que devuelva el valor ajustado.
'sanitation.handler' => function ($operation, $tableName, $column, $value) {
return is_string($value) ? strip_tags($value) : $value;
},
El ejemplo anterior eliminará todas las etiquetas HTML de las cadenas en la entrada.
Por defecto se acepta toda la entrada. Si desea validar la entrada, puede agregar el middleware 'validation' y definir una función 'validation.handler' que devuelva un valor booleano que indique si el valor es válido o no.
'validation.handler' => function ($operation, $tableName, $column, $value, $context) {
return ($column['name'] == 'post_id' && !is_numeric($value)) ? 'must be numeric' : true;
},
Cuando editas un comentario con id 4 usando:
PUT /records/comments/4
Y envías como cuerpo:
{"post_id":"two"}
Luego, el servidor devolverá un código de estado HTTP '422' y un mensaje de error agradable:
{
"code": 1013,
"message": "Input validation failed for 'comments'",
"details": {
"post_id":"must be numeric"
}
}
Puede analizar esta salida para hacer que los campos de formulario aparezcan con un borde rojo y su mensaje de error correspondiente.
Puede usar el middleware "multiTenancy" cuando tenga una base de datos de múltiples inquilinos. Si sus inquilinos están identificados por la columna "customer_id" puede usar el siguiente controlador:
'multiTenancy.handler' => function ($operation, $tableName) {
return ['customer_id' => 12];
},
Esta construcción agrega un filtro que requiere que "customer_id" sea "12" a cada operación (excepto para "crear"). También establece la columna "customer_id" en "crear" a "12" y elimina la columna de cualquier otra operación de escritura.
Puede utilizar el middleware "joinLimits" y "pageLimits" para evitar el raspado de la base de datos. El middleware "joinLimits" limita la profundidad de la tabla, el número de tablas y el número de registros devueltos en una operación de unión. Si desea permitir 5 uniones directas directas con un máximo de 25 registros cada una, puede especificar:
'joinLimits.depth' => 1,
'joinLimits.tables' => 5,
'joinLimits.records' => 25,
El middleware "pageLimits" limita el número de página y los registros de números devueltos por una operación de lista. Si no desea permitir más de 10 páginas con un máximo de 25 registros cada una, puede especificar:
'pageLimits.pages' => 10,
'pageLimits.records' => 25,
NOTA: el número máximo de registros también se aplica cuando no hay un número de página especificado en la solicitud.
Puede utilizar el middleware de "personalización" para modificar la solicitud y respuesta e implementar cualquier otra funcionalidad.
'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);
},
El ejemplo anterior agregará un encabezado "X-Time-Taken" con el número de segundos que ha tomado la llamada a la API.
Las cargas de archivos se admiten a través de FileReader API, consulte el Ejemplo.
En el punto final "/openapi" se sirve la especificación de OpenAPI 3.0 (anteriormente llamada "Swagger"). Es una documentación instantánea legible por máquina de su API. Para obtener más información, echa un vistazo a estos enlaces:
- Swagger Editor se puede usar para ver y depurar la especificación generada.
- Especificación OpenAPI es un manual para crear una especificación OpenAPI.
- Swagger Petstore es una documentación de ejemplo que se genera utilizando OpenAPI.
Hay 4 motores de caché que pueden configurarse mediante el parámetro de configuración "cacheType":
- TempFile (por defecto)
- Redis
- Memcache
- Memcached
Puede instalar las dependencias de los últimos tres motores ejecutando:
sudo apt install php-redis redis
sudo apt install php-memcache memcached
sudo apt install php-memcached memcached
El motor predeterminado no tiene dependencias y usará archivos temporales en la ruta "temporal" del sistema.
Puede usar el parámetro de configuración "cachePath" para especificar la ruta del sistema de archivos para los archivos temporales o, en caso de que use un "cacheType" no predeterminado, el nombre de host (opcionalmente con puerto) del servidor de caché.
Estos son los tipos admitidos con su longitud / precisión / escala predeterminadas:
character types
- varchar(255)
- clob
boolean types:
- boolean
integer types:
- integer
- bigint
floating point types:
- float
- double
decimal types:
- decimal(19,4)
date/time types:
- date
- time
- timestamp
binary types:
- varbinary(255)
- blob
other types:
- geometry / * tipo no jdbc, extensión con soporte limitado * /
JavaScript no es compatible con enteros de 64 bits. Todos los números se almacenan como valores de punto flotante de 64 bits. La mantisa de un número de punto flotante de 64 bits es solo de 53 bits y es por eso que todos los números enteros mayores a 53 bits pueden causar problemas en JavaScript.
Los siguientes errores pueden ser reportados:
Error | Código de respuesta HTTP | Mensaje (original) | Mensaje (Español) |
---|---|---|---|
1000 | 404 Not found | Route not found | Ruta no encontrada |
1001 | 404 Not found | Table not found | Tabla no encontrada |
1002 | 422 Unprocessable entity | Argument count mismatch | Discrepancia en el recuento de argumentos |
1003 | 404 Not found | Record not found | Registro no encontrado |
1004 | 403 Forbidden | Origin is forbidden | El origen esta prohibido |
1005 | 404 Not found | Column not found | Columna no encontrada |
1006 | 409 Conflict | Table already exists | La tabla ya existe |
1007 | 409 Conflict | Column already exists | La columna ya existe |
1008 | 422 Unprocessable entity | Cannot read HTTP message | No se puede leer el mensaje HTTP |
1009 | 409 Conflict | Duplicate key exception | Excepción de clave duplicada |
1010 | 409 Conflict | Data integrity violation | Violación de integridad de datos |
1011 | 401 Unauthorized | Authentication required | Autenticacion requerida |
1012 | 403 Forbidden | Authentication failed | Autenticación fallida |
1013 | 422 Unprocessable entity | Input validation failed | La validación de entrada falló |
1014 | 403 Forbidden | Operation forbidden | Operación prohibida |
1015 | 405 Method not allowed | Operation not supported | Operación no soportada |
1016 | 403 Forbidden | Temporary or permanently blocked | Temporal o permanentemente bloqueado |
1017 | 403 Forbidden | Bad or missing XSRF token | Token XSRF malo o faltante |
1018 | 403 Forbidden | Only AJAX requests allowed | Solo se permiten peticiones AJAX |
1019 | 403 Forbidden | Pagination Forbidden | Paginación Prohibida |
9999 | 500 Internal server error | Unknown error | Error desconocido |
Se utiliza la siguiente estructura JSON:
{
"code":1002,
"message":"Argument count mismatch in '1'"
}
NOTA: Cualquier respuesta que no sea de error tendrá un estado: 200 OK
Estoy probando principalmente en Ubuntu y tengo las siguientes configuraciones de prueba:
- (Docker) Debian 9 con PHP 7.0, MariaDB 10.1, PostgreSQL 9.6 (PostGIS 2.3)
- (Docker) Ubuntu 16.04 con PHP 7.0, MariaDB 10.0, PostgreSQL 9.5 (PostGIS 2.2) y SQL Server 2017
- (Docker) Ubuntu 18.04 con PHP 7.2, MySQL 5.7, PostgreSQL 10.4 (PostGIS 2.4)
Esto no cubre todos los entornos (todavía), así que notifíqueme sobre las pruebas fallidas e informe su entorno. ,,Intentaré cubrir las configuraciones más relevantes en la carpeta "docker" del proyecto.
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;
}
}
Instale la ventana acoplable utilizando los siguientes comandos y luego cierre la sesión e inicie sesión para que los cambios surtan efecto:
sudo apt install docker.io
sudo usermod -aG docker ${USER}
Para ejecutar las pruebas de la ventana acoplable, ejecute "build_all.sh" y "run_all.sh" desde el directorio de la ventana acoplable. La salida debe ser:
================================================
Debian 9 (PHP 7.0)
================================================
[1/4] Starting MariaDB 10.1 ..... done
[2/4] Starting PostgreSQL 9.6 ... done
[3/4] Starting SQLServer 2017 ... skipped
[4/4] Cloning PHP-CRUD-API v2 ... skipped
------------------------------------------------
mysql: 95 tests ran in 2651 ms, 0 failed
pgsql: 95 tests ran in 573 ms, 0 failed
sqlsrv: skipped, driver not loaded
================================================
Ubuntu 16.04 (PHP 7.0)
================================================
[1/4] Starting MariaDB 10.0 ..... done
[2/4] Starting PostgreSQL 9.5 ... done
[3/4] Starting SQLServer 2017 ... done
[4/4] Cloning PHP-CRUD-API v2 ... skipped
------------------------------------------------
mysql: 95 tests ran in 2670 ms, 0 failed
pgsql: 95 tests ran in 550 ms, 0 failed
sqlsrv: 95 tests ran in 6624 ms, 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: 95 tests ran in 3186 ms, 0 failed
pgsql: 95 tests ran in 556 ms, 0 failed
sqlsrv: skipped, driver not loaded
La ejecución de prueba anterior (incluido el inicio de las bases de datos) toma menos de un minuto en mi máquina.
$ ./run.sh
1) debian9
2) ubuntu16
3) ubuntu18
> 3
================================================
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: 95 tests ran in 3186 ms, 0 failed
pgsql: 95 tests ran in 556 ms, 0 failed
sqlsrv: skipped, driver not loaded
root@b7ab9472e08f:/php-crud-api#
Como puede ver, la secuencia de comandos "run.sh" le da acceso a un aviso en un entorno de docker elegido. En este entorno se montan los archivos locales. Esto permite una fácil depuración en diferentes entornos. Puede escribir "exit" cuando haya terminado.