做网站服务器的配置诚信网站建设的意义
2026/3/24 17:57:00 网站建设 项目流程
做网站服务器的配置,诚信网站建设的意义,电商网站计划,番禺网站推广面试官#xff1a;post 为什么会发送两次请求#xff1f; ——这个问题真的是面试高频#xff0c;又容易翻车。 你想象一下哈。 你在面试#xff0c;写了个很标准的 Spring Boot Controller#xff1a; RestController RequestMapping(/user) public clas…面试官post 为什么会发送两次请求——这个问题真的是面试高频又容易翻车。你想象一下哈。你在面试写了个很标准的 Spring Boot ControllerRestController RequestMapping(/user) public class UserController { PostMapping(/save) public String save(RequestBody UserDTO user) { System.out.println(save user user); return ok; } }本地调试的时候你打开浏览器的 Network 面板或者看后端日志发现一个很诡异的事你明明只点了一次“保存” 结果浏览器 Network 里出现了两条记录后端日志里也像是执行了两次这时候面试官一句话丢过来“你说说POST 为啥会发两次”如果只回答一句“网络不好重试了”基本直接凉。这个问题背后其实大概就那几种情况只要搞清楚了回答起来就很顺。场景一其实只算一次 —— CORS 预检把你吓到了最常见的误会OPTIONS POST 被你当成“POST 调了两次”。浏览器在跨域、且请求“比较复杂”的时候会先发一个OPTIONS请求问问后端“哥们这个真正的 POST 我能不能发”流程是这样的浏览器先发OPTIONS /user/save后端返回一堆Access-Control-Allow-*头浏览器确认“没问题”再发真正的POST /user/save所以 Network 面板里会看到两条记录但真正的业务 Controller 只会命中一次OPTIONS 通常不会走你的业务逻辑。在 Spring Boot 里如果你打开了全局 CORS大概长这样Configuration publicclass CorsConfig { Bean public WebMvcConfigurer corsConfigurer() { returnnew WebMvcConfigurer() { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOrigins(http://localhost:3000) .allowedMethods(GET, POST, PUT, DELETE) .allowedHeaders(*) .allowCredentials(true); } }; } }想确认是不是这个原因很简单看 Network 里的 Method是不是一条 OPTIONS一条 POST后端给 Controller 打个日志只会进一次就是 CORS 预检面试的时候可以怎么说很多同学看到浏览器有两条记录就以为 POST 调了两次其实一条是 CORS 预检的 OPTIONS一条才是真正的 POST这种场景业务只会执行一次。这一句说清楚面试官一般会点点头。场景二真的发了两次 —— 重定向搞的鬼第二种非常常见的原因是重定向redirect。举个很典型的坑你有个接口/order/create没登录时会被网关或者 Spring Security 拦截返回 302 跳到/login。流程变成浏览器发POST /order/create服务端返回302 Location: /login浏览器自动再发一个GET /login这时候你在 Network 里看到两条记录一条POST /order/create 302一条GET /login 200严格说只有一个 POST但如果是“POST - POST”的重定向就真有可能业务被打到两次。比如你在 Nginx 里写了比较奇怪的 rewrite把/order重写成了/order/或者从 HTTP 重定向到 HTTPS配置不当的时候会出现POST http://xxx/api/order/create301 跳转到https://xxx/api/order/create浏览器再次发请求有些浏览器/代理在 301/302 时会把 POST 变成 GET有些在 307/308 会保留原来的方法这就很有讲头了。你可以顺手提一句如果服务端返回的是 307 / 308浏览器会保留原来的方法和 body这时候就可能出现两次 POST 请求所以线上做跳转时要小心这些状态码的使用。场景三客户端 / 网关的“好心重试”第三种就是各种“自动重试机制”。常见几类网关 / 负载均衡Nginxproxy_next_upstream配置了超时/错误重试某些 API 网关默认帮你重试一次HTTP 客户端自己重试OkHttp 默认就有retryOnConnectionFailure(true)Feign 可以配置Retryer自己封装的 RestTemplate 可能也加了重试逻辑一个简单的 Feign 重试配置示例Configuration public class FeignRetryConfig { Bean public Retryer feignRetryer() { // period100ms, maxPeriod1s, maxAttempts3 return new Retryer.Default(100, TimeUnit.SECONDS.toMillis(1), 3); } }如果下游服务第一次稍微慢一点或者偶发抖动Feign 觉得“有点不太对我再试一次” 于是你后端就看到了两次 POST。这类情况的特点两次请求的 body 完全一样时间间隔非常短几十毫秒到几百毫秒第一次可能是超时 / 5xx第二次成功排查的时候除了应用日志一定要看Nginx / 网关日志很多“鬼畜请求”都藏在那里面。场景四前端真点了两次或帮你点了两次这个就比较接地气了。最常见的几个用户手速太快按钮双击回车提交 点击提交页面做了自动重试或者某种“点击即重发”的逻辑前端没做节流/防抖输入框变更就发 POST最朴素的示例反面教材button idsubmitBtn提交/button script document.getElementById(submitBtn).onclick function () { fetch(/user/save, { method: POST, body: JSON.stringify({ name: Tom }), headers: { Content-Type: application/json } }); }; /script啥防护也没有用户双击一下直接两次 POST后端根本没法分辨是不是同一次操作。稍微规范一点的写法会在第一次点击后把按钮禁用const btn document.getElementById(submitBtn); btn.onclick function () { if (btn.disabled) { return; } btn.disabled true; fetch(/user/save, { method: POST, body: JSON.stringify({ name: Tom }), headers: { Content-Type: application/json } }).finally(() { btn.disabled false; // 看业务决定要不要恢复 }); };面试的时候可以顺带提一句“前端也要配合做防重复提交”给人感觉你是站在全链路视角看问题的。核心补救POST 要配上“幂等性保险”上面这些情况有的是“看起来发了两次其实没问题”比如 CORS 有的是真发送了两次。那真正线上要紧的是就算发了两次也不能让业务乱套。这就绕不过一个词幂等性—— 同一个请求重复执行多次结果应该是一样的。POST 默认不是幂等的所以要自己加“保险”。比较常见的几种做法1. 用业务唯一键做幂等比如支付、下单这种通常都有一个业务唯一号比如orderNo。服务端可以用 Redis / 数据库做一次“抢占”谁抢到了谁执行Service publicclass PayService { privatestaticfinal String IDEMPOTENT_KEY_PREFIX pay:order:; Resource private StringRedisTemplate stringRedisTemplate; public void pay(String orderNo) { String key IDEMPOTENT_KEY_PREFIX orderNo; // setIfAbsent true 说明是第一次处理 Boolean success stringRedisTemplate.opsForValue() .setIfAbsent(key, 1, Duration.ofMinutes(10)); if (Boolean.FALSE.equals(success)) { // 说明已经处理过这笔支付直接返回不再重复扣款 System.out.println(orderNo orderNo 已处理过拒绝重复支付); return; } // 真正的扣款逻辑 doPay(orderNo); } private void doPay(String orderNo) { // 调用三方支付、更新订单状态、落库等等…… System.out.println(执行真正的支付逻辑, orderNo orderNo); } }哪怕网关、客户端帮你把同一笔支付请求重试了好几次只要orderNo相同这个方法也只会真正执行一次。2. 用一次性“幂等 token”再举个更“通用”的 POST 场景可能没天然的业务唯一键可以走幂等 token方案前端在提交前先向后端要一个token真正提交时把token带上后端把token当 keysetIfAbsent一次重复提交时setIfAbsent失败直接返回“重复请求”伪代码示意RestController RequestMapping(/idempotent) publicclass IdempotentController { Resource private StringRedisTemplate stringRedisTemplate; GetMapping(/token) public String generateToken() { String token UUID.randomUUID().toString(); stringRedisTemplate.opsForValue() .set(idem:token: token, 1, Duration.ofMinutes(5)); return token; } PostMapping(/submit) public String submit(RequestHeader(Idempotent-Token) String token, RequestBody SubmitDTO dto) { String key idem:token: token; Boolean success stringRedisTemplate.opsForValue() .setIfAbsent(key, used, Duration.ofMinutes(5)); if (Boolean.FALSE.equals(success)) { return重复请求; } // 真正的业务逻辑 handleBusiness(dto); returnok; } private void handleBusiness(SubmitDTO dto) { System.out.println(处理业务: dto); } }这样哪怕浏览器帮你“多按了几次”同一个 token 也只能成功一次。真遇到线上“POST 两次”怎么排查如果是实战而不是纸上谈兵我一般会这么干你可以照着改成自己的话术先看浏览器 NetworkMethod 是 OPTIONS POST还是两条 POST有没有 301/302/307/308 这种跳转两次请求的 URL、参数、body 是否完全一样看网关 / Nginx 日志一条请求是否被转发到了多个上游有没有重试 / upstream timed out 之类的记录看应用层日志比如用 Spring 的过滤器简单加个 requestIdComponent publicclass LogFilter implements Filter { Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String requestId UUID.randomUUID().toString().replace(-, ); MDC.put(requestId, requestId); try { HttpServletRequest req (HttpServletRequest) request; System.out.println(requestId requestId , req.getMethod() req.getRequestURI()); chain.doFilter(request, response); } finally { MDC.remove(requestId); } } }给每个请求加一个requestId或 traceId打印在入口和核心业务逻辑里确认业务代码是不是确实走了两遍最后看配置Feign / OkHttp / RestTemplate 有没有配置重试网关有没有“自动重试一次”的选项前端有没有防重复提交排查完你基本可以非常有底气地告诉别人这两次 POST 到底从哪儿来的。https://mp.weixin.qq.com/s/9vVePfiEDeE_Ef3Qgx3Cvw

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询