Angular form 按钮无法提交并通过 POST 重定向
结论
支付网关要求通过 POST 携带数据并跳转页面时,应使用浏览器原生 <form> 提交,不能用 Angular 的 HttpClient.post() 代替。
HttpClient.post() 发起的是 AJAX/XHR 请求。即使服务器返回重定向,浏览器也只会在请求内部处理响应,不会将当前页面导航到支付页面。Angular 的 NgForm、formGroup 和 (ngSubmit) 还可能拦截原生提交,因此外部支付表单应避开 Angular 的表单提交机制。
推荐写法:使用原生表单提交
假设 LiqPay 接口接收 data 和 signature 两个字段,可以这样写:
<form
ngNoForm
method="POST"
action="https://www.liqpay.ua/api/3/checkout"
accept-charset="UTF-8"
>
<input type="hidden" name="data" [value]="data">
<input type="hidden" name="signature" [value]="signature">
<button type="submit">付款</button>
</form>
这段代码需要注意以下几点:
method="POST"表示通过 POST 提交数据。action="..."用于指定支付网关地址。- 浏览器会根据
name="data"和name="signature"生成 POST 参数。控件只有value而没有name时,不会被提交。 type="submit"让按钮执行表单提交。ngNoForm告诉 Angular 不要把该表单转换成NgForm,由浏览器按原生方式提交并跳转。
不要在这个表单上同时使用 (ngSubmit)、[formGroup],也不要给提交按钮绑定 (click) 事件,否则可能重新进入 Angular 的表单处理流程。
如果数据生成后需要立即跳转
可以在代码中创建并提交一个临时原生表单:
redirectToLiqPay(): void {
const form = document.createElement('form');
form.method = 'POST';
form.action = 'https://www.liqpay.ua/api/3/checkout';
form.acceptCharset = 'UTF-8';
const dataInput = document.createElement('input');
dataInput.type = 'hidden';
dataInput.name = 'data';
dataInput.value = this.data;
const signatureInput = document.createElement('input');
signatureInput.type = 'hidden';
signatureInput.name = 'signature';
signatureInput.value = this.signature;
form.appendChild(dataInput);
form.appendChild(signatureInput);
document.body.appendChild(form);
form.submit();
}
然后在模板中用普通按钮调用:
<button type="button" (click)="redirectToLiqPay()">
付款
</button>
如果需要在新窗口中打开支付页面,可以在提交前设置:
form.target = '_blank';
是否允许在新窗口支付,应以支付平台的接入要求为准。
为什么原来的 HttpClient.post() 不会跳转
下面的代码只会在后台请求接口响应:
this._http.post(url, body, options);
它不是浏览器页面提交,所以不会将当前页面导航到 url。原代码还存在以下问题:
- Angular
HttpClient返回的 Observable 通常需要订阅,请求才会实际执行:
this._http.post(url, body, options).subscribe(...);
- 请求体应该使用表单值,而不是
FormGroup对象本身:
this.payForm.value
-
声明
Content-Type: application/json并不表示支付网关支持 JSON。传统支付跳转接口通常接收浏览器表单编码的数据,具体格式应以该接口当前的接入文档为准。 -
前端向第三方域名发起 AJAX 请求时,还可能受到 CORS 限制。浏览器原生表单导航不需要第三方开放 AJAX CORS。
即使补上 subscribe(),这种方式也只适合调用 API,无法实现 POST 后进入支付页面。
注意事项
signature 等支付签名通常应由后端生成。不要将商户私钥、签名密钥或其他机密写入 Angular 前端代码,因为浏览器中的代码和数据都能被用户查看或修改。
更稳妥的流程是:Angular 先向自己的后端请求支付参数,由后端生成 data 和 signature,前端收到参数后,再通过原生表单提交到支付网关。
备注:内容仅供参考。
