PAYATHON 2026

Django view 如何携带额外信息重定向到其他域名?

支付老李

结论

Django 可以直接重定向到其他域名,但重定向本身不能附带 JSON 数据。

浏览器收到重定向响应后,会根据 Location 响应头访问目标地址。如果对方接收 GET 参数,需要把数据编码到 URL 查询字符串中;如果对方要求 POST 数据,则要提交表单,普通重定向无法做到这一点。

使用查询参数重定向

可以使用 Python 标准库中的 urllib.parse.urlencode() 编码参数,不必手动拼接 &、=,也不用单独处理特殊字符。

from decimal import Decimal
from urllib.parse import urlencode

from django.http import HttpResponseRedirect


def payment_view(request):
    target_url = "https://ssl.dotpay.pl/test_payment/"

    params = {
        "id": "123456",
        "amount": Decimal("123.00"),
        "description": "Test",
    }

    redirect_url = f"{target_url}?{urlencode(params)}"
    return HttpResponseRedirect(redirect_url)

浏览器会跳转到:

https://ssl.dotpay.pl/test_payment/?id=123456&amount=123.00&description=Test

也可以使用 redirect():

from django.shortcuts import redirect
from urllib.parse import urlencode


def payment_view(request):
    params = {
        "id": "123456",
        "amount": "123.00",
        "description": "Test",
    }

    url = "https://ssl.dotpay.pl/test_payment/?" + urlencode(params)
    return redirect(url)

urlencode() 能正确处理空格、中文和其他特殊字符。例如:

urlencode({"description": "测试订单 A"})

这会生成经过 URL 编码的查询字符串。

将 URL 构造移出 view

如果不想在 view 中构造 URL,可以把相关逻辑放进独立函数:

from urllib.parse import urlencode


PAYMENT_URL = "https://ssl.dotpay.pl/test_payment/"


def build_payment_url(payment):
    params = {
        "id": payment.external_id,
        "amount": format(payment.amount, ".2f"),
        "description": payment.description,
    }

    return f"{PAYMENT_URL}?{urlencode(params)}"

view 只需负责调用:

from django.shortcuts import redirect


def payment_view(request, payment_id):
    payment = Payment.objects.get(pk=payment_id)
    return redirect(build_payment_url(payment))

这样写能让 view 更简洁,参数生成逻辑也可以单独测试。

为什么不能直接通过 JSON 重定向

普通 HTTP 重定向通常使用 302 或 303 状态码,并带有类似这样的响应头:

Location: https://ssl.dotpay.pl/test_payment/?id=123456&amount=123.00

重定向响应没有通用机制让浏览器在访问新地址时额外提交一段 JSON。即使 Django 把 JSON 写进重定向响应体,目标网站也不会自动收到这些数据。

下面的代码不会把 JSON 传给目标域名:

from django.http import JsonResponse


def payment_view(request):
    return JsonResponse({
        "redirect_url": "https://ssl.dotpay.pl/test_payment/",
        "id": "123456",
    })

它只会把数据返回给当前请求的调用方。除非前端 JavaScript 读取 JSON 并自行发起请求,否则浏览器不会跳转。

目标接口要求 POST 时

如果支付平台要求通过表单字段接收参数,可以返回一个自动提交的 HTML 表单:

from django.shortcuts import render


def payment_view(request):
    context = {
        "target_url": "https://ssl.dotpay.pl/test_payment/",
        "fields": {
            "id": "123456",
            "amount": "123.00",
            "description": "Test",
        },
    }

    return render(request, "payment_redirect.html", context)

模板内容:

<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <title>正在跳转</title>
</head>
<body>
    <form id="payment-form" method="post" action="{{ target_url }}">
        {% for name, value in fields.items %}
            <input type="hidden" name="{{ name }}" value="{{ value }}">
        {% endfor %}
        <noscript>
            <button type="submit">继续支付</button>
        </noscript>
    </form>

    <script>
        document.getElementById("payment-form").submit();
    </script>
</body>
</html>

这不是 HTTP 重定向,而是由浏览器向另一个域名发送一次 POST 请求。能否使用这种方式,取决于目标平台规定的请求方法、字段名称、签名方式和安全要求。

注意事项

  • 不要把密码、访问令牌、银行卡信息等敏感数据放进 URL。查询参数可能记录在浏览器历史、服务器日志和 Referer 信息中。
  • 金额建议使用 Decimal,并明确格式化为两位小数,以免出现浮点数精度问题。
  • 如果重定向地址来自用户输入,必须通过域名白名单校验,否则可能造成开放重定向漏洞。
  • 支付平台通常还会要求签名、回调地址或订单校验,具体字段应以目标平台当前的接口文档为准。
  • requests.post() 等服务端 HTTP 请求不会让用户的浏览器跳转。这类请求适合服务器之间的调用,不能代替浏览器重定向。
  • 307 或 308 可以保留原请求的方法和请求体,但不能凭空生成新的 JSON 或表单数据,因此通常不适合用来组装支付请求。

对方要求 GET 时,使用 urlencode() 生成查询字符串;要求 POST 时,使用表单提交。只有目标接口明确支持 JSON,才由前端或 Django 服务端按照其 API 规范发送 JSON。

备注:内容仅供参考。