取消订单必须用或带event.preventdefault()的,禁用get请求,使用post/delete+csrf校验,添加确认弹窗与按钮状态反馈,并每次进入页面拉取最新订单状态。

点击取消订单前必须拦截默认行为
直接写 @#@#@#@#@#@#@#@#@#@0 是危险的——用户点下去就立刻发请求,没机会反悔,也没法加验证。浏览器会直接跳转或发起 GET 请求,后端若没做幂等或权限校验,可能误取消、重复取消,甚至被爬虫或恶意脚本批量触发。
- 永远用
<button></button>或带onclick的<a></a>,并第一时间调用event.preventDefault() - 取消操作本质是「副作用动作」,语义上不属于导航,
<button type="button"></button>比<a></a>更准确 - 如果仍要用链接样式,CSS 控制外观即可,不要依赖
<a></a>的默认行为
确认弹窗不能只靠 window.confirm()
window.confirm("确定取消?") 看似简单,但无法定制样式、不支持键盘焦点管理、在 iOS Safari 中可能被拦截,且返回值只有 true/false,没法区分“点确定”“点取消”还是“关掉弹窗”。更关键的是,它阻塞主线程,影响可访问性(屏幕阅读器可能跳过)。
- 优先用轻量级自定义 modal,比如一个
<div class="modal"> + <code>display: none/flex切换 - 点击“确定”后再调用取消接口,而不是把接口塞进 confirm 回调里硬编码
- 务必为按钮添加
aria-label和role="dialog"等基础可访问属性 - 示例结构:
<div class="cancel-modal" aria-hidden="true"> <p>订单将不可恢复,请确认</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill9263" title="Html2md"><img src="https://img.php.cn/upload/skill/000/000/081/179249945220127.jpg" alt="Html2md" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/skill9263" title="Html2md" class="overflowclass">Html2md</a> <p class="overflowclass">使用 Readability + Turndown 将 HTML 页面转换为干净的、Agent 友好的 Markdown。去除导航栏、广告、页脚、Cookie 横幅、社交按钮等干扰元素。支持 URL...</p> </div> <a rel="nofollow" href="/xiazai/skill9263" title="Html2md" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div> <button id="confirm-cancel">确定取消</button> <button id="cancel-close">再想想</button> </div>
- 后端路由必须限定为
POST或DELETE,前端用fetch()或XMLHttpRequest显式指定方法 - 带上一次性
csrf_token字段(如隐藏 input 或 header),后端严格校验 - 接口返回明确状态码:
200表示成功,403表示无权限,409表示订单已不可取消(如已发货) - 示例 fetch 调用:
fetch('/api/order/123/cancel', { method: 'POST', headers: { 'X-CSRF-Token': document.querySelector('[name=csrf_token]').value }, body: JSON.stringify({ reason: '地址填错' }) }) - 点击后立即将按钮设为
disabled,文字改为“取消中…” - 用
AbortController控制 fetch 请求,用户关闭弹窗时主动中止 - 成功后不仅要更新 UI(如显示“已取消”标签),还要禁用所有相关操作按钮(如“再次购买”需等页面刷新后才可用)
- 失败时恢复按钮状态,并展示具体错误原因(如
"订单状态不允许取消"),而不是笼统的“操作失败”
取消请求必须用 POST/DELETE,禁用 GET
把取消逻辑放在 GET 接口里(如 /api/order/123/cancel?_method=DELETE)是常见错误。GET 请求可被缓存、预加载、记录在服务器日志甚至代理日志中,攻击者只需构造一个图片链接就能触发取消:<img src="/api/order/123/cancel">。
按钮状态要即时反馈,避免重复提交
用户手快连点两次,或者网络慢导致没看到响应,就可能发出多个取消请求。后端虽可幂等处理,但前端该拦住的就得拦住——这不是“锦上添花”,而是防止用户困惑和数据不一致的基本线。
最易被忽略的一点:取消操作不是原子的。从点击、弹窗、确认、发请求、收响应、改 DOM,中间任何一环出问题,UI 状态都可能和后端不一致。所以每次进入订单页,都应该拉一次最新状态,而不是依赖本地缓存或上一步的假设。










