【问题标题】:Express redirect POST to PUT with same URL使用相同的 URL 将 POST 快速重定向到 PUT
【发布时间】:2018-08-05 04:52:37
【问题描述】:

在我的快递应用中,我有两条路线如下:

router.post('/:date', (req, res) => {

    // if date exists, redirect to PUT
    // else add to database

})

router.put('/:date', (req, res) => {

    // update date

})

如果date 已经存在于POST 呼叫中,我想重定向到PUT。使用res.redirect 执行此操作的最佳方法是什么?

docs 中,所有重定向都指向不同的 URL 模式。我想保持 URL 不变,并将其余动词从 POST 更改为 PUT

我查看了this SO question,并在POST 中添加了这一行:

res.redirect(303, '/:date');

但它没有将我重定向到PUT

【问题讨论】:

  • 我会将 post 和 put 处理程序的主要逻辑提取为命名函数,然后根据条件从 post 或 put 处理程序中调用相应的函数。无需重定向
  • 没错。这就是我最终做的。我只是好奇是否有办法做到这一点。谢谢

标签: javascript node.js rest express routes


【解决方案1】:

由于多种原因,您在此处尝试执行的操作将不起作用,但您无需执行任何操作 - 请参见下文。

第一个问题

根据规范,您在此处使用的 303“查看其他”重定向应始终后跟 GET(或 HEAD)请求,而不是 PUT 或其他任何内容。请参阅 RFC 7231,第 6.4.4 节:

相关部分:

303 (See Other) 状态码表示服务器是 将用户代理重定向到不同的资源,如 Location 标头字段中的 URI,旨在提供 对原始请求的间接响应。 用户代理可以执行 针对该 URI 的检索请求(如果使用,则为 GET 或 HEAD 请求 HTTP),也可能被重定向,并呈现最终结果 作为对原始请求的回答。请注意,新的 URI 在 Location头域不被认为等同于有效 请求 URI。 [强调]

第二个问题

其他流行的重定向类型 - 301“永久移动”和 302“找到”在实践中通常与规范相反就像它们是 303“查看其他”一样,因此 GET 请求是制作。

参见维基百科上的List of HTTP status codes

这是与标准相矛盾的行业实践的示例。 HTTP/1.0 规范 (RFC 1945) 要求客户端执行临时重定向(最初的描述短语是“临时移动”),但流行的浏览器使用 303 See Other 的功能实现了 302。因此,HTTP/1.1 增加了状态码 303 和 307 来区分这两种行为。但是,一些 Web 应用程序和框架使用 302 状态代码,就好像它是 303。[强调添加]

第三个问题

有一个 307 临时重定向(从 HTTP/1.1 开始),但它明确禁止更改 HTTP 方法,因此您只能将 POST 重定向到 POST、将 PUT 重定向到 PUT 等,这有时很有用,但在这个案例 - 参见维基百科:

在这种情况下,应该使用另一个 URI 重复请求;但是,未来的请求仍应使用原始 URI。与历史上 302 的实现方式相比,在重新发出原始请求时,不允许更改请求方法。例如,应该使用另一个 POST 请求重复一个 POST 请求。

这个 307 重定向仍然不是您想要的,即使它是,据我所知,它并没有得到普遍支持,因此需要谨慎使用。

有关更多信息,另请参阅此答案:

您的选择

您可以抽象出您的控制器 - 对于任何复杂的事情,您通常都会这样做:

// controllers - usually 'required' from a different file 
const update = (req, res) = {
  // update date
};
const add = (req, res) => {
  if (date exists) {
    return update(req, res);
  }
  // add to database
};

router.post('/:date', add);
router.put('/:date', update);

或者您可以将控制器的部分抽象为函数。

通用控制器

另外,请注意,您可以编写通用控制器,调用每个可能在这里工作的 HTTP 方法:

router.use('/:date', (req, res) => {
});

休息

请注意,您在此处所做的不是通常的 RESTful 命名路径的方式,在您的情况下仅对新日期和更新日期使用 PUT 可能是有意义的。

与许多人的想法相反PUT 并不意味着更新。这意味着将资源(新的或不是的)放到某个 URL(如果它已经存在,则覆盖旧的)。这很像在 shell 中写这个:

echo abc > /the/path/to/file.txt

如果文件存在,它将“更新”文件,但如果不存在,它也会创建一个新文件。

例如,如果您有 /users/:id 路径,那么您使用:

  • GET /users 获取用户列表
  • GET /users/:id 以获取具有该 ID 的特定用户
  • POST /user (not /users/:id) 以创建新用户而不提供 ID
  • PUT /users/:id 覆盖现有用户或创建提供 ID 的新用户
  • PATCH /users/:id 以更新具有该 ID 的用户提供的字段

在这里,据我了解,您的 :date 就像一个 ID,即。如果记录已存在,则要覆盖该记录,如果不存在则创建。 在这两种情况下,您都提供了 :date 路径组件,因此您也可以在所有情况下使用 PUT。

换句话说,您不能从一种 HTTP 方法重定向到另一种 HTTP 方法(GET 除外),但在这种情况下您不需要这样做。

【讨论】:

  • 是的,是的,在最后一个“REST”点上,。 OP 应该直接使用PUT。事实上,我什至会说 PUT 应该始终是“创建”的默认值,如果自然或客户端确定的密钥是可能的,并且仅在 PUT 不可行时使用非幂等 POST。跨度>
  • @Evert 我同意 100%。从字面上看,半小时前我在 Medium 上读到 an article 很少有人评论说 PUT 总是 UPDATE (甚至提供助记符,比如 PUT = Please Update This 等)人们总是在传播这个口头禅,然后每个人都会得到错误的印象PUT 无法创建资源,当不需要重定向任何内容或共享控制器代码时,它会导致类似这个问题的混乱,因为它是具有单个 HTTP 方法的单个控制器的完美用例。
  • 非常感谢您提供详细的答案和其他信息!您提供的第一个选项最适合我的具体情况。
猜你喜欢
  • 2016-01-17
  • 2017-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-03
相关资源
最近更新 更多