Best practices voor het ontwerpen van API's in Laravel

Van hier gestolen.

Dit bericht heeft een audioversie dankzij de Blogcast-app van Miguel Piedrafita.

Ik ben niet alleen een DevOps-ingenieur, maar ik ben ook vanaf nul begonnen met PHP sinds ik een klein kind was, in de dagen toen ik in de 5e klas zat. Sindsdien heb ik mijn code voortdurend verbeterd en ontdekte ik dat mensen een soort normen voor schonere code, betere visualisatie hebben aangenomen en omdat het normen zijn, kon iedereen beter begrijpen wat elke andere ontwikkelaar in dezelfde codebase schreef.

Ik heb altijd graag meer in de backend dan in de frontend gecodeerd. Ik probeerde een volledige stapel te zijn, maar het paste niet bij mij. Dus ik ben teruggevallen op het schrijven van backend, vooral API's. Deze keer zal ik het niet hebben over coderingsnormen in het algemeen, maar ik zal spreken over wat API's eigenlijk werken en hoe ze gemakkelijker te ontwikkelen zijn, van beveiliging tot tips over hoe het beter te doen.

Kortom, een API is een interface die gegevens retourneert in een speciaal formaat dat elke soort applicatie, of het nu een Android-app of een web-app is, kan begrijpen.

JSON wordt veel gebruikt, omdat het bijna overal is. De andere optie is om XML te gebruiken, maar ik had problemen met sommige API's van derden (met name betalingsproviders in mijn land) die XML gebruikten boven JSON en de ontwikkeling was totaal onzin. Ik raad aan om altijd JSON API's te ontwikkelen, tenzij iemand een XML API aanvraagt.

Bij het ontwikkelen van een API moet u enkele zaken in acht nemen, in deze specifieke volgorde:

  • beveiliging - beveiliging met OAuth, een API-sleutel of CORS is een must. Optioneel moet u een throttler gebruiken om de aanvragen voor uw app te beperken.
  • headers - zorg ervoor dat uw apps het juiste inhoudstype verzenden. Een inhoudstypekop is een stukje informatie dat de klant die de gegevens ontvangt vertelt: "dit ding dat ik u stuur is JSON" of "dat hier XML is. correct ontleden ”, zodat de browser of uw client weet hoe deze correct moet worden gedecodeerd.
  • codeerstandaarden en naamgeving - dit is puur backend. Zorg ervoor dat u consistent bent in uw antwoorden. Houd u aan slechts één soort naamgeving en juiste opmaak.

Ik ben dol op het coderen van API's in Laravel, omdat je verder wilt schalen met Event Broadcasting of de andere ontbrekende Lumen-functies die het razendsnel maken, je kunt het doen zonder het hele project helemaal opnieuw te schrijven. Als u op het absolute minimum blijft, voel u dan vrij om Lumen te gebruiken.

Veiligheid

Het grootste probleem waarvoor u moet zorgen, is de beveiliging. Het is gemakkelijk om het te beveiligen, maar als u het niet goed doet, krijgt u mogelijk ongewenste toegang. In Laravel wilt u misschien Laravel Passport gebruiken - het behoort tot het Laravel-ecosysteem, ondersteunt authenticatie via die app-ID - App Secret-dingy om een ​​toegangstoken te krijgen, hetzij als iemand of een server, het is backend of frontend. Kortom, u zult met uw App-ID en App-geheim een ​​verzoek indienen bij een OAuth-eindpunt om een ​​token te ontvangen, dat kan worden gegenereerd door een server (dat wil zeggen toegang tot een API via een opdracht die elke dag in een cronjob wordt uitgevoerd) of door een gebruiker die zich heeft aangemeld bij uw app.

Als alternatief wilt u misschien een JWT Token-authenticatie gebruiken, die bijna hetzelfde werkt, maar misschien is het gemakkelijker te begrijpen. Kies uw keuze en kijk welke het beste bij u past, zowel bij de implementatie als bij uw behoeften.

headers

Webverzoeken zijn gewoon normale gesprekken tussen een client en een server, of tussen twee servers. Ze vertrouwen op een verzoek en een antwoord, tenzij het een websocket-type verzoek is, maar dat is gewoon een ander verhaal. Bij het aanvragen of terugsturen van zaken, is er een beetje informatie genaamd Headers dat moet worden opgevangen - sommigen vertellen de server hoe de ontvangen informatie moet worden verwerkt of hoe de klant het antwoord wil ontvangen.

Het is alsof je moeder je vertelt: "ga wat melk kopen in de winkel maar koop alleen van de X-merkmelk". Je weet wat je moet doen, maar je hoeft er maar één soort melk uit te halen. Het is hetzelfde als de verzoeken: "ik wil de lijst met gebruikers krijgen, maar geef ze in JSON" of "ik wil alle gebruikers krijgen, maar stuur me in stukken van 20" (voor het geval dat u een GET-parameter opgeeft ). Hiervoor is er een koptekst genaamd Accepts, dit kan application / json zijn voor het geval je het antwoord als JSON wilt. Het is geen must, maar voor sommige apps zoals AJAX, als het die header detecteert, zal het automatisch de reactie voor de client decoderen, zonder iets als dit te doen:

var data = JSON.parse (response.data);

Een andere koptekst waarvan je op de hoogte moet zijn, is het inhoudstype, dat een beetje verwarrend is, maar het is het tegenovergestelde van Accepteert: het vertelt de server hoe hij de inhoud moet behandelen die hij krijgt. Als u bijvoorbeeld RAW-gegevens wilt verzenden, zoals een JSON-tekenreeks, kunt u het inhoudstype instellen op toepassing / json, maar als u de inhoud wilt ontvangen via de variabele $ _POST, moet u deze instellen op x-www -vorm-urlencode. Dit helpt je niet alleen bij het parseren van de inhoud rechtstreeks via $ _POST, maar het moet ook worden gebruikt in HTML-formulieren omdat het gemakkelijk toegankelijk is. Als u gegevens zoals binair verzendt, bijvoorbeeld via bestandsinvoer, moet u ervoor zorgen dat u de inhoud multipart / formulier-gegevens hebt verzonden.

In Laravel is dit geen probleem, omdat u rechtstreeks toegang hebt tot gegevens.

Voor toepassing / json:

functie-index (verzoek $ aanvragen)
{
   $ var = $ request-> variabele;
}

In application / json kunt u echter met de methode -> all () de JSON-inhoud niet zien:

functie-index (verzoek $ aanvragen)
{
   $ data = $ request-> all (); // dit is een lege array, zelfs als we gegevens hebben.
}

Indien ingesteld op x-www-form-urlencoded of multipart / form-data, kunt u alle variabelen zien met -> all ().

Wanneer u terug antwoordt, kunt u in Laravel de ingebouwde JSON-reactie gebruiken OF u kunt het beter uitwerken en een pakket zoals Laravel Fractal of Laravel Responder gebruiken. Uit eigen ervaring heeft Laravel Responder het werk beter gedaan, op een meer semantische manier. Laat me je laten zien:

functie getUsers (Verzoek $ aanvragen)
{
   return responder () -> success (Gebruiker :: all ()) -> respond ();
}

Hiermee worden alle gebruikers met de status OK 200 geretourneerd en opgemaakt als JSON. Cool toch? Voor fouten moet je zoiets doen, waarmee je een code en een bericht kunt verzenden:

functie getUsers (Verzoek $ aanvragen)
{
   $ users = Gebruiker :: all ();

   if ($ users-> count () === 0) {
      return responder () -> error ('no_users', 'Er zijn geen gebruikers.') -> respond ();
   }
   return responder () -> success ($ users) -> respond ();
}

Dit pakket ondersteunt veel meer, dus ga naar de documenten omdat het gemakkelijk kan worden geïntegreerd met transformatoren en aangepaste verzonden gegevens.

Coderingsstandaarden

Wat ik graag zie, is dat mensen zich houden aan een aantal normen die bij hen passen of schoon zijn. Hier zijn enkele tips die u kunnen helpen schonere code te bouwen en uw API-routes beter te structureren.

Gebruik routes / api.php bestand voor API routes

Laravel wordt geleverd met een afzonderlijk routes / api.php-bestand dat afwijkt van het gebruikelijke routes / web.php-bestand dat wordt gebruikt voor webroutering. api.php-bestand is gemaakt om uw API-routes op te slaan. Het heeft een ingebouwde middleware (die te zien is in app / Http / Kernel.php, in de variabele $ middlewareGroups, onder api) en een voorvoegsel van / api, dus alle gedefinieerde routes zijn al beschikbaar voor / api

Maak gebruik van de routenamen

Wat ik graag doe, is om een ​​as-instelling in te stellen voor de hele API, zodat ik toegang heb tot de routes met hun naam, met api. voorvoegsel.

Route :: get ('/ users', 'API \ UserController @ getUsers') -> naam ('get.users');

De URL van deze route kan worden opgehaald met behulp van route ('get.users'), maar deze kan conflicteren met web.php.

Route :: groep (['as' => 'api.'], Function () {
   Route :: get ('/ users', 'API \ UserController @ getUsers') -> naam ('get.users');
});

Op deze manier heb je het op de route ('api.get.users').

Als u de routenamen gebruikt, als u schrijftesten, hoeft u de URL niet overal te vervangen als u van plan bent de URL-locatie te wijzigen en de routenaam te behouden.

Beschrijvende, maar toch eenvoudige routes

Een goede gewoonte is om de routes in secties te groeperen, zoals gebruikers, berichten, enz. En de meest eenvoudige dingen te gebruiken die je kunt bedenken:

Route :: groep (['as' => 'api.'], Function () {
   Route :: groep (['as' => 'account.', 'Prefix' => '/ account'], functie () {
         Route :: get ('/ users', 'API \ UserController @ getUsers') -> naam ('get.users');
         Route :: get ('/ user / {id}', 'API \ UserController @ getUser') -> naam ('get.user');
         Route :: post ('/ user', 'API \ UserController @ createUser') -> naam ('create.user');
         // enzovoort.
   });
});

Gebruik :: get () voor het ophalen van gegevens, :: post () voor het maken, :: patch () of :: put () voor het bewerken en :: delete () voor het verwijderen van gegevens. Zo gebruik je uiteindelijk hetzelfde eindpunt, maar met de verschillende soorten verzoeken kun je andere acties activeren.

In uw controller moet u eenvoudige codering gebruiken en gebruik maken van de Request-klassen, zoals ik verder heb uitgelegd in Laravel verder duwen - beste tips en goede praktijken voor Laravel 5.7

functie getUsers (Verzoek $ aanvragen)
{
   $ users = Gebruiker :: all ();
   return responder () -> success ($ users) -> respond ();
}
functie getUser ($ id, Request $ request)
{
   $ user = User :: findOrFail ($ id);
   return responder () -> success ($ user) -> respond ();
}
functie createUser (CreateUserRequest $ aanvraag)
{
    $ user = User :: create ($ request-> all ());
    return responder () -> success ($ user) -> respond ();
}
// enzovoort.

Zorg er ook voor dat je voor alle acties dezelfde namen gebruikt, zodat je ze sneller vindt en iedereen de codebase kan binnengaan en het gemakkelijk maakt om inhoud te onderhouden of te maken. Verbind geen API-sleutels of gevoelige gegevens met de repo, schrijf schone code en leer graag van de anderen.

Te moeilijk om te begrijpen? Bereik me!

Als je meer vragen hebt over Laravel, als je hulp nodig hebt met informatie over DevOps of gewoon een bedankje wilt zeggen !, kun je me vinden op Twitter @rennokki!

Word lid van onze community Slack en lees onze wekelijkse Faun-onderwerpen ⬇

Als dit bericht nuttig was, klik dan een paar keer op de klap -knop hieronder om je steun voor de auteur te tonen! ⬇