Recherche…


Travailler avec l'API RESTFul

Le transfert d'état représentatif (REST) ​​est un style architectural utilisé pour le développement Web, introduit et défini en 2000 par Roy Fielding.

Voir sur le wiki: wiki REST

Il est basé sur le protocole HTTP ( HTTP on Wiki ), les requêtes HTTP (GET, POST, PATCH, DELETE ...) / les codes réponses (404, 400, 200, 201, 500 ...) et la structure des corps.

C'est un excellent moyen d'exposer vos données à un autre système sur Internet.

Imaginez que vous souhaitiez créer une api RESTFul pour gérer votre StackOverFlower (utilisateur) sur votre base de données locale.

Prenons l'exemple!

Framework Symfony 2.8

  1. Serveur Web :

Vous devez installer et configurer un serveur Web sur votre machine locale, voir Wamp ou Lamp ou Mamp : vous devez avoir une version récente de PHP ( !!! Exigences Symfony !!! )

  1. Php cli et compositeur:

Vous devez configurer PHP cli (variable selon notre système), tapez ce "PHP cli [OS-NAME] How-to" dans notre ami Google! Vous devez installer le composeur, voir Composer install

  1. Symfony:

Vous devez installer Symfony 2.8 (avec compositeur, c'est mieux), ouvrez un terminal (ou cmd sur Windows) et accédez au chemin de votre serveur Web.

Symfony 2 fonctionne avec l'un des meilleurs types de structure: Bundles. Tous sont des paquets sur Symfony! Nous pouvons le tester ci-dessus.

cd /your-web-server-path/
composer create-project symfony/framework-standard-edition example "2.8.*"

Allez dans l'arborescence voir: Symfony 2.8 est installé sur le répertoire "example".

  1. FOSRest (for FriendsOfSymfony) sur JMSSerializer Bundle:

Vous devez installer ces deux bundles:

JMSSerializer ( Install ):

composer require jms/serializer-bundle "~0.13"

FosRestBundle ( Install ):

composer require friendsofsymfony/rest-bundle

N'oubliez pas de les activer dans AppKernel.php!

  1. Configuration de base:

Créez votre propre ensemble "Exemple" et créez la base de données.

cd /path/to/your/symfony/
php app/console generate:bundle
php app/console doctrine:generate:database

Allez au bas du fichier de configuration de votre application Symfony 2.8 et collez-le:

#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 }

Faites votre répertoire de doctrine ("example / src / ExampleBundle / Entity") et le fichier de ressources ("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

Générer un schéma d'entité et de mise à jour:

php app/console doctrine:generate:entity StackOverFlower
php app/console doctrine:schema:update --force

Faire un contrôleur par défaut:

#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);
    }
}

Faites votre vue Twig par défaut:

#src/ExampleBundle/Resources/views/StackOverFlower.html.twig
{% if errors is defined %}
  {{ errors }}  
{% else %}
  {% if users is defined %}
    {{ users | serialize }}
  {% else %}
    {{ user | serialize }}
  {% endif %}
{% endif %}

Vous venez de faire votre première API RESTFul!

Vous pouvez le tester sur: http: //votre-nom-serveur/votre-symfony-path/app_dev.php/stackoverflower/new/test .

Comme vous pouvez le voir dans la base de données, un nouvel utilisateur a été créé avec le nom "test".

Vous pouvez obtenir la liste des stackoverflower sur: http: //votre-nom-serveur/votre-symfony-path/app_dev.php/stackoverflowers

Vous avez un exemple complet sur mon compte github de cet exemple: exemple Git Hub , sur la branche "master" de cet exemple, et sur la branche "real-routes" un exemple avec une URL plus appropriée (comme POST et DELETE).

A plus tard pour un exemple avec SOAP!

Meilleures salutations,

Mathieu

Travailler avec l'API SOAP

SOAP (Simple Access Object Protocol) est basé sur XML, comme XML-RPC, est ancêtre , avec un fichier appelé WSDL , qui décrit la méthode à exposer.

Ce protocole est souvent basé sur SOAP-Enveloppe , un SOAP-Body , ou encore SOAP-Header , les données sont enveloppées dans une structure et interprétées de la même manière à partir de différentes langues.

Description d'un message de savon

Pour plus d'informations, voir: SOAP sur wiki

Comme décrit ci-dessus, le plus important pour décrire votre service Web est le fichier WSDL , voir: explication WSDL sur le wiki

La base du travail sera de définir ce qui est exposé sur votre API SOAP, votre classe et votre processus métier seront automatiquement gérés par la classe PHP SOAPServer de base. Vous avez toujours besoin du code!

Voyons comment le fichier est construit:

  1. Service: Définit l'URI de l'API et ce qui sera associé.
  2. Liaison: elle définit les opérations associées au service
  3. Opérations: certaines méthodes que vous souhaitez exposer au Web
  4. PortTypes: définir des requêtes et des réponses
  5. Demandes et réponses: ce que vous attendez d'entrée et de sortie
  6. Messages: quelle forme attendez-vous (paramètres) sur chaque IO, ils peuvent être simples (string, integer, float ...) ou complexes (format structuré)

Avec ces informations de base, vous pouvez atteindre toutes les API que vous souhaitez.

Imaginez que vous souhaitiez créer une API SOAP pour gérer votre StackOverFlower (utilisateur) sur votre base de données locale.

Prenons l'exemple!

Installez le serveur Web, Php cli, Composer, Symfony 2.8, créez un nouvel ensemble "ExampleBundle" et créez le schéma comme décrit ci-dessus.

Avant de commencer à construire notre logique métier, nous devions savoir quoi exposer de notre contrôleur. Ce travail est effectué en utilisant le WSDL. Ceci est un exemple d'une bonne syntaxe d'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>

Nous devons prendre cela sur votre répertoire web symfony (dans le sous-répertoire soap et nommez-le "stackoverflower.wsdl").

Vraiment inspiré de WSDl Exemple . Vous pouvez valider cela avec un validateur WSDl en ligne

Après cela, nous pouvons rendre notre service et contrôleur de base inspiré de SOAP Symfony 2.8 Doc .

Service géré par 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";
  }
}

Configurez ce service:

#src\ExampleBundle\Resources\config\services.yml
services:
  stackoverflower_service:
    class: ExampleBundle\Services\StackOverFlowerService
    arguments: [@doctrine.orm.entity_manager]

Comme vous pouvez le voir, nous injectons la Doctrine Entity Manger comme une dépendance car nous devons l'utiliser pour CRUD StackOverFlower Object.

Contrôleur, qui expose l'objet de service:

#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;
  }
}

Pour en savoir plus sur les services, voir: Conteneur de services sur le doc Symfony

La route :

example_soap:
  path:     /soap
  defaults: { _controller: ExampleBundle:StackOverFlower:index }

Le modèle de base de la brindille:

#src\ExampleBundle\Resources\views\Soap\default.html.twig
{% if status is defined %}
{{ status }}
{% else %}
{{ list }}
{% endif %}

Nous avons créé votre première API SOAP avec Symfony 2.8!

Avant de l'exposer, il faut tester !!

Dans votre StackOverFlowerController, ajoutez ceci:

  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); 

Les routes:

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 }

Vous pouvez taper ceci dans votre navigateur:

  1. getList
  2. Nouveau
  3. modifier
  4. effacer

Ceci est un exemple très basique d'une API non sécurisée avec SOAP, je peux faire un exemple d'exemple sécurisé derrière une authentification par clé api ultérieurement.

Que tous les gens ...

Mathieu



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow