基于Django+React的多租户应用子域名路由配置问询
多租户子域名方案:Django+React全栈配置指南
一、DNS与Nginx服务器配置
DNS设置
在域名服务商后台添加通配符DNS记录,让所有子域名指向你的服务器IP:
- 记录类型选A(或CNAME,若用负载均衡)
- 主机记录填
* - 记录值填服务器公网IP
这样所有形如username.mywebsite.com的子域名都会解析到你的服务器。
Nginx配置
配置Nginx监听所有子域名请求,转发给Django的WSGI服务(如Gunicorn)。示例配置:
server { listen 80; server_name *.mywebsite.com; # 静态文件直接由Nginx处理(比Django托管更高效,可选) location /static/ { root /path/to/your/django/project; expires 30d; } # React build静态资源路由 location /static/react/ { root /path/to/your/react/build; expires 30d; } # 所有动态请求转发给Django location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; 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; } }
若需保持Django托管React build的原有逻辑,可跳过Nginx的React静态资源配置。
二、Django端修改
1. 子域名提取中间件
编写中间件从请求Host中提取租户标识(用户名),挂载到request对象:
# myproject/middleware.py class TenantMiddleware: def __init__(self, get_response): self.get_response = get_response # 排除无需解析为租户的特殊子域名 self.excluded_subdomains = {'www', 'api', 'admin'} def __call__(self, request): # 去除端口号,提取纯域名部分 host = request.get_host().split(':')[0] domain_parts = host.split('.') # 兼容二级/三级域名(如mywebsite.com或mywebsite.co.uk) if len(domain_parts) >= 2: subdomain = domain_parts[0] request.tenant_username = subdomain if subdomain not in self.excluded_subdomains else None else: request.tenant_username = None response = self.get_response(request) return response
在settings.py中注册中间件:
MIDDLEWARE = [ ... 'myproject.middleware.TenantMiddleware', ... ]
2. 子域名路由处理
推荐用django-hosts库实现子域名到不同URL配置的映射:
- 安装:
pip install django-hosts - 在
settings.py添加配置:
INSTALLED_APPS = [ ... 'django_hosts', ] MIDDLEWARE = [ 'django_hosts.middleware.HostsRequestMiddleware', # 放在最前 ... 'django_hosts.middleware.HostsResponseMiddleware', # 放在最后 ] ROOT_HOSTCONF = 'myproject.hosts' DEFAULT_HOST = 'www' # 主域名默认路由
- 创建
myproject/hosts.py:
from django_hosts import patterns, host host_patterns = patterns('', # 主域名(www.mywebsite.com)使用默认urls.py host(r'www', 'myproject.urls', name='www'), # 租户子域名(username.mywebsite.com)使用专属路由配置 host(r'(?P<username>[a-zA-Z0-9_-]+)', 'myproject.tenant_urls', name='tenant'), )
- 创建
myproject/tenant_urls.py,配置租户专属路由:
from django.urls import path, re_path from django.views.generic import TemplateView from django.views.static import serve import os react_build_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'react', 'build') urlpatterns = [ # 托管React build静态文件 re_path(r'^static/(?P<path>.*)$', serve, {'document_root': os.path.join(react_build_dir, 'static')}), # Catch-all路由,返回React index.html处理前端路由 re_path(r'^.*$', TemplateView.as_view(template_name='index.html')), # 租户专属API路由 path('api/tenant/', include('tenant_api.urls')), ]
3. 多租户数据过滤
在DRF视图中通过request.tenant_username过滤数据:
from rest_framework.views import APIView from rest_framework.response import Response from .models import TenantData from .serializers import TenantDataSerializer class TenantDataView(APIView): def get(self, request): if not request.tenant_username: return Response({'error': '无效租户'}, status=400) # 根据用户名过滤租户数据 data = TenantData.objects.filter(user__username=request.tenant_username) serializer = TenantDataSerializer(data, many=True) return Response(serializer.data)
三、React端调整
1. 前端路由适配
- 保持React路由为相对路径(如
/dashboard),不要硬编码主域名 - 依赖Django租户路由中的catch-all规则,确保子域名下所有路径都返回React的
index.html
2. API请求适配
将API请求改为相对路径,避免硬编码主域名:
// 错误写法:硬编码主域名 axios.get('https://mywebsite.com/api/tenant/data') // 正确写法:相对路径 axios.get('/api/tenant/data')
这样无论当前是主域名还是子域名,请求都会自动适配当前域名。
3. 环境变量配置
本地测试时在.env中设置API前缀为相对路径:
REACT_APP_API_URL=/api
生产环境保持相同配置即可。
四、本地测试子域名配置
1. 修改本地hosts文件
在/etc/hosts(Linux/macOS)或C:\Windows\System32\drivers\etc\hosts(Windows)中添加:
127.0.0.1 alice.localhost 127.0.0.1 bob.localhost
让alice.localhost和bob.localhost指向本地服务器。
2. Django配置调整
在settings.py中允许本地子域名:
ALLOWED_HOSTS = ['*.localhost', '127.0.0.1']
3. 启动服务测试
- 启动Django服务:
python manage.py runserver - 访问
http://alice.localhost:8000,检查中间件是否正确提取alice作为租户用户名 - 测试React路由和API请求是否正常工作
五、异常处理建议
- 当子域名对应的用户名不存在时,可在中间件或视图中返回404页面,或重定向到主域名注册页
- 对
api.mywebsite.com这类特殊子域名单独配置路由,避免误解析为租户
内容的提问来源于stack exchange,提问作者Ajith Chitturi
相关产品推荐
相关产品推荐

