Django中如何将hostname/endpoint请求重定向至api.hostname/endpoint
你之前在urlpatterns里加规则不生效,核心原因是hostname(根域)和api.hostname(API子域)是两个独立的服务入口,插件发往根域/endpoint的请求,根本不会路由到你API服务的Django路由层,你在API服务的路由表里写再多规则都碰不到这些请求,自然达不到效果。
方案1:反向代理层转发(首推,性能最好,无跨域问题)
如果你的服务前面挂了Nginx、Caddy这类反向代理,直接在根域的代理配置里加匹配规则,把对应路径的流量直接转到API子域,完全不用改业务代码。
- Nginx配置(放到根域对应的
server块内):
# 按你插件实际请求的路径改匹配规则,下面示例匹配三类接口路径 location ~ ^/(user/info|order/list|upload/.*)$ { proxy_pass https://api.hostname$request_uri; # 透传必要请求头,避免鉴权、IP识别异常 proxy_set_header Host api.hostname; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }
- 如果用Caddy,配置更简洁,在根域配置块加:
hostname.com { # 保留你原来根域站点的所有配置 handle_path /user/* /order/* /upload/* { reverse_proxy https://api.hostname.com } }
这个方案在流量入口层就完成转发,请求根本不会落到根域的业务服务上,性能损耗可以忽略,也不会触发浏览器/插件的跨域限制。
方案2:根域服务配置重定向(不想动代理时选)
如果根域本身也跑在Django服务上,直接在根域对应的Django项目的根路由里加重定向规则就行——注意别再写到API子域的路由表里了,之前就是配错了位置。
配置示例(根域的urls.py):
from django.views.generic import RedirectView from django.urls import re_path urlpatterns = [ # 先写你原来根域的其他路由 # 匹配需要转发的接口路径,用307状态码保留原请求方法和请求体,避免POST请求变GET丢参数 re_path( r'^(?P<api_path>user/.*|order/.*|upload/.*)$', RedirectView.as_view( url='https://api.hostname/%(api_path)s', permanent=False, query_string=True ), name='api_redirect' ), ]
注意这个方案的局限:如果插件没有做跨域适配,重定向后可能触发CORS拦截,而且绝对不要用301/302状态码,这两个状态码会把POST/PUT类请求改成GET,请求体直接丢失。
方案3:API服务绑定多域名(零转发开销)
如果你能改DNS和服务配置,可以直接把根域也解析到API服务的服务器IP,在API服务的域名绑定里加上根域,再写个简单的中间件做路径隔离就行,连转发都不用。
首先改Django配置:
# settings.py ALLOWED_HOSTS = ['api.hostname', 'hostname']
然后加个简单的中间件处理路由:
# middlewares.py class HostForwardMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): # 只处理根域过来的API路径请求,其他根域请求按需处理 valid_api_prefix = ('/user/', '/order/', '/upload/') if request.get_host() == 'hostname': if request.path.startswith(valid_api_prefix): # 直接走API服务的原有逻辑处理 request.META['HTTP_HOST'] = 'api.hostname' else: # 非API路径的根域请求,可转到对应服务或返回404 pass return self.get_response(request)
最后把这个中间件加到settings.py的MIDDLEWARE列表最前面就行。这个方案相当于API服务同时响应两个域名的指定路径请求,没有额外转发开销,但是要注意做好路径隔离,别把根域的其他正常请求也拦到API逻辑里。
避坑提醒:所有转发规则都要记得保留原请求的query参数和请求体,不然插件请求会丢参数。
内容的提问来源于stack exchange,提问作者humanbeing

