首页 > 解决方案 > 使用 Express.js 和 mysql 处理 PATCH 请求

问题描述

假设我有以下路线:

POST /users其中所有字段都是必需的,这会将用户添加到我的数据库中,假设这个用户获得id1

{
    "firstName": "FirstName",
    "lastName": "LastName",
    "email": "email@example.com",
    "address": "address"
}

我还有GET /users显示数据库中所有用户的路线。

[
    {
        "id": 1,
        "firstName": "FirstName",
        "lastName": "LastName",
        "email": "email@example.com",
        "address": "address"
    }
]

现在,我正在尝试更新特定用户的路线,PATCH /users.

我想让所有字段都是可选的,并且只发送我通过 PATCH 路由更新的字段,并且id将从 JWT 令牌中解码。

重要的是要注意数据显示在前端的表单中,我想更新表单,但只发送从表单而不是整个表单更新的值。实际的表单有 30 多个字段。

我知道如果我使用PUT请求,我可以发送表单中的所有值并执行以下操作:

editUser = async ({ firstName, lastName, email, address }) => {
    let sql = `UPDATE users
    SET firstName = ?, lastName = ?, email=?, address=? WHERE id = ?`;

    const result = await query(sql, [firstName, lastName, email, address, userId ]);

    return result;
}

现在,假设我只更新表单中的电子邮件并发送它。例如: PATCH /users

{
    "email":"email@gmail.com"
}

使用以下方法也很容易:

editUser = async ({ email }) => {
    let sql = `UPDATE users
    SET email = ? WHERE id = ?`;

    const result = await query(sql, [email, userId]);

    return result;
}

PATCH以上只是电子邮件的好方法。

所以现在当我这样做时,GET /users我得到以下信息:

[
    {
        "id": 1,
        "firstName": "FirstName",
        "lastName": "LastName",
        "email": "email@gmail.com",
        "address": "address"
    }
]

但是如何处理我不知道哪些密钥被发送到后端的不同情况?例如,一组可以如下: PATCH /users

{
    "lastName": "updatedLastName"
    "email":"email@gmail.com"
}

在实践中,上述可以是对象中所有不同键的任意组合。什么是根据键生成自定义查询并仅使用需要更新的列更新表中的行的好方法?

标签: mysqlnode.jsapiexpress

解决方案


我没有比这些更好的主意:

使用 forEach:

const buildPatchQuery = (table, id, data) => {
    if (Object.keys(data).length === 0) return null; // Or return what you want
    let sql = `UPDATE ${table} SET`;
    Object.entries(data).forEach(([key, value]) => {
        const valueToSet = typeof data[key] === 'string' ? `'${value}'` : value;
        sql += ` ${key}=${valueToSet},`;
    });
    sql = sql.slice(0, -1); // Remove last ","
    sql += ` WHERE id=${id};`;
    return sql;
}

用地图:

const buildPatchQuery = (table, id, data) => {
    if (Object.keys(data).length === 0) return null; // Or return what you want
    let query = `UPDATE ${table} SET `;
    query += Object.keys(data).map((key) => {
        const valueToSet = typeof data[key] === 'string' ? `'${data[key]}'` : data[key];
        return `${key}=${valueToSet}`;
    }).join(', ');
    return query + ` WHERE id=${id};`;
}

减少:

const buildPatchQuery = (table, id, data) => {
    if (Object.keys(data).length === 0) return null; // Or return what you want
    let query = `UPDATE ${table} SET`;
    query += Object.entries(data).reduce((acc, [key, value], index, array) => {
        const valueToSet = typeof data[key] === 'string' ? `'${value}'` : value;
        const optionalComma = index < array.length-1 ? ',' : '';
        return `${acc} ${key}=${valueToSet}${optionalComma}`;
    }, '');
    return query + ` WHERE id=${id};`;
}

通过使用您的 SQL 库:

const buildPatchQuery = (table, id, data) => {
    if (Object.keys(data).length === 0) return null; // Or return what you want
    let sql = `UPDATE ${table} SET`;
    const newValues = [];
    Object.entries(data).forEach(([key, value]) => {
        sql += ` ${key}=?,`;
        newValues.push(value);
    });
    sql = sql.slice(0, -1); // Remove last ","
    sql += ' WHERE id=?;';
    newValues.push(id);
    return query(sql, newValues);
}

经测试:

console.log(buildPatchQuery('users', 1, {name: 'toto', email: 'truc', age: 18}));
console.log(buildPatchQuery('users', 1, {name: 'toto'}));
console.log(buildPatchQuery('users', 1, {}));

(未通过 SQL 库测试)

但是,请务必检查data对象并通过检查身份验证和权限来保护您的路线,以避免 SQL 注入或任何黑客攻击!


推荐阅读