Buscar..
Trabajar con RESTFul API
La Representación Estatal de Transferencia (REST) es un estilo arquitectónico utilizado para el desarrollo web, introducido y definido en 2000 por Roy Fielding.
Véalo en wiki: REST wiki
Se basa en el protocolo HTTP ( HTTP en Wiki ), solicitudes HTTP (GET, POST, PATCH, DELETE ...) / códigos de respuestas (404, 400, 200, 201, 500 ...) y estructura de cuerpos.
Esta es una excelente manera de exponer sus datos a otro sistema en Internet.
Imagine que desea crear una API RESTFul para administrar su StackOverFlower (usuario) en su base de datos local.
¡Hagamos el ejemplo!
Framework Symfony 2.8
- Servidor web :
Debes instalar y configurar un servidor web en tu máquina local, consulta Wamp o Lamp o Mamp : debes tener una versión reciente de PHP ( !!! requisitos de Symfony !!! )
- Php cli y compositor:
Debe configurar PHP cli (que varía en nuestro sistema), escriba este "Cómo hacer PHP cli [OS-NAME]" en nuestro amigo Google. Debes instalar compositor, ver instalar compositor
- Symfony:
Debes instalar Symfony 2.8 (con el compositor, es la mejor manera), abrir un terminal (o cmd en Windows) e ir a la ruta de tu servidor web.
Symfony 2 funciona con uno de los mejores tipos de estructura: paquetes. Todos son paquetes en Symfony! Podemos probarlo arriba.
cd /your-web-server-path/
composer create-project symfony/framework-standard-edition example "2.8.*"
Vaya a la estructura de árbol y vea: Symfony 2.8 está instalado en el directorio "ejemplo".
- FOSRest (para FriendsOfSymfony) en el paquete JMSSerializer:
Debes instalar estos dos paquetes:
JMSSerializer ( Instalar ):
composer require jms/serializer-bundle "~0.13"
FosRestBundle ( Instalar ):
composer require friendsofsymfony/rest-bundle
¡No olvides activarlos en AppKernel.php!
- Configuracion basica :
Haga su propio paquete "Ejemplo" y cree la base de datos.
cd /path/to/your/symfony/
php app/console generate:bundle
php app/console doctrine:generate:database
Ve a la parte inferior de tu archivo de configuración de la aplicación Symfony 2.8 y pégalo:
#app/config/config.yml
fos_rest:
format_listener:
rules:
- { path: '^/stackoverflower', priorities: ['xml', 'json'], fallback_format: xml, prefer_extension: true }
- { path: '^/', priorities: [ 'text/html', '*/*'], fallback_format: html, prefer_extension: true }
Haga su directorio de doctrina ("example / src / ExampleBundle / Entity") y el archivo de recursos ("StackOverFlower.orm.yml"):
# src/ExampleBundle/Resources/config/doctrine/StackOverFlower.orm.yml
ExampleBundle\Entity\StackOverFlower:
type: entity
table: stackoverflower
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
name:
type: string
length: 100
Generar entidad y actualizar esquema:
php app/console doctrine:generate:entity StackOverFlower
php app/console doctrine:schema:update --force
Hacer un controlador por defecto:
#src/ExampleBundle/Controller/StackOverFlowerController.php
namespace ExampleBundle\Controller;
use FOS\RestBundle\Controller\FOSRestController;
use Symfony\Component\HttpFoundation\Request;
use FOS\RestBundle\Controller\Annotations\Get;
use FOS\RestBundle\Controller\Annotations\Post;
use FOS\RestBundle\Controller\Annotations\Delete;
use ExampleBundle\Entity\StackOverFlower;
class StackOverFlowerController extends FOSRestController
{
/**
* findStackOverFlowerByRequest
*
* @param Request $request
* @return StackOverFlower
* @throws NotFoundException
*/
private function findStackOverFlowerByRequest(Request $request) {
$id = $request->get('id');
$user = $this->getDoctrine()->getManager()->getRepository("ExampleBundle:StackOverFlower")->findOneBy(array('id' => $id));
return $user;
}
/**
* validateAndPersistEntity
*
* @param StackOverFlower $user
* @param Boolean $delete
* @return View the view
*/
private function validateAndPersistEntity(StackOverFlower $user, $delete = false) {
$template = "ExampleBundle:StackOverFlower:example.html.twig";
$validator = $this->get('validator');
$errors_list = $validator->validate($user);
if (count($errors_list) == 0) {
$em = $this->getDoctrine()->getManager();
if ($delete === true) {
$em->remove($user);
} else {
$em->persist($user);
}
$em->flush();
$view = $this->view($user)
->setTemplateVar('user')
->setTemplate($template);
} else {
$errors = "";
foreach ($errors_list as $error) {
$errors .= (string) $error->getMessage();
}
$view = $this->view($errors)
->setTemplateVar('errors')
->setTemplate($template);
}
return $view;
}
/**
* newStackOverFlowerAction
*
* @Get("/stackoverflower/new/{name}")
*
* @param Request $request
* @return String
*/
public function newStackOverFlowerAction(Request $request)
{
$user = new StackOverFlower();
$user->setName($request->get('name'));
$view = $this->validateAndPersistEntity($user);
return $this->handleView($view);
}
/**
* editStackOverFlowerAction
*
* @Get("/stackoverflower/edit/{id}/{name}")
*
* @param Request $request
* @return type
*/
public function editStackOverFlowerAction(Request $request) {
$user = $this->findStackOverFlowerByRequest($request);
if (! $user) {
$view = $this->view("No StackOverFlower found for this id:". $request->get('id'), 404);
return $this->handleView($view);
}
$user->setName($request->get('name'));
$view = $this->validateAndPersistEntity($user);
return $this->handleView($view);
}
/**
* deleteStackOverFlowerAction
*
* @Get("/stackoverflower/delete/{id}")
*
* @param Request $request
* @return type
*/
public function deleteStackOverFlowerAction(Request $request) {
$user = $this->findStackOverFlowerByRequest($request);
if (! $user) {
$view = $this->view("No StackOverFlower found for this id:". $request->get('id'), 404);
return $this->handleView();
}
$view = $this->validateAndPersistEntity($user, true);
return $this->handleView($view);
}
/**
* getStackOverFlowerAction
*
* @Get("/stackoverflowers")
*
* @param Request $request
* @return type
*/
public function getStackOverFlowerAction(Request $request) {
$template = "ExampleBundle:StackOverFlower:example.html.twig";
$users = $this->getDoctrine()->getManager()->getRepository("ExampleBundle:StackOverFlower")->findAll();
if (count($users) === 0) {
$view = $this->view("No StackOverFlower found.", 404);
return $this->handleView();
}
$view = $this->view($users)
->setTemplateVar('users')
->setTemplate($template);
return $this->handleView($view);
}
}
Haga su vista predeterminada de Twig:
#src/ExampleBundle/Resources/views/StackOverFlower.html.twig
{% if errors is defined %}
{{ errors }}
{% else %}
{% if users is defined %}
{{ users | serialize }}
{% else %}
{{ user | serialize }}
{% endif %}
{% endif %}
¡Acabas de hacer tu primera API RESTFul!
Puede probarlo en: http: //su-servidor-nombre/tu-simfonía-ruta/app_dev.php/stackoverflower/new/test .
Como puede ver en la base de datos, se ha creado un nuevo usuario con el nombre "prueba".
Puede obtener la lista de stackoverflower en: http: //su-servidor-nombre/tu-symfony-path/app_dev.php/stackoverflowers
Tiene un ejemplo completo en mi cuenta de github de este ejemplo: ejemplo de Git Hub , en la rama "maestra" de este ejemplo, y en la rama de "rutas reales", un ejemplo con una URL más apropiada (como POST y DELETE).
¡Nos vemos luego para un ejemplo con SOAP!
Atentamente,
Mathieu
Trabajar con API SOAP
SOAP (Simple Access Object Protocol) está basado en XML, como XML-RPC, es ancestro , con un archivo llamado WSDL , que describe el método a exponer.
Este protocolo a menudo se basa en SOAP-Enveloppe , un SOAP-Body , y alternativamente SOAP-Header , los datos se envuelven en una estructura y se interpretan de la misma manera desde diferentes idiomas.
Para más información, ver: SOAP en wiki.
Como se describió anteriormente, lo más importante para describir su servicio web es el archivo WSDL , consulte: explicación WSDL en wiki
El trabajo básico será definir qué se expone en su API SOAP, su clase y su proceso de negocio serán manejados automáticamente por la clase básica de SOAPServer de PHP. ¡Todavía necesitas el código!
Veamos cómo se construye el archivo:
- Servicio: Establezca la API URI y lo que se asociará.
- Encuadernación: define las operaciones asociadas al servicio.
- Operaciones: algunos métodos que desea exponer a la web.
- PortTypes: definir consultas y respuestas
- Solicitudes y respuestas: lo que esperas entrada y salida
- Mensajes: qué formato espera (parámetros) en cada IO, pueden ser simples (cadena, entero, flotante ...) o tipo complejo (formato estructurado)
Con esta información básica, puede lograr todas las API que desee.
Imagina que quieres hacer una api de SOAP para administrar tu StackOverFlower (usuario) en tu base de datos local.
¡Hagamos el ejemplo!
Instala el servidor web, Php cli, Composer, Symfony 2.8, crea un nuevo paquete "ExampleBundle" y crea el esquema como se describe anteriormente.
Antes de comenzar a construir nuestra lógica de negocios, teníamos que saber qué exponer de nuestro controlador. Este trabajo se realiza mediante el uso de WSDL. Este es un ejemplo de una buena sintaxis de un WSDL:
<definitions name="StackOverFlowerService"
targetNamespace="http://example/soap/stackoverflower.wsdl"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://example/soap/stackoverflower.wsdl"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<message name="NewRequest">
<part name="name" type="xsd:string"/>
</message>
<message name="NewResponse">
<part name="status" type="xsd:string"/>
</message>
<message name="getListRequest"></message>
<message name="getListResponse">
<part name="list" type="xsd:string"/>
</message>
<message name="editRequest">
<part name="id" type="xsd:string"/>
<part name="name" type="xsd:string"/>
</message>
<message name="editResponse">
<part name="status" type="xsd:string"/>
</message>
<message name="deleteRequest">
<part name="id" type="xsd:string"/>
</message>
<message name="deleteResponse">
<part name="status" type="xsd:string"/>
</message>
<portType name="StackOverFlower_PortType">
<operation name="newStack">
<input message="tns:NewRequest"/>
<output message="tns:NewResponse"/>
</operation>
<operation name="getList">
<input message="tns:getListRequest"/>
<output message="tns:getListResponse"/>
</operation>
<operation name="edit">
<input message="tns:editRequest"/>
<output message="tns:editResponse"/>
</operation>
<operation name="delete">
<input message="tns:deleteRequest"/>
<output message="tns:deleteResponse"/>
</operation>
</portType>
<binding name="StackOverFlower_Binding" type="tns:StackOverFlower_PortType">
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="newStack">
<soap:operation soapAction="newStack"/>
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:example:new"
use="encoded"/>
</input>
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:example:new"
use="encoded"/>
</output>
</operation>
<operation name="getList">
<soap:operation soapAction="getList"/>
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:example:get-list"
use="encoded"/>
</input>
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:example:get-list"
use="encoded"/>
</output>
</operation>
<operation name="edit">
<soap:operation soapAction="edit"/>
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:example:edit"
use="encoded"/>
</input>
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:example:edit"
use="encoded"/>
</output>
</operation>
<operation name="delete">
<soap:operation soapAction="delete"/>
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:example:delete"
use="encoded"/>
</input>
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:example:delete"
use="encoded"/>
</output>
</operation>
</binding>
<service name="StackOverFlower_Service">
<documentation>Description File of StackOverFlowerService</documentation>
<port binding="tns:StackOverFlower_Binding" name="StackOverFlower_Port">
<soap:address
location="http://example/stackoverflower/" />
</port>
</service>
</definitions>
Debemos tomar esto en tu directorio web de Symfony (en el subdirectorio de SOAP y nombrarlo como "stackoverflower.wsdl").
Realmente inspirado en el ejemplo WSDl . Puede validar eso con un validador WSDl en línea
Después de esto, podemos crear nuestro controlador y servicio básico, inspirados en SOAP Symfony 2.8 Doc .
Servicio, que es manejado por PHP SOAPServer:
#src\ExampleBundle\Services\StackOverFlowerService.php
namespace ExampleBundle\Services;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use ExampleBundle\Entity\StackOverFlower;
class StackOverFlowerService
{
private $em;
private $stackoverflower;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function newStack($name)
{
$stackoverflower = new StackOverFlower();
$stackoverflower->setName($name);
$this->em->persist($stackoverflower);
$this->em->flush();
return "ok";
}
public function getList()
{
$stackoverflowers = $this->em->getRepository("ExampleBundle:StackOverFlower")->findAll();
$encoders = array(new XmlEncoder(), new JsonEncoder());
$normalizers = array(new ObjectNormalizer());
$serializer = new Serializer($normalizers, $encoders);
return $serializer->serialize($stackoverflowers, 'json');
}
public function edit($id, $name)
{
$stackoverflower = $this->em->getRepository("ExampleBundle:StackOverFlower")->findOneById($id);
$stackoverflower->setName($name);
$this->em->persist($stackoverflower);
$this->em->flush();
return "ok";
}
public function delete($id)
{
$stackoverflower = $this->em->getRepository("ExampleBundle:StackOverFlower")->findOneById($id);
$this->em->remove($stackoverflower);
$this->em->flush();
return "ok";
}
}
Configura este servicio:
#src\ExampleBundle\Resources\config\services.yml
services:
stackoverflower_service:
class: ExampleBundle\Services\StackOverFlowerService
arguments: [@doctrine.orm.entity_manager]
Como puede ver, inyectamos Doctrine Entity Manger como una dependencia porque tenemos que usar esto para el objeto StackOverFlower de CRUD.
Controlador, que expone el objeto de servicio:
#src\ExampleBundle\Controller\StackOverFlowerController.php
namespace ExampleBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class StackOverFlowerController extends Controller
{
public function indexAction()
{
ini_set("soap.wsdl_cache_enabled", "0");
$options = array(
'uri' => 'http://example/app_dev.php/soap',
'cache_wsdl' => WSDL_CACHE_NONE,
'exceptions' => true
);
$server = new \SoapServer(dirname(__FILE__).'/../../../**web/soap/stackoverflower.wsdl**', $options);
$server->setObject($this->get('stackoverflower_service'));
$response = new Response();
$response->headers->set('Content-Type', 'text/xml; charset=utf-8');
ob_start();
$server->handle();
$response->setContent(ob_get_clean());
return $response;
}
}
Para obtener más información sobre los servicios, consulte: Contenedor de servicios en Symfony doc.
La ruta :
example_soap:
path: /soap
defaults: { _controller: ExampleBundle:StackOverFlower:index }
La plantilla básica de la ramita:
#src\ExampleBundle\Resources\views\Soap\default.html.twig
{% if status is defined %}
{{ status }}
{% else %}
{{ list }}
{% endif %}
¡Hemos creado tu primera API SOAP con Symfony 2.8!
Antes de exponerlo, ¡tenemos que probarlo!
En su StackOverFlowerController, agregue esto:
public function testNewAction(Request $request)
{
$service = $this->get('stackoverflower_service');
$result = $service->newStack($request->query->get('name'));
return $this->render('ExampleBundle:Soap:default.html.twig', array('status' => $result));
}
public function testEditAction(Request $request)
{
$service = $this->get('stackoverflower_service');
$result = $service->edit($request->query->get('id'), $request->query->get('name'));
return $this->render('ExampleBundle:Soap:default.html.twig', array('status' => $result));
}
public function testGetListAction(Request $request)
{
$service = $this->get('stackoverflower_service');
$result = $service->getList();
return $this->render('ExampleBundle:Soap:default.html.twig', array('list' => $result));
}
public function testDeleteAction(Request $request)
{
$service = $this->get('stackoverflower_service');
$result = $service->delete($request->query->get('id'));
return $this->render('ExampleBundle:Soap:default.html.twig', array('list' => $result));
}
// To test this from an another server, you can type this :
// $client = new \SoapClient("http://example/app_dev.php/soap?wsdl", array("trace" => 1, "exception" => 1));
// $result = $client->newStack($request->query->get('name'));
// print_r($result);
Las rutas:
test_new:
path: /stackoverflower/new
defaults: { _controller: ExampleBundle:StackOverFlower:testNew }
test_edit:
path: /stackoverflower/edit
defaults: { _controller: ExampleBundle:StackOverFlower:testEdit }
test_get_list:
path: /stackoverflower/get-list
defaults: { _controller: ExampleBundle:StackOverFlower:testGetList }
test_delete:
path: /stackoverflower/delete
defaults: { _controller: ExampleBundle:StackOverFlower:testDelete }
Puede escribir esto en su navegador:
Este es un ejemplo muy básico de una API no segura con SOAP. Puedo hacer un ejemplo de un ejemplo seguro detrás de una autenticación de clave api más adelante.
Eso es todo amigos...
Mathieu