Teil 4. Erstellen einer REST-API: Behandeln von POST-, PUT- und DELETE-AnforderungenIm
vorherigen Artikel haben Sie der API Logik für GET-Anforderungen hinzugefügt, mit denen Daten aus einer Datenbank abgerufen wurden. In diesem Beitrag vervollständigen Sie die Grundfunktionen der CRUD-API, indem Sie Logik hinzufügen, um POST-, PUT- und DELETE-Anforderungen zu verarbeiten.
Routing-Logik hinzufügenUm die Routing-Logik zu vereinfachen, leiten Sie alle HTTP-Methoden über eine vorhandene Route um (mit einem optionalen ID-Parameter). Öffnen Sie die
Datei services / router.js und ersetzen Sie die aktuelle Routing-Logik (Zeilen 5-6) durch den folgenden Code:
router.route('/employees/:id?') .get(employees.get) .post(employees.post) .put(employees.put) .delete(employees.delete);
Die aktualisierte Routing-Logik ordnet die vier am häufigsten verwendeten HTTP-Methoden für grundlegende CRUD-Operationen der richtigen Controller-Logik zu.
POST-AnforderungsverarbeitungHTTP-POST-Anforderungen werden zum Erstellen neuer Ressourcen verwendet (in diesem Fall Mitarbeiterdatensätze). Die Hauptidee besteht darin, Daten aus dem Hauptteil der HTTP-Anforderung zu extrahieren und daraus eine neue Zeile in der Datenbank zu erstellen. Öffnen Sie zum Hinzufügen der Controller-Logik für POST-Anforderungen die Datei
controller / employee.js und fügen Sie den folgenden Code hinzu:
function getEmployeeFromRec(req) { const employee = { first_name: req.body.first_name, last_name: req.body.last_name, email: req.body.email, phone_number: req.body.phone_number, hire_date: req.body.hire_date, job_id: req.body.job_id, salary: req.body.salary, commission_pct: req.body.commission_pct, manager_id: req.body.manager_id, department_id: req.body.department_id }; return employee; } async function post(req, res, next) { try { let employee = getEmployeeFromRec(req); employee = await employees.create(employee); res.status(201).json(employee); } catch (err) { next(err); } } module.exports.post = post;
Die Funktion getEmployeeFromRec nimmt ein Anforderungsobjekt und gibt ein Objekt mit den Eigenschaften zurück, die zum Erstellen eines Mitarbeiterdatensatzes erforderlich sind. Die Funktion wurde außerhalb der Post-Funktion deklariert, damit sie später für PUT-Anforderungen verwendet werden kann.
Die Post-Funktion initialisiert die Variable mit getEmployeeFromRec, die dann an die create-Methode der Mitarbeiterdatenbank-API übergeben wird. Nach dem Erstellungsvorgang wird der Statuscode „201 Erstellt“ zusammen mit dem Mitarbeiter-JSON (einschließlich des neuen Werts der Mitarbeiter-ID) an den Client gesendet.
Jetzt können Sie auf die Logik in der Datenbank-API achten. Öffnen Sie die Datei
db_apis / employee.js und fügen Sie den folgenden Code hinzu.
const createSql = `insert into employees ( first_name, last_name, email, phone_number, hire_date, job_id, salary, commission_pct, manager_id, department_id ) values ( :first_name, :last_name, :email, :phone_number, :hire_date, :job_id, :salary, :commission_pct, :manager_id, :department_id ) returning employee_id into :employee_id`; async function create(emp) { const employee = Object.assign({}, emp); employee.employee_id = { dir: oracledb.BIND_OUT, type: oracledb.NUMBER } const result = await database.simpleExecute(createSql, employee); employee.employee_id = result.outBinds.employee_id[0]; return employee; } module.exports.create = create;
Die obige Logik deklariert zunächst eine Konstante mit dem Namen createSql, um die insert-Anweisung zu speichern. Beachten Sie, dass
Bindungsvariablen und keine Zeichenfolgenverkettung verwendet werden, um auf die einzufügenden Werte zu verweisen. Es lohnt sich zu wiederholen, wie wichtig Bindevariablen aus Sicherheits- und Leistungsgründen sind. Vermeiden Sie nach Möglichkeit die Verkettung von Zeichenfolgen.
Innerhalb der Funktion create wird die Mitarbeiterkonstante für eine Kopie des Parameters emp mit Object.assign definiert und initialisiert. Dies verhindert eine direkte Änderung des von der Steuerung übertragenen Objekts.
Anschließend wird die Eigenschaft employee_id zum Mitarbeiterobjekt hinzugefügt (konfiguriert als "out bind"), sodass sie alle Bindungsvariablen enthält, die zum Ausführen der SQL-Anweisung erforderlich sind. Anschließend wird die Funktion insertExecute mit der Funktion simpleExecute ausgeführt, und mit der Eigenschaft outBinds wird die Eigenschaft employee.employee_id überschrieben, bevor das Objekt zurückgegeben wird.
Da es einen Link zum oracledb-Modul gibt, müssen Sie die folgende Zeile oben in die Datei einfügen.
const oracledb = require('oracledb');
PUT-AnforderungsverarbeitungPUT-Anforderungen werden verwendet, um vorhandene Ressourcen zu aktualisieren. Es ist wichtig zu beachten, dass PUT verwendet wird, um die gesamte Ressource zu ersetzen - es werden keine Teilaktualisierungen durchgeführt (ich werde Ihnen zeigen, wie dies in Zukunft mit PATCH gemacht wird). Kehren Sie zur Datei
controller / employee.js zurück und fügen Sie den folgenden Code hinzu.
async function put(req, res, next) { try { let employee = getEmployeeFromRec(req); employee.employee_id = parseInt(req.params.id, 10); employee = await employees.update(employee); if (employee !== null) { res.status(200).json(employee); } else { res.status(404).end(); } } catch (err) { next(err); } } module.exports.put = put;
Die put-Funktion verwendet getEmployeeFromRec, um ein Objekt mit dem Namen employee zu initialisieren, und fügt dann der URL die Eigenschaft employee_id aus dem Parameter id hinzu. Anschließend wird das Mitarbeiterobjekt an die Datenbank-API-Aktualisierungsfunktion übergeben und die entsprechende Antwort basierend auf dem Ergebnis an den Client gesendet.
Fügen Sie der Datei
db_apis / employee.js den folgenden Code hinzu, um die Aktualisierungslogik zur Datenbank-API hinzuzufügen.
const updateSql = `update employees set first_name = :first_name, last_name = :last_name, email = :email, phone_number = :phone_number, hire_date = :hire_date, job_id = :job_id, salary = :salary, commission_pct = :commission_pct, manager_id = :manager_id, department_id = :department_id where employee_id = :employee_id`; async function update(emp) { const employee = Object.assign({}, emp); const result = await database.simpleExecute(updateSql, employee); if (result.rowsAffected && result.rowsAffected === 1) { return employee; } else { return null; } } module.exports.update = update;
Die Aktualisierungslogik ist der Erstellungslogik sehr ähnlich. Eine Variable wird deklariert, um die SQL-Anweisung zu speichern, und dann wird simpleExecute verwendet, um die Anweisung mit den übertragenen dynamischen Werten auszuführen (nachdem sie in ein anderes Objekt kopiert wurden). Mit result.rowsAffected wird festgestellt, ob die Aktualisierung erfolgreich war, und der richtige Wert zurückgegeben.
Anforderungsverarbeitung LÖSCHENDie letzte Methode, die Sie implementieren müssen, ist DELETE, mit der überraschenderweise Ressourcen aus der Datenbank entfernt werden. Fügen Sie den folgenden Code am Ende der Datei
controller / employee.js hinzu. async function del(req, res, next) { try { const id = parseInt(req.params.id, 10); const success = await employees.delete(id); if (success) { res.status(204).end(); } else { res.status(404).end(); } } catch (err) { next(err); } } module.exports.delete = del;
Die JavaScript-Engine löst eine Ausnahme aus, wenn Sie versuchen, eine Funktion namens "delete" mit dem Funktionsoperator zu deklarieren. Um dies zu umgehen, wird eine Funktion mit dem Namen "del" deklariert und dann als "delete" exportiert.
An diesem Punkt sollten Sie in der Lage sein, Logik zu lesen und zu verstehen. Im Gegensatz zu den vorherigen Beispielen hat diese HTTP-Anforderung keinen Textkörper. Im Routenpfad wird nur der Parameter id verwendet. Der Statuscode „204 No Content“ wird häufig bei DELETE-Anforderungen verwendet, wenn der Antworttext nicht gesendet wird.
Um die Datenbanklogik zu vervollständigen,
kehren Sie zur Datei
db_apis / employee.js zurück und fügen Sie am Ende den folgenden Code hinzu.
const deleteSql = `begin delete from job_history where employee_id = :employee_id; delete from employees where employee_id = :employee_id; :rowcount := sql%rowcount; end;` async function del(id) { const binds = { employee_id: id, rowcount: { dir: oracledb.BIND_OUT, type: oracledb.NUMBER } } const result = await database.simpleExecute(deleteSql, binds); return result.outBinds.rowcount === 1; } module.exports.delete = del;
Da die Tabelle JOB_HISTORY eine Fremdschlüsseleinschränkung aufweist, die auf die Tabelle EMPLOYEES verweist, wird ein einfacher PL / SQL-Block verwendet, um die erforderlichen Zeilen in einem einzigen Zyklus aus beiden Tabellen zu entfernen.
Analysieren von JSON-AnforderungenWenn Sie sich die Funktion getEmployeeFromRec in controller / employee.js ansehen, werden Sie feststellen, dass die Eigenschaft des Anfragetexts ein JavaScript-Objekt ist. Dies bietet eine einfache Möglichkeit, Werte aus dem Anforderungshauptteil abzurufen, dies geschieht jedoch nicht automatisch.
Die von Ihnen erstellte API erwartet, dass Clients Daten im JSON-Format im Hauptteil der POST- und PUT-Anforderungen senden. Darüber hinaus sollten Clients den Content-Type-Header der Anforderung auf application / json setzen, damit der Webserver weiß, welcher Typ von Anforderungshauptteil gesendet wird. Sie können die integrierte Middleware express.json verwenden, damit Express solche Anforderungen analysieren kann.
Öffnen Sie die
Datei services / web-server.js und fügen Sie die folgenden Zeilen direkt unter dem Aufruf von app.use hinzu, wodurch der Anforderungspipeline Morgan hinzugefügt wird.
Wenn JSON-Daten in native JavaScript-Objekte analysiert werden, werden nur die in JSON unterstützten Datentypen korrekt JavaScript-Typen zugeordnet. Daten werden in JSON nicht unterstützt und normalerweise als ISO 8601-Zeichenfolgen dargestellt. Mit der an die Express.json-Middleware übergebenen Reviver-Funktion können Sie die Konvertierung manuell durchführen. Fügen Sie den folgenden Code am Ende der
Datei services / web-server.js hinzu .
const iso8601RegExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/; function reviveJson(key, value) {
API-TestsEs ist Zeit, die neue CRUD-Funktionalität zu testen! Bisher haben Sie den Browser zum Testen der API verwendet, dies funktioniert jedoch nicht für POST-, PUT- und DELETE-Anforderungen. Ich werde Ihnen zeigen, wie Sie die API mit Curl-Befehlen testen, da sie in einer virtuellen Maschine leicht zugänglich ist. Sie können jedoch ein grafisches Tool wie
Postman ,
Insomina (kostenlos) verwenden.
Starten Sie die Anwendung, öffnen Sie ein anderes Terminalfenster und führen Sie den folgenden Befehl aus, um einen neuen Mitarbeiter zu erstellen.
curl -X "POST" "http://localhost:3000/api/employees" \ -i \ -H 'Content-Type: application/json' \ -d $'{ "first_name": "Dan", "last_name": "McGhan", "email": "dan@fakemail.com", "job_id": "IT_PROG", "hire_date": "2014-12-14T00:00:00.000Z", "phone_number": "123-321-1234" }'
Wenn die Anforderung erfolgreich war, muss die Antwort ein Mitarbeiterobjekt mit dem Attribut employee_id enthalten. Hier ist ein Beispiel:

In meinem Fall war der Wert von employee_id 227 - Sie müssen die folgenden Befehle basierend auf dem empfangenen Wert von employee_id ändern.
Um beispielsweise einen neuen Eintrag zu aktualisieren, geben Sie PUT für die URL mit diesem Bezeichnerwert ein.
curl -X "PUT" "http://localhost:3000/api/employees/227" \ -i \ -H 'Content-Type: application/json' \ -d $'{ "first_name": "Dan", "last_name": "McGhan", "email": "dan@fakemail.com", "job_id": "AD_PRES", "hire_date": "2014-12-14T00:00:00.000Z", "phone_number": "123-321-1234" }'
Der UPDATE_JOB_HISTORY-Trigger im HR-Schema erkennt eine Jobänderung und fügt der Tabelle JOB_HISTORY eine Zeile hinzu. Wenn Sie sich diese Tabelle ansehen, sollten Sie einen Datensatz für den neuen Mitarbeiter sehen. Führen Sie den folgenden Befehl aus, um den Jobverlauf und die Mitarbeiterdatensätze zu löschen.
curl -i -X "DELETE" "http://localhost:3000/api/employees/227"
Und jetzt haben Sie alles, volle CRUD-Funktionalität!
Die API macht gute Fortschritte, aber es gibt noch viel zu tun. Im
letzten Beitrag werde ich Ihnen zeigen, wie Sie Paginierungs-, Sortier- und Filterfunktionen in GET-Anforderungen hinzufügen.