Ricerca…
Lavora con API RESTFul
REpresentational State Transfer (REST) è uno stile architettonico utilizzato per lo sviluppo web, introdotto e definito nel 2000 da Roy Fielding.
Guardalo su wiki: wiki REST
Si basa su protocollo HTTP ( HTTP su Wiki ), richieste HTTP (GET, POST, PATCH, DELETE ...) / codici di risposte (404, 400, 200, 201, 500 ...) e struttura dei corpi.
Questo è un ottimo modo per esporre i tuoi dati su un altro sistema su Internet.
Immagina di voler creare una API RESTFul per gestire StackOverFlower (utente) sul tuo database locale.
Facciamo l'esempio!
Framework di Symfony 2.8
- Server web :
Devi installare e configurare un server web sul tuo computer locale, vedi Wamp o Lamp o Mamp : devi avere una versione recente di PHP ( !!! Requisiti di symfony !!! )
- Php cli e Compositore:
Devi configurare PHP cli (variando sul nostro sistema), digita questo "PHP cli [OS-NAME] how-to" nel nostro amico Google! Devi installare compositore, vedi Installazione di Composer
- Symfony:
Devi installare Symfony 2.8 (con il compositore, è il modo migliore), aprire un terminale (o cmd su Windows) e andare al percorso del tuo server web.
Symfony 2 funziona con uno dei migliori tipi di struttura: Bundles. Tutti sono pacchetti su Symfony! Possiamo testarlo sopra.
cd /your-web-server-path/
composer create-project symfony/framework-standard-edition example "2.8.*"
Vai alla struttura ad albero e vedi: Symfony 2.8 è installato nella directory "example".
- FOSRest (per FriendsOfSymfony) sul pacchetto JMSSerializer:
Devi installare questi due pacchetti:
JMSSerializer ( Installa ):
composer require jms/serializer-bundle "~0.13"
FosRestBundle ( Installa ):
composer require friendsofsymfony/rest-bundle
Non dimenticare di attivarli in AppKernel.php!
- Configurazione di base:
Crea il tuo pacchetto "Esempio" e crea il database.
cd /path/to/your/symfony/
php app/console generate:bundle
php app/console doctrine:generate:database
Vai in fondo al file di configurazione dell'applicazione Symfony 2.8 e incollalo:
#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 }
Crea la tua directory doctrine ("esempio / src / ExampleBundle / Entity") e il file di risorse ("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
Genera schema entità e aggiornamento:
php app/console doctrine:generate:entity StackOverFlower
php app/console doctrine:schema:update --force
Crea un controller predefinito:
#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);
}
}
Crea la tua vista di Twig predefinita:
#src/ExampleBundle/Resources/views/StackOverFlower.html.twig
{% if errors is defined %}
{{ errors }}
{% else %}
{% if users is defined %}
{{ users | serialize }}
{% else %}
{{ user | serialize }}
{% endif %}
{% endif %}
Hai appena creato la tua prima API RESTFul!
Puoi testarlo su: http: //your-server-name/your-symfony-path/app_dev.php/stackoverflower/new/test .
Come puoi vedere nel databse, è stato creato un nuovo utente con il nome "test".
È possibile ottenere l'elenco di stackover su: http: //your-server-name/your-symfony-path/app_dev.php/stackoverflowers
Hai un esempio completo sul mio account github di questo esempio: esempio di Git Hub , nel ramo "master" questo esempio, e sul "real-routes" branche un esempio con URL più appropriato (come POST e DELETE).
Ci vediamo più tardi per un esempio con SOAP!
I migliori saluti,
Mathieu
Lavora con l'API SOAP
SOAP (Simple Access Object Protocol) è basato su XML, come XML-RPC, è l'antenato , con file chiamato WSDL , che descrive il metodo da esporre.
Questo protocollo è spesso basato su SOAP-Enveloppe , un SOAP-Body e in alternativa SOAP-Header , i dati sono racchiusi in una struttura e interpretati allo stesso modo da diverse lingue.
Per maggiori informazioni, vedi: SOAP su wiki
Come descritto sopra, il più importante per descrivere il tuo servizio web è il file WSDL , vedi: spiegazione WSDL su wiki
La base del lavoro sarà definire ciò che è esposto sulla tua API SOAP, la tua classe e il tuo processo di business sarà gestito automaticamente dalla classe PHP SOAPServer di base. Hai ancora bisogno del codice!
Vediamo come viene costruito il file:
- Servizio: imposta l'URI dell'API e ciò che verrà associato.
- Binding: definisce le operazioni associate al servizio
- Operazioni: alcuni metodi che si desidera esporre al Web
- PortTypes: definisce query e risposte
- Richieste e risposte: cosa ti aspetti da input e output
- Messaggi: quale formato ti aspetti (parametri) su ogni IO, possono essere semplici (stringa, intero, float ...) o tipo complesso (formato strutturato)
Con queste informazioni di base, puoi ottenere tutte le API che desideri.
Immagina di voler creare un'API SOAP per gestire StackOverFlower (utente) sul tuo database locale.
Facciamo l'esempio!
Installa Web server, Php cli, Composer, Symfony 2.8, crea un nuovo Bundle "ExampleBundle" e crea lo schema come descritto sopra.
Prima di iniziare a costruire la nostra logica di business, abbiamo dovuto sapere cosa esporre del nostro controller. Questo lavoro viene eseguito utilizzando WSDL. Questo è un esempio di una buona sintassi di 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>
Dobbiamo prendere questo nella tua directory web symfony (nella sottodirectory soap e chiamarlo "stackoverflower.wsdl").
Davvero ispirato all'esempio WSDl . È possibile convalidarlo con un validatore WSDl in linea
Dopo questo, possiamo rendere il nostro servizio di base e controller, ispirato da SOAP Symfony 2.8 Doc .
Servizio, gestito da 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 questo servizio:
#src\ExampleBundle\Resources\config\services.yml
services:
stackoverflower_service:
class: ExampleBundle\Services\StackOverFlowerService
arguments: [@doctrine.orm.entity_manager]
Come puoi vedere, iniettiamo Doctrine Entity Manger come dipendenza perché dobbiamo usarlo in CRUD StackOverFlower Object.
Controller, che espone l'oggetto servizio:
#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;
}
}
Per ulteriori informazioni sui servizi, consultare: Contenitore di servizi su Symfony doc
La strada :
example_soap:
path: /soap
defaults: { _controller: ExampleBundle:StackOverFlower:index }
Il modello di ramoscello di base:
#src\ExampleBundle\Resources\views\Soap\default.html.twig
{% if status is defined %}
{{ status }}
{% else %}
{{ list }}
{% endif %}
Abbiamo creato la tua prima API SOAP con Symfony 2.8!
Prima di esporlo, dobbiamo testare !!
Nel tuo StackOverFlowerController, aggiungi questo:
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);
I percorsi:
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 }
Puoi digitare questo nel tuo browser:
Questo è un esempio molto semplice di un'API non sicura con SOAP, posso fare un esempio di un esempio protetto dietro un'autenticazione della chiave API in seguito.
Che tutti ...
Mathieu