第4部分:创建REST API:处理POST,PUT和DELETE请求在
上一篇文章中,您向用于从数据库检索数据的GET请求的API添加了逻辑。 在本文中,您将通过添加处理POST,PUT和DELETE请求的逻辑来完成CRUD API的基本功能。
添加路由逻辑为了简化路由逻辑,您将通过现有路由(带有可选的id参数)重定向所有HTTP方法。 打开
services / router.js文件,并用以下代码替换当前的路由逻辑(第5-6行):
router.route('/employees/:id?') .get(employees.get) .post(employees.post) .put(employees.put) .delete(employees.delete);
更新后的路由逻辑将用于基本CRUD操作的四种最常见的HTTP方法映射到正确的控制器逻辑。
POST请求处理HTTP POST请求用于创建新资源(在这种情况下为员工记录)。 主要思想是从HTTP请求的正文中提取数据,并使用它在数据库中创建新行。 要为POST请求添加控制器逻辑,请打开
controllers / employee.js文件并添加以下代码:
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;
getEmployeeFromRec函数接受一个请求对象,并返回一个具有创建雇员记录所必需的属性的对象。 该函数在post函数外部声明,因此以后可以用于PUT请求。
post函数使用getEmployeeFromRec初始化变量,然后将其传递给员工数据库API的create方法。 创建操作之后,状态代码“ 201已创建”与员工JSON(包括员工标识符的新值)一起发送到客户端。
现在,您可以注意数据库API中的逻辑。 打开
db_apis / employee.js文件,并在下面添加以下代码。
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;
上面的逻辑首先声明一个名为createSql的常量来存储insert语句。 请注意,它使用
绑定变量 (而不是字符串连接)来引用要插入的值。 出于安全和性能的原因,值得重复绑定变量的重要性。 尽可能避免使用字符串连接。
在create函数内部,使用Object.assign为emp参数的副本定义并初始化了雇员常量。 这样可以防止直接修改从控制器传送来的对象。
然后,将employee_id属性添加到employee对象(配置为“ out bind”),以便它包含执行SQL语句所需的所有绑定变量。 然后,使用simpleExecute函数执行insert语句,并使用outBinds属性在返回对象之前覆盖employee.employee_id属性。
由于存在指向oracledb模块的链接,因此需要在文件顶部添加以下行。
const oracledb = require('oracledb');
PUT请求处理PUT请求将用于更新现有资源。 重要的是要注意,PUT用于替换整个资源-它不会执行部分更新(以后我将向您展示如何使用PATCH进行更新)。 返回
controllers / employee.js文件,并在下面添加以下代码。
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;
put函数使用getEmployeeFromRec初始化一个名为employee的对象,然后将id参数中的employee_id属性添加到URL。 然后将雇员对象传递给数据库API更新功能,并根据结果将相应的响应发送给客户端。
要将更新逻辑添加到数据库API,请将以下代码添加到
db_apis / employee.js文件。
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;
更新逻辑与创建逻辑非常相似。 声明一个变量以存储SQL语句,然后使用simpleExecute来执行带有已传输动态值的语句(将它们复制到另一个对象之后)。 result.rowsAffected用于确定更新是否成功并返回正确的值。
删除请求处理您需要实现的最后一个方法是DELETE,毫不奇怪,它将从数据库中删除资源。 在
controllers / employee.js文件的末尾添加以下代码
。 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;
如果您尝试使用函数运算符声明一个称为“删除”的函数,则JavaScript引擎将引发异常。 为了解决这个问题,声明了一个名为“ del”的函数,然后将其导出为“ delete”。
此时,您应该能够阅读和理解逻辑。 与前面的示例不同,此HTTP请求没有正文;仅id参数用于路由路径中。 当未发送响应正文时,状态代码“ 204 No Content”通常用于DELETE请求。
要完成数据库逻辑,请返回
db_apis / employee.js文件,并将以下代码添加到末尾。
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;
因为JOB_HISTORY表具有引用EMPLOYEES表的外键约束,所以使用一个简单的PL / SQL块可以在一个周期内从两个表中删除必要的行。
解析JSON请求如果查看controllers / employee.js中的getEmployeeFromRec函数,您会注意到请求正文属性是一个JavaScript对象。 这提供了一种从请求正文获取值的简便方法,但这不会自动发生。
您创建的API要求客户端在POST和PUT请求的主体中以JSON格式发送数据。 此外,客户端应将请求的Content-Type标头设置为application / json,以便Web服务器知道发送的请求主体类型。 您可以使用express.json内置的中间件,以便Express可以解析此类请求。
打开
services / web-server.js文件,并在app.use调用下方添加以下行,这会将morgan添加到请求管道。
将JSON数据解析为本机JavaScript对象时,只有JSON支持的数据类型才能正确映射到JavaScript类型。 JSON不支持日期,通常以ISO 8601字符串表示日期。使用传递给express.json中间件的reviver函数,您可以手动执行转换。 将以下代码添加到
services / web-server.js文件的末尾 。
const iso8601RegExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/; function reviveJson(key, value) {
API测试现在是时候测试新的CRUD功能了! 到目前为止,您已经使用浏览器测试了API,但这不适用于POST,PUT和DELETE请求。 我将向您展示如何使用curl命令测试API,因为它可以在虚拟机中轻松访问。 但是您可以使用图形工具,例如
Postman ,
Insomina (免费)。
启动应用程序,然后打开另一个终端窗口并运行以下命令以创建新员工。
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" }'
如果请求成功,则响应必须包含一个带有employee_id属性的雇员对象。 这是一个例子:

在我的情况下,employee_id的值为227-您将需要根据收到的employee_id的值更改以下命令。
例如,要更新新条目,请使用此标识符值为URL输入PUT。
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" }'
HR模式中的UPDATE_JOB_HISTORY触发器将检测到作业更改并将行添加到JOB_HISTORY表中。 如果查看此表,应该会看到新员工的记录。 运行以下命令以删除作业历史记录和员工记录。
curl -i -X "DELETE" "http://localhost:3000/api/employees/227"
现在,您拥有了所有功能,并且具有完整的CRUD功能!
API进展顺利,但仍有工作要做。 在
上一篇文章中,我将向您展示如何在GET请求中添加分页,排序和过滤功能。