您的位置 首页 php

如何使用PHP,Slim和MySQL创建REST API 第二部分

前一部分如何使用PHP,Slim和MySQL创建REST API,我们已经学习了有关REST API的基本概念,并通过安装所需的工具来准备好您的开发环境。我希望每个人都对REST和其他技术领域有很好的了解。另外我假设你已经安装了所有必需的工具。

今天我们将学习如何设置PHP项目并编写REST API的实际代码。我们还将学习编写必要的SQL查询来执行数据库CRUD操作。

8.启动PHP项目

众所周知,IDE使开发过程更容易。因此,我建议您使用IDE来开发PHP项目,而不是使用普通的记事本。你可以去Eclipse,Aptana Studio,PhpStorm或Netbeans。PHP项目目录结构

下图将让您了解我们现在要开发的项目的目录结构。

libs – 所有第三方库都在这里。在我们的例子中,我们将Slim库放在这里

include – 我们构建的所有助手类都放在这里

index.php – 负责所有API请求

.htaccess – url结构规则和其他apache规则

现在让我们开始PHP项目

1)转到安装 wamp 的目录。通常,wamp将安装在C:\ wamp中。(如果您安装了任何其他软件而不是WAMP,则应该转到该软件推荐的目录)。

2)作为第一步,我们开始创建所需的目录。在wamp文件夹中,转到www文件夹(c:\ wamp \ www \)并创建一个名为task_manager的文件夹。该文件夹将是我们项目的父目录。在task_manager内部再创建两个名为libs,include和v1的文件夹。

3)现在,粘贴Slim库里面的库文件夹。Slim的下载链接在前一部分中提供。

4)通常,当index.php包含在url中时,Slim框架会起作用,这会使url格式不正确。因此,使用.htacess规则,我们可以从url中删除index.php并制作一些友好的URL。在v1文件夹中创建一个名为.htaccess的文件并粘贴以下代码。(请注意,此文件名不应包含名称中的任何其他扩展名,例如.txt)

9准备助手类

首先,我们开始编写此项目中所需的一组辅助类。这些辅助类提供了与数据库交互所需的必要功能。

1)内部包含文件夹创建名为Config.php的文件,其中包含以下内容。此文件包含整个项目配置,如数据库连接参数和其他变量。

config.php文件

<?php

/**

* Database configuration

*/

define (‘DB_USERNAME’, ‘root’);

define(‘DB_PASSWORD’, ”);

define(‘DB_HOST’, ‘localhost’);

define(‘DB_NAME’, ‘task_manager’);

define(‘USER_CREATED_SUCCESSFULLY’, 0);

define(‘USER_CREATE_FAILED’, 1);

define(‘USER_ALREADY_EXISTED’, 2);

?>

2)创建另一个名为 DbConnect.php的 类此类文件主要负责数据库连接。

DbConnect.php

<?php

/**

* Handling database connection

*

* @author Ravi Tamada

*/

class DbConnect {

private $conn;

function __construct() {

}

/**

* Establishing database connection

* @return database connection handler

*/

function connect() {

include_once dirname(__FILE__) . ‘./Config.php’;

// Connecting to mysql database

$this->conn = new mysqli(DB_HOST, DB_USERNAME, DB_PASSWORD, DB_NAME);

// Check for database connection error

if (mysqli_connect_errno()) {

echo “Failed to connect to MySQL: ” . mysqli_connect_error();

}

// returing connection resource

return $this->conn;

}

}

?>

3)加密密码

保护用户密码的最佳方法是不将它们存储为纯文本,而是在存储到db之前对所有密码进行加密。以下类负责加密用户密码。创建另一个名为PassHash.php的文件并粘贴以下代码。

PassHash.php

<?php

class PassHash {

// blowfish

private static $algo = ‘$2a’;

// cost parameter

private static $cost = ‘$10’;

// mainly for internal use

public static function unique_salt() {

return substr(sha1(mt_rand()), 0, 22);

}

// this will be used to generate a hash

public static function hash($password) {

return crypt($password, self::$algo .

self::$cost .

‘$’ . self::unique_salt());

}

// this will be used to compare a password against a hash

public static function check_password($hash, $password) {

$full_salt = substr($hash, 0, 29);

$new_hash = crypt($password, $full_salt);

return ($hash == $new_hash);

}

}

?>

4)现在创建另一个名为DbHandler.php的类。该类是我们项目中的重要文件之一,它提供了对数据库执行CRUD操作所必需的功能。每个函数都由它的名称和注释自我解释,我不必解释它们。

DbHandler.php

<?php

/**

* Class to handle all db operations

* This class will have CRUD methods for database tables

*

* @author Ravi Tamada

*/

class DbHandler {

private $conn;

function __construct() {

require_once dirname(__FILE__) . ‘./DbConnect.php’;

// opening db connection

$db = new DbConnect();

$this->conn = $db->connect();

}

/* ————- `users` table method —————— */

/**

* Creating new user

* @param String $name User full name

* @param String $email User login email id

* @param String $password User login password

*/

public function createUser($name, $email, $password) {

require_once ‘PassHash.php’;

$response = array();

// First check if user already existed in db

if (!$this->isUserExists($email)) {

// Generating password hash

$password_hash = PassHash::hash($password);

// Generating API key

$api_key = $this->generateApiKey();

// insert query

$stmt = $this->conn->prepare(“INSERT INTO users(name, email, password_hash, api_key, status) values(?, ?, ?, ?, 1)”);

$stmt->bind_param(“ssss”, $name, $email, $password_hash, $api_key);

$result = $stmt->execute();

$stmt-> close ();

// Check for successful insertion

if ($result) {

// User successfully inserted

return USER_CREATED_SUCCESSFULLY;

} else {

// Failed to create user

return USER_CREATE_FAILED;

}

} else {

// User with same email already existed in the db

return USER_ALREADY_EXISTED;

}

return $response;

}

/**

* Checking user login

* @param String $email User login email id

* @param String $password User login password

* @return boolean User login status success/fail

*/

public function checkLogin($email, $password) {

// fetching user by email

$stmt = $this->conn->prepare(“SELECT password_hash FROM users WHERE email = ?”);

$stmt->bind_param(“s”, $email);

$stmt->execute();

$stmt->bind_result($password_hash);

$stmt->store_result();

if ($stmt->num_rows > 0) {

// Found user with the email

// Now verify the password

$stmt->fetch();

$stmt->close();

if (PassHash::check_password($password_hash, $password)) {

// User password is correct

return TRUE;

} else {

// user password is incorrect

return FALSE;

}

} else {

$stmt->close();

// user not existed with the email

return FALSE;

}

}

/**

* Checking for duplicate user by email address

* @param String $email email to check in db

* @return boolean

*/

private function isUserExists($email) {

$stmt = $this->conn->prepare(“SELECT id from users WHERE email = ?”);

$stmt->bind_param(“s”, $email);

$stmt->execute();

$stmt->store_result();

$num_rows = $stmt->num_rows;

$stmt->close();

return $num_rows > 0;

}

/**

* Fetching user by email

* @param String $email User email id

*/

public function getUserByEmail($email) {

$stmt = $this->conn->prepare(“SELECT name, email, api_key, status, created_at FROM users WHERE email = ?”);

$stmt->bind_param(“s”, $email);

if ($stmt->execute()) {

$user = $stmt->get_result()->fetch_assoc();

$stmt->close();

return $user;

} else {

return NULL;

}

}

/**

* Fetching user api key

* @param String $user_id user id primary key in user table

*/

public function getApiKeyById($user_id) {

$stmt = $this->conn->prepare(“SELECT api_key FROM users WHERE id = ?”);

$stmt->bind_param(“i”, $user_id);

if ($stmt->execute()) {

$api_key = $stmt->get_result()->fetch_assoc();

$stmt->close();

return $api_key;

} else {

return NULL;

}

}

/**

* Fetching user id by api key

* @param String $api_key user api key

*/

public function getUserId($api_key) {

$stmt = $this->conn->prepare(“SELECT id FROM users WHERE api _key = ?”);

$stmt->bind_param(“s”, $api_key);

if ($stmt->execute()) {

$user_id = $stmt->get_result()->fetch_assoc();

$stmt->close();

return $user_id;

} else {

return NULL;

}

}

/**

* Validating user api key

* If the api key is there in db, it is a valid key

* @param String $api_key user api key

* @return boolean

*/

public function isValidApiKey($api_key) {

$stmt = $this->conn->prepare(“SELECT id from users WHERE api_key = ?”);

$stmt->bind_param(“s”, $api_key);

$stmt->execute();

$stmt->store_result();

$num_rows = $stmt->num_rows;

$stmt->close();

return $num_rows > 0;

}

/**

* Generating random Unique MD5 String for user Api key

*/

private function generateApiKey() {

return md5(uniqid(rand(), true));

}

/* ————- `tasks` table method —————— */

/**

* Creating new task

* @param String $user_id user id to whom task belongs to

* @param String $task task text

*/

public function createTask($user_id, $task) {

$stmt = $this->conn->prepare(“INSERT INTO tasks(task) VALUES(?)”);

$stmt->bind_param(“s”, $task);

$result = $stmt->execute();

$stmt->close();

if ($result) {

// task row created

// now assign the task to user

$new_task_id = $this->conn->insert_id;

$res = $this->createUserTask($user_id, $new_task_id);

if ($res) {

// task created successfully

return $new_task_id;

} else {

// task failed to create

return NULL;

}

} else {

// task failed to create

return NULL;

}

}

/**

* Fetching single task

* @param String $task_id id of the task

*/

public function getTask($task_id, $user_id) {

$stmt = $this->conn->prepare(“SELECT t.id, t.task, t.status, t.created_at from tasks t, user_tasks ut WHERE t.id = ? AND ut.task_id = t.id AND ut.user_id = ?”);

$stmt->bind_param(“ii”, $task_id, $user_id);

if ($stmt->execute()) {

$task = $stmt->get_result()->fetch_assoc();

$stmt->close();

return $task;

} else {

return NULL;

}

}

/**

* Fetching all user tasks

* @param String $user_id id of the user

*/

public function getAllUserTasks($user_id) {

$stmt = $this->conn->prepare(“SELECT t.* FROM tasks t, user_tasks ut WHERE t.id = ut.task_id AND ut.user_id = ?”);

$stmt->bind_param(“i”, $user_id);

$stmt->execute();

$tasks = $stmt->get_result();

$stmt->close();

return $tasks;

}

/**

* Updating task

* @param String $task_id id of the task

* @param String $task task text

* @param String $status task status

*/

public function updateTask($user_id, $task_id, $task, $status) {

$stmt = $this->conn->prepare(“UPDATE tasks t, user_tasks ut set t.task = ?, t.status = ? WHERE t.id = ? AND t.id = ut.task_id AND ut.user_id = ?”);

$stmt->bind_param(“siii”, $task, $status, $task_id, $user_id);

$stmt->execute();

$num_affected_rows = $stmt->affected_rows;

$stmt->close();

return $num_affected_rows > 0;

}

/**

* Deleting a task

* @param String $task_id id of the task to delete

*/

public function deleteTask($user_id, $task_id) {

$stmt = $this->conn->prepare(“DELETE t FROM tasks t, user_tasks ut WHERE t.id = ? AND ut.task_id = t.id AND ut.user_id = ?”);

$stmt->bind_param(“ii”, $task_id, $user_id);

$stmt->execute();

$num_affected_rows = $stmt->affected_rows;

$stmt->close();

return $num_affected_rows > 0;

}

/* ————- `user_tasks` table method —————— */

/**

* Function to assign a task to user

* @param String $user_id id of the user

* @param String $task_id id of the task

*/

public function createUserTask($user_id, $task_id) {

$stmt = $this->conn->prepare(“INSERT INTO user_tasks(user_id, task_id) values(?, ?)”);

$stmt->bind_param(“ii”, $user_id, $task_id);

$result = $stmt->execute();

$stmt->close();

return $result;

}

}

?>

10.处理API调用

现在我们拥有REST API所需的所有类。现在我们可以启动代码来处理所有单独的api调用。

在v1文件夹中创建一个名为index.php的文件并添加以下代码。这里我们包括必需的库和其他辅助函数。

verifyRequiredParams() – 此函数验证请求中的必需参数。

validateEmail() – 验证电子邮件地址是否有效。

echoRespnse() – 此函数将使用状态代码回显 json 响应。

<?php

require_once ‘../include/DbHandler.php’;

require_once ‘../include/PassHash.php’;

require ‘.././libs/Slim/Slim/Slim.php’;

\Slim\Slim::registerAutoloader();

$app = new \Slim\Slim();

// User id from db – Global Variable

$user_id = NULL;

/**

* Verifying required params posted or not

*/

function verifyRequiredParams($required_fields) {

$error = false;

$error_fields = “”;

$request_params = array();

$request_params = $_REQUEST;

// Handling PUT request params

if ($_SERVER[‘REQUEST_METHOD’] == ‘PUT’) {

$app = \Slim\Slim:: getInstance ();

parse_str($app->request()->getBody(), $request_params);

}

foreach ($required_fields as $field) {

if (!isset($request_params[$field]) || strlen(trim($request_params[$field])) <= 0) {

$error = true;

$error_fields .= $field . ‘, ‘;

}

}

if ($error) {

// Required field(s) are missing or empty

// echo error json and stop the app

$response = array();

$app = \Slim\Slim::getInstance();

$response[“error”] = true;

$response[” message “] = ‘Required field(s) ‘ . substr($error_fields, 0, -2) . ‘ is missing or empty’;

echoRespnse(400, $response);

$app->stop();

}

}

/**

* Validating email address

*/

function validateEmail($email) {

$app = \Slim\Slim::getInstance();

if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {

$response[“error”] = true;

$response[“message”] = ‘Email address is not valid’;

echoRespnse(400, $response);

$app->stop();

}

}

/**

* Echoing json response to client

* @param String $status_code Http response code

* @param Int $response Json response

*/

function echoRespnse($status_code, $response) {

$app = \Slim\Slim::getInstance();

// Http response code

$app->status($status_code);

// setting response content type to json

$app->contentType(‘application/json’);

echo json_encode($response);

}

$app->run();

?>

11.用户注册

为了与API交互,用户必须首先在我们的系统中注册。一旦他注册了API密钥,就会生成并存储在数据库中。此API密钥仅对该用户是私有的。

在index.php中添加以下代码。此功能处理用户注册。

在下表中,您可以找到有关URL,HTTP方法和需要发布的参数的API请求信息。

URL /register

Method POST

Params name, email, password

成功注册后,将发布以下json响应。

{

“error”: false,

“message”: “You are successfully registered”

}

结果:

如果请求缺少必需参数,则将发出以下json。

{

“error”: true,

“message”: “Required field(s) email, password is missing or empty”

}

结果:

12.用户登录

添加以下代码以处理用户登录。验证用户凭据后,将在json响应中发出该用户的API密钥。在所有剩余的api调用中,api密钥应包含在请求头中。

index.php

/**

* User Login

* url – /login

* method – POST

* params – email, password

*/

$app->post(‘/login’, function() use ($app) {

// check for required params

verifyRequiredParams(array(’email’, ‘password’));

// reading post params

$email = $app->request()->post(’email’);

$password = $app->request()->post(‘password’);

$response = array();

$db = new DbHandler();

// check for correct email and password

if ($db->checkLogin($email, $password)) {

// get the user by email

$user = $db->getUserByEmail($email);

if ($user != NULL) {

$response[“error”] = false;

$response[‘name’] = $user[‘name’];

$response[’email’] = $user[’email’];

$response[‘apiKey’] = $user[‘api_key’];

$response[‘createdAt’] = $user[‘created_at’];

} else {

// unknown error occurred

$response[‘error’] = true;

$response[‘message’] = “An error occurred. Please try again”;

}

} else {

// user credentials are wrong

$response[‘error’] = true;

$response[‘message’] = ‘Login failed. Incorrect credentials’;

}

echoRespnse(200, $response);

});

URL /login

Method POST

Params email, password

成功登录后,将发布以下json。

{

“error”: false,

“name”: “zhangsa”,

“email”: “123@qq.com”,

“apiKey”: “22d575d8c31a11b070c5eb519bbd1d3b”,

“createdAt”: “2019-04-07 16:38:28”

}

结果:

如果凭据错误,您可以期待以下json。

{

“error”: true,

“message”: “Login failed. Incorrect credentials”

}

13.验证API密钥

在处理任务数据时,我们需要通过阅读授权字段来使用请求标头中的API密钥来识别用户。基本上我们将查看匹配的API密钥的数据库并获取适当的用户。如果用户表中没有API密钥,那么我们将停止执行并回显错误json。

在index.php中添加以下方法。每次在对数据库执行任何相关操作之前,都会执行authenticate()方法。

index.php

/**

* Adding Middle Layer to authenticate every request

* Checking if the request has valid api key in the ‘Authorization’ header

*/

function authenticate(\Slim\Route $route) {

// Getting request headers

$headers = apache_request_headers();

$response = array();

$app = \Slim\Slim::getInstance();

// Verifying Authorization Header

if (isset($headers[‘Authorization’])) {

$db = new DbHandler();

// get the api key

$api_key = $headers[‘Authorization’];

// validating api key

if (!$db->isValidApiKey($api_key)) {

// api key is not present in users table

$response[“error”] = true;

$response[“message”] = “Access Denied. Invalid Api key”;

echoRespnse(401, $response);

$app->stop();

} else {

global $user_id;

// get user primary key id

$user = $db->getUserId($api_key);

if ($user != NULL)

$user_id = $user[“id”];

}

} else {

// api key is missing in header

$response[“error”] = true;

$response[“message”] = “Api key is misssing”;

echoRespnse(400, $response);

$app->stop();

}

}

如果请求标头中缺少api密钥,则将使用400状态代码回显以下json。

{

“error”: true,

“message”: “Api key is misssing”

}

如果api密钥无效,则json将回显401状态代码。

{

“error”: true,

“message”: “Access Denied. Invalid Api key”

}

Api通过身份验证调用(在请求中包含API密钥)

以下是API调用应在请求标头中具有Api密钥。这些api调用主要处理用户的任务数据,如创建,读取,更新和删除。

文章来源:智云一二三科技

文章标题:如何使用PHP,Slim和MySQL创建REST API 第二部分

文章地址:https://www.zhihuclub.com/77761.shtml

关于作者: 智云科技

热门文章

网站地图