Εκπαιδευτικό πρόγραμμα REST API - Κλήσεις πελάτη REST, υπηρεσία REST και API που εξηγούνται με παραδείγματα κώδικα

Αναρωτηθήκατε ποτέ πώς λειτουργεί η είσοδος / εγγραφή σε έναν ιστότοπο στο πίσω μέρος; Ή πώς όταν αναζητάτε "χαριτωμένα γατάκια" στο YouTube, θα έχετε πολλά αποτελέσματα και μπορείτε να κάνετε streaming από ένα απομακρυσμένο μηχάνημα;

Σε αυτόν τον οδηγό φιλικό για αρχάριους, θα σας καθοδηγήσω στη διαδικασία δημιουργίας ενός RESTful API. Θα αποχαρακτηρίσουμε μέρος της ορολογίας και θα ρίξουμε μια ματιά στον τρόπο με τον οποίο μπορούμε να κωδικοποιήσουμε έναν διακομιστή στο NodeJS. Ας βουτήξουμε λίγο πιο βαθιά στο JavaScript!

Βγάλτε αυτήν την ορολογία

Λοιπόν, τι είναι το REST; Σύμφωνα με την Wikipedia:

Η αντιπροσωπευτική μεταφορά κατάστασης ( REST ) είναι ένα αρχιτεκτονικό στυλ λογισμικού που καθορίζει ένα σύνολο περιορισμών που πρέπει να χρησιμοποιούνται για τη δημιουργία υπηρεσιών Web. Οι υπηρεσίες RESTful Web επιτρέπουν στα αιτούντα συστήματα να έχουν πρόσβαση και να χειρίζονται αναπαράσταση κειμένων των πόρων του Ιστού χρησιμοποιώντας ένα ομοιόμορφο και προκαθορισμένο σύνολο λειτουργιών χωρίς κατάσταση

Ας απομυθοποιήσουμε τι σημαίνει αυτό (ελπίζουμε να έχετε την πλήρη φόρμα). Το REST είναι βασικά ένα σύνολο κανόνων επικοινωνίας μεταξύ πελάτη και διακομιστή. Υπάρχουν μερικοί περιορισμοί στον ορισμό του REST:

  1. Αρχιτεκτονική πελάτη-διακομιστή : το περιβάλλον εργασίας χρήστη του ιστότοπου / της εφαρμογής πρέπει να διαχωριστεί από το αίτημα / αποθήκευση δεδομένων, έτσι ώστε κάθε τμήμα να μπορεί να κλιμακωθεί ξεχωριστά.
  2. Statelessness : η επικοινωνία δεν πρέπει να έχει αποθηκευμένο περιβάλλον πελάτη στο διακομιστή. Αυτό σημαίνει ότι κάθε αίτημα προς τον διακομιστή πρέπει να υποβάλλεται με όλα τα απαιτούμενα δεδομένα και δεν πρέπει να γίνονται παραδοχές εάν ο διακομιστής έχει δεδομένα από προηγούμενα αιτήματα.
  3. Πολυεπίπεδη σύστημα : ο πελάτης δεν θα πρέπει να μπορεί να πει εάν επικοινωνεί απευθείας με τον διακομιστή ή κάποιον ενδιάμεσο. Αυτοί οι ενδιάμεσοι διακομιστές (είτε πρόκειται για διακομιστή μεσολάβησης είτε για εξισορρόπηση φορτίου) επιτρέπουν την επεκτασιμότητα και την ασφάλεια του υποκείμενου διακομιστή.

Εντάξει, οπότε τώρα που γνωρίζετε ποιες είναι οι υπηρεσίες RESTful, ακολουθούν ορισμένοι από τους όρους που χρησιμοποιούνται στον τίτλο:

  1. Πελάτης REST : κωδικός ή μια εφαρμογή που μπορεί να έχει πρόσβαση σε αυτές τις υπηρεσίες REST. Χρησιμοποιείτε ένα τώρα! Ναι, το πρόγραμμα περιήγησης μπορεί να λειτουργήσει ως ανεξέλεγκτος πελάτης REST (ο ιστότοπος χειρίζεται τα αιτήματα του προγράμματος περιήγησης). Το πρόγραμμα περιήγησης, για μεγάλο χρονικό διάστημα, χρησιμοποίησε μια ενσωματωμένη συνάρτηση που ονομάζεται XMLHttpRequest για όλα τα αιτήματα REST. Όμως, αυτό διαδέχθηκε ο FetchAPI, μια σύγχρονη προσέγγιση που βασίζεται σε υποσχέσεις στα αιτήματα. Άλλα παραδείγματα είναι βιβλιοθήκες κώδικα όπως axios, superagent and got ή ορισμένες ειδικές εφαρμογές όπως Postman (ή μια διαδικτυακή έκδοση, postwoman!) Ή ένα εργαλείο γραμμής εντολών όπως το cURL!
  2. Υπηρεσία REST : ο διακομιστής. Υπάρχουν πολλές δημοφιλείς βιβλιοθήκες που κάνουν τη δημιουργία αυτών των διακομιστών ένα αεράκι, όπως το ExpressJS για το NodeJS και το Django για το Python.
  3. REST API : αυτό καθορίζει το τελικό σημείο και τις μεθόδους που επιτρέπεται η πρόσβαση / υποβολή δεδομένων στον διακομιστή. Θα το συζητήσουμε λεπτομερώς παρακάτω. Άλλες εναλλακτικές λύσεις είναι: GraphQL, JSON-Pure και oData.

Πες μου λοιπόν, πώς φαίνεται το REST;

Σε πολύ γενικούς όρους, ζητάτε από τον διακομιστή ορισμένα δεδομένα ή ζητάτε να αποθηκεύσει ορισμένα δεδομένα και ο διακομιστής ανταποκρίνεται στα αιτήματα.

Σε όρους προγραμματισμού, υπάρχει ένα τελικό σημείο (ένα URL) που ο διακομιστής περιμένει να λάβει ένα αίτημα. Συνδεόμαστε σε αυτό το τελικό σημείο και στέλνουμε ορισμένα δεδομένα για εμάς (θυμηθείτε, το REST είναι απάτριδες, δεν αποθηκεύονται δεδομένα σχετικά με το αίτημα) και ο διακομιστής αποκρίνεται με τη σωστή απόκριση.

Τα λόγια είναι βαρετά, επιτρέψτε μου να σας δείξω μια επίδειξη. Θα χρησιμοποιήσω τον Ταχυδρόμο για να σας δείξω το αίτημα και την απάντηση:

Τα δεδομένα που επιστρέφονται είναι σε JSON (Σημείωση αντικειμένου JavaScript) και μπορείτε να έχετε απευθείας πρόσβαση.

Εδώ, //official-joke-api.appspot.com/random_jokeονομάζεται τελικό σημείο ενός API. Θα υπάρχει ένας διακομιστής που θα ακούει σε αυτό το τελικό σημείο για αιτήματα όπως αυτό που κάναμε.

Ανατομία του REST:

Εντάξει, οπότε τώρα γνωρίζουμε ότι τα δεδομένα μπορούν να ζητηθούν από τον πελάτη και ο διακομιστής θα ανταποκριθεί κατάλληλα. Ας δούμε βαθύτερα πώς διαμορφώνεται ένα αίτημα.

  1. Endpoint : Σας έχω ήδη πει για αυτό. Για μια ανανέωση, είναι το URL όπου ακούει ο διακομιστής REST.
  2. Μέθοδος : Νωρίτερα, έγραψα ότι μπορείτε είτε να ζητήσετε δεδομένα είτε να τα τροποποιήσετε, αλλά πώς θα γνωρίζει ο διακομιστής τι είδους λειτουργία θέλει να εκτελέσει ο πελάτης; Το REST εφαρμόζει πολλαπλές «μεθόδους» για διαφορετικούς τύπους αιτημάτων, τα πιο δημοφιλή είναι τα ακόλουθα:

    - GET : Λάβετε πόρους από το διακομιστή.

    - POST : Δημιουργία πόρου στο διακομιστή.

    - PATCH ή PUT : Ενημέρωση υπάρχοντος πόρου στο διακομιστή.

    - ΔΙΑΓΡΑΦΗ : Διαγραφή υπάρχοντος πόρου από το διακομιστή.

  3. Κεφαλίδες : Οι πρόσθετες λεπτομέρειες που παρέχονται για επικοινωνία μεταξύ πελάτη και διακομιστή (θυμηθείτε, το REST είναι απάτριδα). Μερικές από τις κοινές κεφαλίδες είναι:

    Αίτηση:

    - κεντρικός υπολογιστής : η διεύθυνση IP του πελάτη (ή από όπου προήλθε το αίτημα)

    - Γλώσσα αποδοχής : γλώσσα κατανοητή από τον πελάτη

    - πράκτορας χρήστη : δεδομένα σχετικά με τον πελάτη, το λειτουργικό σύστημα και τον προμηθευτή

    Απάντηση :

    - κατάσταση : η κατάσταση του αιτήματος ή ο κωδικός HTTP.

    - τύπος περιεχομένου : τύπος πόρου που αποστέλλεται από διακομιστή.

    - set-cookie : ορίζει cookie ανά διακομιστή

  4. Δεδομένα : (ονομάζεται επίσης σώμα ή μήνυμα) περιέχει πληροφορίες που θέλετε να στείλετε στον διακομιστή.

Αρκετά με τις λεπτομέρειες - δείξτε μου τον κωδικό.

Ας αρχίσουμε να κωδικοποιούμε μια υπηρεσία REST στον κόμβο. Θα εφαρμόσουμε όλα τα πράγματα που μάθαμε παραπάνω. Θα χρησιμοποιήσουμε επίσης το ES6 + για να γράψουμε την υπηρεσία μας.

Βεβαιωθείτε ότι έχετε εγκαταστήσει το Node.JS nodeκαι ότι npmείστε διαθέσιμοι στη διαδρομή σας. Θα χρησιμοποιώ τους κόμβους 12.16.2 και NPM 6.14.4.

Δημιουργήστε έναν κατάλογο rest-service-nodeκαι cd σε αυτόν:

mkdir rest-service-node cd rest-service-node

Αρχικοποιήστε το έργο κόμβου:

npm init -y

Η -yσημαία παραλείπει όλες τις ερωτήσεις. Εάν θέλετε να συμπληρώσετε ολόκληρο το ερωτηματολόγιο, απλώς εκτελέστε npm init.

Let's install some packages. We will be using the ExpressJS framework for developing the REST Server. Run the following command to install it:

npm install --save express body-parser

What's body-parser there for? Express, by default, is incapable of handling data sent via POST request as JSON. body-parser allows Express to overcome this.

Create a file called server.js and add the following code:

const express = require("express"); const bodyParser = require("body-parser"); const app = express(); app.use(bodyParser.json()); app.listen(5000, () => { console.log(`Server is running on port 5000.`); }); 

The first two lines are importing Express and body-parser.

Third line initializes the Express server and sets it to a variable called app.

The line, app.use(bodyParser.json()); initializes the body-parser plugin.

Finally, we are setting our server to listen on port 5000 for requests.

Getting data from the REST Server:

To get data from a server, we need a GET request. Add the following code before app.listen:

const sayHi = (req, res) => { res.send("Hi!"); }; app.get("/", sayHi);

We have created a function sayHi which takes two parameters req and res (I will explain later) and sends a 'Hi!' as response.

app.get() takes two parameters, the route path and function to call when the path is requested by the client. So, the last line translates to: Hey server, listen for requests on the '/' (think homepage) and call the sayHi function if a request is made.

app.get also gives us a request object containing all the data sent by the client and a response object which contains all the methods with which we can respond to the client. Though these are accessible as function parameters, the general naming convention suggests we name them res for response and req for request.

Enough chatter. Let's fire up the server! Run the following server:

node server.js

If everything is successful, you should see a message on console saying: Server is running on port 5000.

Note: You can change the port to whatever number you want.

Open up your browser and navigate to //localhost:5000/ and you should see something like this:

There you go! Your first GET request was successful!

Sending data to REST Server:

As we have discussed earlier, let's setup how we can implement a POST request into our server. We will be sending in two numbers and the server will return the sum of the numbers. Add this new method below the app.get :

app.post("/add", (req, res) => { const { a, b } = req.body; res.send(`The sum is: ${a + b}`); });

Here, we will be sending the data in JSON format, like this:

{ "a":5, "b":10 }

Let's get over the code:

On line 1, we are invoking the .post() method of ExpressJS, which allows the server to listen for POST requests. This function takes in the same parameters as the .get() method. The route that we are passing is /add, so one can access the endpoint as //your-ip-address:port/add or in our case localhost:5000/add. We are inlining our function instead of writing a function elsewhere.

On line 2, we have used a bit of ES6 syntax, namely, object destructuring. Whatever data we send via the request gets stored and is available in the body of the req object. So essentially, we could've replaced line 2 with something like:

const num1 = req.body.a; const num2 = req.body.b;

On line 3, we are using the send() function of the res object to send the result of the sum. Again, we are using template literals from ES6. Now to test it (using Postman):

So we have sent the data 5 and 10 as a and b using them as the body. Postman attaches this data to the request and sends it. When the server receives the request, it can parse the data from req.body , as we did in the code above. The result is shown below.

Alright, the final code:

const express = require("express"); const bodyParser = require("body-parser"); const app = express(); app.use(bodyParser.json()); const sayHi = (req, res) => { res.send("Hi!"); }; app.get("/", sayHi); app.post("/add", (req, res) => { const { a, b } = req.body; res.send(`The sum is: ${a + b}`); }); app.listen(5000, () => { console.log(`Server is running on port 5000.`); }); 

REST Client:

Okay, we have created a server, but how do we access it from our website or webapp? Here the REST client libraries will come in handy.

We will be building a webpage which will contain a form, where you can enter two numbers and we will display the result. Let's start.

First, let's change the server.js a bit:

const path = require("path"); const express = require("express"); const bodyParser = require("body-parser"); const app = express(); app.use(bodyParser.json()); app.get("/", (req, res) => { res.sendFile(path.join(__dirname, "index.html")); }); app.post("/add", (req, res) => { const { a, b } = req.body; res.send({ result: parseInt(a) + parseInt(b) }); }); app.listen(5000, () => { console.log(`Server is running on port 5000.`); }); 

We imported a new package path, which is provided by Node, to manipulate path cross-platform. Next we changed the GET request on '/' and use another function available in res, ie. sendFile, which allows us to send any type of file as response. So, whenever a person tries to navigate to '/', they will get our index.html page.

Finally, we changed our app.post function to return the sum as JSON and convert both a and b to integers.

Let's create an html page, I will call it index.html, with some basic styling:

     REST Client   * { margin: 0; padding: 0; box-sizing: border-box; } .container { height: 100vh; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; display: flex; flex-direction: column; justify-content: center; align-items: center; } form { display: flex; flex-direction: column; margin-bottom: 20px; } label, input[type="submit"] { margin-top: 20px; } 

Simple POST Form

Number 1: Number 2: Click Add!

Let's add a script tag just before the closing body tag, so we don't need to maintain a .js file. We will begin by listening for the submit event and call a function accordingly:

 document.addEventListener("submit", sendData); 

First we need to prevent page refresh when the 'Add' button is clicked. This can be done using the preventDefault() function. Then, we will get the value of the inputs at that instant:

function sendData(e) { e.preventDefault(); const a = document.querySelector("#num1").value; const b = document.querySelector("#num2").value; }

Now we will make the call to the server with both these values a and b. We will be using the Fetch API, built-in to every browser for this.

Fetch takes in two inputs, the URL endpoint and a JSON request object and returns a Promise. Explaining them here will be out-of-bounds here, so I'll leave that for you.

Continue inside the sendData() function:

fetch("/add", { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json" }, body: JSON.stringify({ a: parseInt(a), b: parseInt(b) }) }) .then(res => res.json()) .then(data => { const { result } = data; document.querySelector( ".result" ).innerText = `The sum is: ${result}`; }) .catch(err => console.log(err));

First we are passing the relative URL of the endpoint as the first parameter to fetch. Next, we are passing an object which contains the method we want Fetch to use for the request, which is POST in this case.

We are also passing headers, which will provide information about the type of data we are sending (content-type) and the type of data we accept as response (accept).

Next we pass body. Remember we typed the data as JSON while using Postman? We're doing kind of a similar thing here. Since express deals with string as input and processes it according to content-type provided, we need to convert our JSON payload into string. We do that with JSON.stringify(). We're being a little extra cautious and parsing the input into integers, so it doesn't mess up our server (since we haven't implemented any data-type checking).

Finally, if the promise (returned by fetch) resolves, we will get that response and convert it into JSON. After that, we will get the result from the data key returned by the response. Then we are simply displaying the result on the screen.

At the end, if the promise is rejected, we will display the error message on the console.

Here's the final code for index.html:

     REST Client   * { margin: 0; padding: 0; box-sizing: border-box; } .container { height: 100vh; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; display: flex; flex-direction: column; justify-content: center; align-items: center; } form { display: flex; flex-direction: column; margin-bottom: 20px; } label, input[type="submit"] { margin-top: 20px; } 

Simple POST Form

Number 1: Number 2: Click Add! document.addEventListener("submit", sendData); function sendData(e) { e.preventDefault(); const a = document.querySelector("#num1").value; const b = document.querySelector("#num2").value; fetch("/add", { method: "POST", headers: { Accept: "application/json", "Content-Type": "application/json" }, body: JSON.stringify({ a: parseInt(a), b: parseInt(b) }) }) .then(res => res.json()) .then(data => { const { result } = data; document.querySelector( ".result" ).innerText = `The sum is: ${result}`; }) .catch(err => console.log(err)); }

I have spun up a little app on glitch for you to test.

Conclusion:

So in this post, we learnt about REST architecture and the anatomy of REST requests. We worked our way through by creating a simple REST Server that serves GET and POST requests and built a simple webpage that uses a REST Client to display the sum of two numbers.

Μπορείτε να το επεκτείνετε για τους υπόλοιπους τύπους αιτημάτων και ακόμη και να εφαρμόσετε μια πλήρη εφαρμογή CRUD back-end.

Ελπίζω να έχετε μάθει κάτι από αυτό. Αν έχετε απορίες, μη διστάσετε να επικοινωνήσετε μαζί μου μέσω του twitter! Καλή κωδικοποίηση!