Django Admin路径错误:/$default前缀导致404问题求助
Django Admin路径异常修复方案(Serverless+Lambda+API Gateway架构)
问题描述
基于REST Framework开发的Django应用部署在Serverless+Lambda+API Gateway HTTP API架构后,Django Admin页面生成的URL出现异常:
- 预期登录表单POST到
/admin/login,实际提交路径变成/$default/admin/login,访问返回404 - 甚至出现嵌套前缀的异常路径
/$default/$default/admin/login/ - 应用其他API功能正常,仅Admin路径错误
相关配置文件
serverless.yml配置
service: api app: api org: myapp frameworkVersion: '3' provider: name: aws runtime: python3.8 functions: serve: handler: wsgi_handler.handler timeout: 20 environment: DB_NAME: ${param:db_name} DB_PASSWORD: ${param:db_password} DB_USER: ${param:db_user} DB_PORT: ${param:db_port} DB_HOST: ${param:db_host} events: - httpApi: "*" migration: handler: migrate.handler timeout: 60 environment: DB_NAME: ${param:db_name} DB_PASSWORD: ${param:db_password} DB_USER: ${param:db_user} DB_PORT: ${param:db_port} DB_HOST: ${param:db_host} custom: wsgi: app: myapp.wsgi.application plugins: - serverless-python-requirements - serverless-wsgi
Django URL配置
from django.contrib import admin from django.urls import path, include from rest_framework.schemas import get_schema_view schema_view = get_schema_view( title="MyApp", description="MyApp Universal API", version="1.0.0", ) urlpatterns = [ path("admin/", admin.site.urls), path("user/", include("myapp.core.urls"), name="user"), path("openapi", schema_view, name="openapi-schema"), ]
Django配置文件
import os from pathlib import Path from dotenv import load_dotenv load_dotenv() BASE_DIR = Path(__file__).resolve().parent.parent SECRET_KEY = "not for you :)" DEBUG = True ALLOWED_HOSTS = ['*'] if 'CODESPACE_NAME' in os.environ: codespace_name = os.getenv("CODESPACE_NAME") codespace_domain = os.getenv("GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN") CSRF_TRUSTED_ORIGINS = [f'https://{codespace_name}-8000.{codespace_domain}'] ROOT_URLCONF = "myapp.urls" INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", 'rest_framework', "myapp.core", ] MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", ] X_FRAME_OPTIONS = "ALLOW-FROM preview.app.github.dev" TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [BASE_DIR / "myapp" / "templates"], "APP_DIRS": True, "OPTIONS": { "context_processors": [ "django.template.context_processors.debug", "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", ], }, }, ] WSGI_APPLICATION = "myapp.wsgi.application" STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': os.environ.get('DB_NAME', 'neondb'), 'USER': os.environ.get('DB_USER', 'postgres'), 'PASSWORD': os.environ.get('DB_PASSWORD'), 'HOST': os.environ.get('DB_HOST', 'localhost'), 'PORT': os.environ.get('DB_PORT', 5432), } } AUTH_PASSWORD_VALIDATORS = [ { "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] LANGUAGE_CODE = "en-us" TIME_ZONE = "UTC" USE_I18N = True USE_TZ = True STATICFILES_DIRS = [ BASE_DIR / "myapp" / "static", ] STATIC_URL = "static/" DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" STATICFILES_STORAGE = 'storages.backends.s3boto3.S3StaticStorage' AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID') AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY') AWS_STORAGE_BUCKET_NAME = "myapp-django-static" AUTH_USER_MODEL = 'core.User' REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( 'rest_framework.renderers.JSONRenderer', ), 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticatedOrReadOnly', ) }
错误截图

修复步骤
1. 修正Serverless HTTP API路径映射
原配置中httpApi: "*"会导致API Gateway自动添加/$default前缀。修改serve函数的事件配置,明确路径规则并指定payload格式:
functions: serve: # ... 其他配置 events: - httpApi: path: /{proxy+} method: ANY payloadFormatVersion: '2.0'
这样配置后,API Gateway会把完整的路径传递给Lambda,不会自动添加$default前缀。
2. 强制Django使用根路径生成URL
在Django的settings.py中添加以下配置:
# 强制Django以根路径生成所有URL,避免额外前缀 FORCE_SCRIPT_NAME = '' # 确保静态资源URL以根路径开头 STATIC_URL = '/static/'
FORCE_SCRIPT_NAME会覆盖Django从请求中获取的脚本路径,强制所有生成的URL都以/开头。
3. 手动修正请求路径(可选,若前两步无效)
如果serverless-wsgi插件仍未正确处理路径,可以修改wsgi_handler.py,手动移除请求中的/$default前缀:
import os import django from django.core.wsgi import get_wsgi_application from serverless_wsgi import handle_request os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.settings") django.setup() application = get_wsgi_application() def handler(event, context): # 移除路径中的第一个/$default前缀 if '/$default' in event['rawPath']: event['rawPath'] = event['rawPath'].replace('/$default', '', 1) event['requestContext']['http']['path'] = event['requestContext']['http']['path'].replace('/$default', '', 1) return handle_request(application, event, context)
4. 验证静态资源配置
确保静态资源已正确上传到S3,STATICFILES_STORAGE和相关AWS配置无误,避免因Admin页面静态资源加载失败导致的路径渲染异常。
内容的提问来源于stack exchange,提问作者Joshua Steele
相关产品推荐
相关产品推荐

