原 荐 HTTP 重定向: 你可能不知道的故

在 Spring 里面我们要重定向的话一般都会这样做:

@Controller
final class RedirectTestController {

    @RequestMapping(value = "/foo")
    String foo() {
        return "foo";
    }

    @RequestMapping(value = "/bar")
    String bar() {
        return "redirect:/foo";
    }
}

应用遇到发送到 /bar/ 的请求被重定向到 /foo. 很简单,不是吗?

现在问题来了, 上面的代码能够在 /bar 上除了能够处理 GET 请求之外, 还能处理其他方法, 比如 POST /bar. 那如果用户发起一个 POST /bar 的请求, 会有什么效果呢? 我们把代码稍微改一下:

@Controller
final class RedirectTestController {

    @RequestMapping(value = "/foo", method = RequestMethod.GET)
    String getFoo() {
        return "get:foo";
    }

    @RequestMapping(value = "/foo", method = RequestMethod.POST)
    String postFoo() {
        return "post:foo";
    }

    @RequestMapping(value = "/bar")
    String bar() {
        return "redirect:/foo";
    }
}

然后使用 PostMan 工具来尝试向 /bar 发起一个 POST 请求. 结果发现返回的是 get:foo, 而不是 post:foo. 检查返回头发现 Spring 处理 redirect: 的时候返回的状态是 HTTP 302 Found. 读一下 HTTP 标准 会发现重定向居然有 4 种不同的响应状态:

  • 301 Moved Permanently
  • 302 Found
  • 303 See Other
  • 307 Temporary Redirect

这究竟是什么鬼, 怎么一个小小的重定向居然会定义出 4 种东东? 那我们下面就来讲讲 HTTP 标准中重定向的故事: 其实最早重定向的返回方式只有 301 Moved Permanently 和 302 Found; 301 的标准定义是请求资源已经有了一个新的 URL,以后对该资源的请求都应该使用新 URL, 这个很容易理解就不多费口舌了. 有趣的故事在 302 上面.

302 是一个业界和标准冲突的典型案例, HTTP 1.0 对 302 的定义 "Moved Temporarly" 是后来的 307 Temporary Redirect, 而几乎所有的浏览器对 302 的实现都是按照后来的 303 See Other 方式实现. 那 307 和 303 之间又有什么差别呢?

不需去看标准里面公文化的描述, 简单地说 307 的语义是当一开始请求资源的方法是 POST 或者 PUT 的时候, 重定向之后重新请求资源还是应该为 POST/PUT, 即保持原有方法. 而 303 的语义则是不管原来的方法是什么, 重新请求资源的方法都是 GET, 而这就是我们文中最初引入的 Spring 重定向例子表现出来的结果.

就是因为业界和标准之间对 302 的定义和实现之间不一致, HTTP 1.1 标准后来干脆明确定义了 303 See Other307 Temporary Redirect, 这样可以很清楚的表达上面两种语义. 而 302 重新定义为 "Found" 作为遗留结果 (标准对业界低头) 一直存在到现在, 可以说 302 是事实上的 303. 这也是为什么 Spring 这样的框架将 redirect 解释为 302 返回的原因了.

那为什么会普遍需要 303 See Other 这种语义呢? 其实一个很常见的做法是当用户使用 POST 请求提交表单之后, 服务器返回一个 302 Found 响应, 重定向到提交结果, 这样在很大程度上可以防止用户重复刷新表单带来的重复提交的问题. 因为当 POST 请求返回之后浏览器已经自动提交了一个 GET 请求到新的结果页面,即使用户再次按 F5 刷新页面,也不会重复提交表单数据了.

现在问题来了,如果项目中真的遇到了需要 307 Temporary Redirect 的情况, 即 POST 请求的 URL 变化了, 需要发起新的 POST 请求, 该怎么处理呢? 还有当 ajax 请求需要被重定向的时候又该如何处理呢? 且听下回分解 ...

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏BaronTalk

写给 Android 应用工程师的 Binder 原理剖析

这篇文章我酝酿了很久,参考了很多资料,读了很多源码,却依旧不敢下笔。生怕自己理解上还有偏差,对大家造成误解,贻笑大方。又怕自己理解不够透彻,无法用清晰直白的文字...

55710
来自专栏程序猿DD

Spring Security (二) Guides

上一篇文章《Spring Security(一)--Architecture Overview》,我们介绍了Spring Security的基础架构,这一节我们...

2286
来自专栏运维

ELK日志分析系统搭建部署

  日志监控和分析在保障业务稳定运行时,起到了很重要的作用,不过一般情况下日志都分散在各个生产服务器,且开发人员无法登陆生产服务器,这时候就需要一个集中式的日志...

1082
来自专栏玩转JavaEE

JavaWeb之最简洁的配置实现文件上传

按:最近公众号文章主要是整理一些老文章,主要是个人CSDN上的博客,也会穿插一些新的技术点。 ---- Spring、SpringMVC持续介绍中,基础配置前面...

2643
来自专栏Linyb极客之路

使用Spirng Boot Admin监控Spring Cloud应用项目

Spring Boot Admin 是一个管理和监控Spring Boot 应用程序的开源软件。每个应用都认为是一个客户端,通过HTTP或者使用 Eureka注...

1271
来自专栏思考的代码世界

Mac搭建yaf项目

1250
来自专栏Kirito的技术分享

Spring Security(二)--Guides

上一篇文章《Spring Security(一)--Architecture Overview》,我们介绍了Spring Security的基础架构,这一节我们...

29511
来自专栏安恒信息

安全漏洞公告

1 McAfee Email Gateway存在未明任意命令执行漏洞 ? 2 Zend Framework代理请求处理IP地址伪造漏洞 ? 3Cisco ASA...

27110
来自专栏Rainbond开源「容器云平台」

【Rainbond最佳实践】Spring Boot框架配置MySQL

1002
来自专栏编程思想之路

linux下Android7.0多用户编译问题

0.0 WHY linux下多用户使用open-jdk8编译时会有jack-server的问题。首先要明白为什么会出现这个问题,只有明白了原因,才能对症下药。注...

4257

扫码关注云+社区