[PHP, Symfony, API] REST API в Symfony (без FosRestBundle) с использованием JWT аутентификации. Часть 1 (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Перевод статьи подготовлен в преддверии старта курса «Symfony Framework».
В первой части статьи мы рассмотрим самый простой способ реализации REST API в проекте Symfony без использования FosRestBundle. Во второй части, которую я опубликую следом, мы рассмотрим JWT аутентификацию. Прежде чем мы начнем, сперва мы должны понять, что на самом деле означает REST.
Что означает Rest?
REST (Representational State Transfer — передача состояния представления) — это архитектурный стиль разработки веб-сервисов, который невозможно игнорировать, потому что в современной экосистеме существует большая потребность в создании Restful-приложений. Это может быть связано с уверенным подъемом позиций JavaScript и связанных фреймворков.
REST API использует протокол HTTP. Это означает, что когда клиент делает какой-либо запрос к такому веб-сервису, он может использовать любой из стандартных HTTP-глаголов: GET, POST, DELETE и PUT. Ниже описано, что произойдет, если клиент укажет соответствующий глагол.
- GET: будет использоваться для получения списка ресурсов или сведений о них.
- POST: будет использоваться для создания нового ресурса.
- PUT: будет использоваться для обновления существующего ресурса.
- DELETE: будет использоваться для удаления существующего ресурса.
REST не имеет состояний (state), и это означает, что на стороне сервера тоже нет никаких состояний запроса. Состояния остаются на стороне клиента (пример — использование JWT для аутентификации, с помощью которого мы собираемся засекьюрить наш REST API). Таким образом, при использовании аутентификации в REST API нам нужно отправить аутентификационный заголовок, чтобы получить правильный ответ без хранения состояния.
Создание проекта Symfony:
Во-первых, мы предполагаем, что вы уже установили PHP и менеджер пакетов Сomposer для создания нового проекта Symfony. С этим всем в наличии создайте новый проект с помощью следующей команды в терминале:
composer create-project symfony/skeleton demo_rest_api
Создание проекта Symfony
Мы используем базовый скелет Symfony, который рекомендуется для микросервисов и API. Вот как выглядит структура каталогов:
Структура проекта
Config: содержит все настройки бандла и список бандлов в bundle.php.
Public: предоставляет доступ к приложению через index.php.
Src: содержит все контроллеры, модели и сервисы
Var: содержит системные логи и файлы кэша.
Vendor: содержит все внешние пакеты.
Теперь давайте установим некоторые необходимые пакеты с помощью Сomposer:
composer require symfony/orm-pack
composer require sensio/framework-extra-bundle
Мы установили sensio/framework-extra-bundle, который поможет нам упростить код, используя аннотации для определения наших маршрутов.
Нам также необходимо установить symphony/orm-pack для интеграции с Doctrine ORM, чтобы соединиться с базой данных. Ниже приведена конфигурация созданной мной базы данных, которая может быть задана в файле .env.
.env файл конфигурации
Теперь давайте создадим нашу первую сущность. Создайте новый файл с именем Post.php в папке src/Entity.
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Entity
* @ORM\Table(name="post")
* @ORM\HasLifecycleCallbacks()
*/
class Post implements \JsonSerializable {
/**
* @ORM\Column(type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\Column(type="string", length=100
*
*/
private $name;
/**
* @ORM\Column(type="text")
*/
private $description;
/**
* @ORM\Column(type="datetime")
*/
private $create_date;
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* @param mixed $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* @return mixed
*/
public function getName()
{
return $this->name;
}
/**
* @param mixed $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* @return mixed
*/
public function getDescription()
{
return $this->description;
}
/**
* @param mixed $description
*/
public function setDescription($description)
{
$this->description = $description;
}
/**
* @return mixed
*/
public function getCreateDate(): ?\DateTime
{
return $this->create_date;
}
/**
* @param \DateTime $create_date
* @return Post
*/
public function setCreateDate(\DateTime $create_date): self
{
$this->create_date = $create_date;
return $this;
}
/**
* @throws \Exception
* @ORM\PrePersist()
*/
public function beforeSave(){
$this->create_date = new \DateTime('now', new \DateTimeZone('Africa/Casablanca'));
}
/**
* Specify data which should be serialized to JSON
* @link https://php.net/manual/en/jsonserializable.jsonserialize.php
* @return mixed data which can be serialized by <b>json_encode</b>,
* which is a value of any type other than a resource.
* @since 5.4.0
*/
public function jsonSerialize()
{
return [
"name" => $this->getName(),
"description" => $this->getDescription()
];
}
}
И после этого выполните команду: php bin/console doctrine:schema:create для создания таблицы базы данных в соответствии с нашей сущностью Post.
Теперь давайте создадим PostController.php, куда мы добавим все методы, взаимодействующие с API. Он должен быть помещен в папку src/Controller.
<?php
/**
* Created by PhpStorm.
* User: hicham benkachoud
* Date: 02/01/2020
* Time: 22:44
*/
namespace App\Controller;
use App\Entity\Post;
use App\Repository\PostRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
/**
* Class PostController
* @package App\Controller
* @Route("/api", name="post_api")
*/
class PostController extends AbstractController
{
/**
* @param PostRepository $postRepository
* @return JsonResponse
* @Route("/posts", name="posts", methods={"GET"})
*/
public function getPosts(PostRepository $postRepository){
$data = $postRepository->findAll();
return $this->response($data);
}
/**
* @param Request $request
* @param EntityManagerInterface $entityManager
* @param PostRepository $postRepository
* @return JsonResponse
* @throws \Exception
* @Route("/posts", name="posts_add", methods={"POST"})
*/
public function addPost(Request $request, EntityManagerInterface $entityManager, PostRepository $postRepository){
try{
$request = $this->transformJsonBody($request);
if (!$request || !$request->get('name') || !$request->request->get('description')){
throw new \Exception();
}
$post = new Post();
$post->setName($request->get('name'));
$post->setDescription($request->get('description'));
$entityManager->persist($post);
$entityManager->flush();
$data = [
'status' => 200,
'success' => "Post added successfully",
];
return $this->response($data);
}catch (\Exception $e){
$data = [
'status' => 422,
'errors' => "Data no valid",
];
return $this->response($data, 422);
}
}
/**
* @param PostRepository $postRepository
* @param $id
* @return JsonResponse
* @Route("/posts/{id}", name="posts_get", methods={"GET"})
*/
public function getPost(PostRepository $postRepository, $id){
$post = $postRepository->find($id);
if (!$post){
$data = [
'status' => 404,
'errors' => "Post not found",
];
return $this->response($data, 404);
}
return $this->response($post);
}
/**
* @param Request $request
* @param EntityManagerInterface $entityManager
* @param PostRepository $postRepository
* @param $id
* @return JsonResponse
* @Route("/posts/{id}", name="posts_put", methods={"PUT"})
*/
public function updatePost(Request $request, EntityManagerInterface $entityManager, PostRepository $postRepository, $id){
try{
$post = $postRepository->find($id);
if (!$post){
$data = [
'status' => 404,
'errors' => "Post not found",
];
return $this->response($data, 404);
}
$request = $this->transformJsonBody($request);
if (!$request || !$request->get('name') || !$request->request->get('description')){
throw new \Exception();
}
$post->setName($request->get('name'));
$post->setDescription($request->get('description'));
$entityManager->flush();
$data = [
'status' => 200,
'errors' => "Post updated successfully",
];
return $this->response($data);
}catch (\Exception $e){
$data = [
'status' => 422,
'errors' => "Data no valid",
];
return $this->response($data, 422);
}
}
/**
* @param PostRepository $postRepository
* @param $id
* @return JsonResponse
* @Route("/posts/{id}", name="posts_delete", methods={"DELETE"})
*/
public function deletePost(EntityManagerInterface $entityManager, PostRepository $postRepository, $id){
$post = $postRepository->find($id);
if (!$post){
$data = [
'status' => 404,
'errors' => "Post not found",
];
return $this->response($data, 404);
}
$entityManager->remove($post);
$entityManager->flush();
$data = [
'status' => 200,
'errors' => "Post deleted successfully",
];
return $this->response($data);
}
/**
* Returns a JSON response
*
* @param array $data
* @param $status
* @param array $headers
* @return JsonResponse
*/
public function response($data, $status = 200, $headers = [])
{
return new JsonResponse($data, $status, $headers);
}
protected function transformJsonBody(\Symfony\Component\HttpFoundation\Request $request)
{
$data = json_decode($request->getContent(), true);
if ($data === null) {
return $request;
}
$request->request->replace($data);
return $request;
}
}
Здесь мы определили пять маршрутов:
● GET /api/posts: вернет список постов.
api получения всех постов
● POST /api/posts: создаст новый пост.
api добавления нового поста
● GET /api/posts/id: вернет пост, соответствующий определенному идентификатору,
получение конкретного поста
● PUT /api/posts/id: обновит пост.
обновление поста
Это результат после обновления:
пост после обновления
● DELETE /api/posts/id: удалит пост.
удаление поста
Это результат получения всех постов после удаления поста с идентификатором 3:
все посты после удаления
Исходный код можно найти здесь
Заключение
Итак, теперь мы понимаем, что такое REST и Restful. Restful API должен быть без состояний. Мы знаем, как создать Restful-приложение, используя HTTP-глаголы. В общем, теперь мы хорошо понимаем REST и готовы профессионально создавать Restful-приложения.
В следующей статье мы рассмотрим, как обеспечить секьюрность API с помощью JWT аутентификации.
Узнать подробнее о курсе «Symfony Framework»
===========
Источник:
habr.com
===========
===========
Автор оригинала: Hicham Ben Kachoud
===========Похожие новости:
- [Laravel, PHP, Разработка веб-сайтов] Laravel-Дайджест (29 июня – 5 июля 2020)
- [PHP] Code review, это просто
- [PHP, Программирование] Понимаем JIT в PHP 8 (перевод)
- [Тестирование IT-систем, Тестирование веб-сервисов, Тестирование игр, Тестирование мобильных приложений] Как выбрать мобильные девайсы для тестирования и не налажать
- [Финансы в IT] Разбор: почему власти США угрожают китайским компаниям насильным снятием их акций с биржевых торгов
- [Natural Language Processing, Open source, Голосовые интерфейсы, Хакатоны, Яндекс API] Сущности для платформы Яндекс.Диалоги
- В Firefox добавлено ускорение декодирования видео через VA-API для систем X11
- [1С-Битрикс, API, PHP, Разработка веб-сайтов] Ещё один велосипед: пишем свой автозагрузчик классов для Битрикс
- [ВКонтакте API, Программирование] Как реализовать свою идею и не сойти с ума на самоизоляции
- [Scala, Программирование] Применение ZIO ZLayer (перевод)
Теги для поиска: #_php, #_symfony, #_api, #_php, #_symfony_framework, #_rest_api, #_otus, #_fosrestbundle, #_jwt, #_blog_kompanii_otus._onlajnobrazovanie (
Блог компании OTUS. Онлайн-образование
), #_php, #_symfony, #_api
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 25-Ноя 20:38
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Перевод статьи подготовлен в преддверии старта курса «Symfony Framework». В первой части статьи мы рассмотрим самый простой способ реализации REST API в проекте Symfony без использования FosRestBundle. Во второй части, которую я опубликую следом, мы рассмотрим JWT аутентификацию. Прежде чем мы начнем, сперва мы должны понять, что на самом деле означает REST. Что означает Rest? REST (Representational State Transfer — передача состояния представления) — это архитектурный стиль разработки веб-сервисов, который невозможно игнорировать, потому что в современной экосистеме существует большая потребность в создании Restful-приложений. Это может быть связано с уверенным подъемом позиций JavaScript и связанных фреймворков. REST API использует протокол HTTP. Это означает, что когда клиент делает какой-либо запрос к такому веб-сервису, он может использовать любой из стандартных HTTP-глаголов: GET, POST, DELETE и PUT. Ниже описано, что произойдет, если клиент укажет соответствующий глагол.
REST не имеет состояний (state), и это означает, что на стороне сервера тоже нет никаких состояний запроса. Состояния остаются на стороне клиента (пример — использование JWT для аутентификации, с помощью которого мы собираемся засекьюрить наш REST API). Таким образом, при использовании аутентификации в REST API нам нужно отправить аутентификационный заголовок, чтобы получить правильный ответ без хранения состояния. Создание проекта Symfony: Во-первых, мы предполагаем, что вы уже установили PHP и менеджер пакетов Сomposer для создания нового проекта Symfony. С этим всем в наличии создайте новый проект с помощью следующей команды в терминале: composer create-project symfony/skeleton demo_rest_api
Создание проекта Symfony Мы используем базовый скелет Symfony, который рекомендуется для микросервисов и API. Вот как выглядит структура каталогов: Структура проекта Config: содержит все настройки бандла и список бандлов в bundle.php. Public: предоставляет доступ к приложению через index.php. Src: содержит все контроллеры, модели и сервисы Var: содержит системные логи и файлы кэша. Vendor: содержит все внешние пакеты. Теперь давайте установим некоторые необходимые пакеты с помощью Сomposer: composer require symfony/orm-pack
composer require sensio/framework-extra-bundle Мы установили sensio/framework-extra-bundle, который поможет нам упростить код, используя аннотации для определения наших маршрутов. Нам также необходимо установить symphony/orm-pack для интеграции с Doctrine ORM, чтобы соединиться с базой данных. Ниже приведена конфигурация созданной мной базы данных, которая может быть задана в файле .env. .env файл конфигурации Теперь давайте создадим нашу первую сущность. Создайте новый файл с именем Post.php в папке src/Entity. <?php
namespace App\Entity; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; /** * @ORM\Entity * @ORM\Table(name="post") * @ORM\HasLifecycleCallbacks() */ class Post implements \JsonSerializable { /** * @ORM\Column(type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @ORM\Column(type="string", length=100 * */ private $name; /** * @ORM\Column(type="text") */ private $description; /** * @ORM\Column(type="datetime") */ private $create_date; /** * @return mixed */ public function getId() { return $this->id; } /** * @param mixed $id */ public function setId($id) { $this->id = $id; } /** * @return mixed */ public function getName() { return $this->name; } /** * @param mixed $name */ public function setName($name) { $this->name = $name; } /** * @return mixed */ public function getDescription() { return $this->description; } /** * @param mixed $description */ public function setDescription($description) { $this->description = $description; } /** * @return mixed */ public function getCreateDate(): ?\DateTime { return $this->create_date; } /** * @param \DateTime $create_date * @return Post */ public function setCreateDate(\DateTime $create_date): self { $this->create_date = $create_date; return $this; } /** * @throws \Exception * @ORM\PrePersist() */ public function beforeSave(){ $this->create_date = new \DateTime('now', new \DateTimeZone('Africa/Casablanca')); } /** * Specify data which should be serialized to JSON * @link https://php.net/manual/en/jsonserializable.jsonserialize.php * @return mixed data which can be serialized by <b>json_encode</b>, * which is a value of any type other than a resource. * @since 5.4.0 */ public function jsonSerialize() { return [ "name" => $this->getName(), "description" => $this->getDescription() ]; } } И после этого выполните команду: php bin/console doctrine:schema:create для создания таблицы базы данных в соответствии с нашей сущностью Post. Теперь давайте создадим PostController.php, куда мы добавим все методы, взаимодействующие с API. Он должен быть помещен в папку src/Controller. <?php
/** * Created by PhpStorm. * User: hicham benkachoud * Date: 02/01/2020 * Time: 22:44 */ namespace App\Controller; use App\Entity\Post; use App\Repository\PostRepository; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; /** * Class PostController * @package App\Controller * @Route("/api", name="post_api") */ class PostController extends AbstractController { /** * @param PostRepository $postRepository * @return JsonResponse * @Route("/posts", name="posts", methods={"GET"}) */ public function getPosts(PostRepository $postRepository){ $data = $postRepository->findAll(); return $this->response($data); } /** * @param Request $request * @param EntityManagerInterface $entityManager * @param PostRepository $postRepository * @return JsonResponse * @throws \Exception * @Route("/posts", name="posts_add", methods={"POST"}) */ public function addPost(Request $request, EntityManagerInterface $entityManager, PostRepository $postRepository){ try{ $request = $this->transformJsonBody($request); if (!$request || !$request->get('name') || !$request->request->get('description')){ throw new \Exception(); } $post = new Post(); $post->setName($request->get('name')); $post->setDescription($request->get('description')); $entityManager->persist($post); $entityManager->flush(); $data = [ 'status' => 200, 'success' => "Post added successfully", ]; return $this->response($data); }catch (\Exception $e){ $data = [ 'status' => 422, 'errors' => "Data no valid", ]; return $this->response($data, 422); } } /** * @param PostRepository $postRepository * @param $id * @return JsonResponse * @Route("/posts/{id}", name="posts_get", methods={"GET"}) */ public function getPost(PostRepository $postRepository, $id){ $post = $postRepository->find($id); if (!$post){ $data = [ 'status' => 404, 'errors' => "Post not found", ]; return $this->response($data, 404); } return $this->response($post); } /** * @param Request $request * @param EntityManagerInterface $entityManager * @param PostRepository $postRepository * @param $id * @return JsonResponse * @Route("/posts/{id}", name="posts_put", methods={"PUT"}) */ public function updatePost(Request $request, EntityManagerInterface $entityManager, PostRepository $postRepository, $id){ try{ $post = $postRepository->find($id); if (!$post){ $data = [ 'status' => 404, 'errors' => "Post not found", ]; return $this->response($data, 404); } $request = $this->transformJsonBody($request); if (!$request || !$request->get('name') || !$request->request->get('description')){ throw new \Exception(); } $post->setName($request->get('name')); $post->setDescription($request->get('description')); $entityManager->flush(); $data = [ 'status' => 200, 'errors' => "Post updated successfully", ]; return $this->response($data); }catch (\Exception $e){ $data = [ 'status' => 422, 'errors' => "Data no valid", ]; return $this->response($data, 422); } } /** * @param PostRepository $postRepository * @param $id * @return JsonResponse * @Route("/posts/{id}", name="posts_delete", methods={"DELETE"}) */ public function deletePost(EntityManagerInterface $entityManager, PostRepository $postRepository, $id){ $post = $postRepository->find($id); if (!$post){ $data = [ 'status' => 404, 'errors' => "Post not found", ]; return $this->response($data, 404); } $entityManager->remove($post); $entityManager->flush(); $data = [ 'status' => 200, 'errors' => "Post deleted successfully", ]; return $this->response($data); } /** * Returns a JSON response * * @param array $data * @param $status * @param array $headers * @return JsonResponse */ public function response($data, $status = 200, $headers = []) { return new JsonResponse($data, $status, $headers); } protected function transformJsonBody(\Symfony\Component\HttpFoundation\Request $request) { $data = json_decode($request->getContent(), true); if ($data === null) { return $request; } $request->request->replace($data); return $request; } } Здесь мы определили пять маршрутов: ● GET /api/posts: вернет список постов. api получения всех постов ● POST /api/posts: создаст новый пост. api добавления нового поста ● GET /api/posts/id: вернет пост, соответствующий определенному идентификатору, получение конкретного поста ● PUT /api/posts/id: обновит пост. обновление поста Это результат после обновления: пост после обновления ● DELETE /api/posts/id: удалит пост. удаление поста Это результат получения всех постов после удаления поста с идентификатором 3: все посты после удаления Исходный код можно найти здесь Заключение Итак, теперь мы понимаем, что такое REST и Restful. Restful API должен быть без состояний. Мы знаем, как создать Restful-приложение, используя HTTP-глаголы. В общем, теперь мы хорошо понимаем REST и готовы профессионально создавать Restful-приложения. В следующей статье мы рассмотрим, как обеспечить секьюрность API с помощью JWT аутентификации. Узнать подробнее о курсе «Symfony Framework» =========== Источник: habr.com =========== =========== Автор оригинала: Hicham Ben Kachoud ===========Похожие новости:
Блог компании OTUS. Онлайн-образование ), #_php, #_symfony, #_api |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 25-Ноя 20:38
Часовой пояс: UTC + 5