简体   繁体   English

Django 在无服务器上运行的应用程序 + Lambda + API 网关 HTTP API 正在重写链接以使用默认前缀

[英]Django application running on top of Serverless + Lambda + API Gateway HTTP API is rewriting links to be prefixed with default

My Django Application (Largely REST Framework based) is currently producing URLs on the admin page that don't resolve.我的 Django 应用程序(主要基于 REST 框架)当前在管理页面上生成无法解析的 URL。 The expected result is that the Django Admin's login prompt submits the form with a POST to /admin/login .预期结果是 Django 管理员的登录提示将带有 POST 的表单提交到/admin/login The resultant URL passed by as the form submission URL by Django is /$default/admin/login and that returns a 404 with the even more obtuse /$default/$default/admin/login/ .作为表单提交 URL 通过 Django 传递的结果 URL 是/$default/admin/login并返回 404 和更迟钝的/$default/$default/admin/login/

I'm presuming I have some sort of misconfiguration in either my Django configuration or serverless.yml .我假设我的 Django 配置或serverless.yml中存在某种配置错误。

As per the following serverless.yml I'm using API Gateway V2, Django through WSGI, and Lambda functions.根据以下serverless.yml ,我正在使用 API Gateway V2、Django 通过 WSGI 和 Lambda 函数。

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

My URLs are pretty standard:我的网址非常标准:

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"),
]

My configuration is even more standard:我的配置更标准:

import os
from pathlib import Path
from dotenv import load_dotenv

load_dotenv()

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "not for you :)"

# SECURITY WARNING: don't run with debug turned on in production!
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"

# Application definition

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"

ROOT_URLCONF = "myapp.urls"

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')

# Database
# https://docs.djangoproject.com/en/4.1/ref/settings/#databases

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),
    }
}


# Password validation
# https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators

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",
    },
]


# Internationalization
# https://docs.djangoproject.com/en/4.1/topics/i18n/

LANGUAGE_CODE = "en-us"

TIME_ZONE = "UTC"

USE_I18N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.1/howto/static-files/

STATICFILES_DIRS = [
    BASE_DIR / "myapp" / "static",
]

STATIC_URL = "static/"

# Default primary key field type
# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field

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',
    )
}

As for the resultant error message:至于由此产生的错误信息:

在此处输入图像描述

Any help (or ideas) would be greatly appreciated!任何帮助(或想法)将不胜感激!

I've tried modifying the path structure of the serverless.yml and have been trawling through the Django source code for any hints to no avail.我已经尝试修改serverless.yml的路径结构,并且一直在浏览 Django 源代码以寻找任何无济于事的提示。 Naturally I'd just like Django admin to work.当然,我只想让 Django 管理员工作。 As far as the rest of the app, it works fine as the API itself isn't self referential.至于应用程序的 rest,它工作正常,因为 API 本身不是自引用的。 Django just isn't returning the correct path. Django 只是没有返回正确的路径。

To put it in brief, Django thinks that all of my routes are prefixed by /$default/ they are not.简而言之,Django 认为我所有的路由都以/$default/为前缀,但事实并非如此。 I'm looking for either a solution to force the path to sent by Django to be / or a fix for my Serverless configuration to mitigate this issue.我正在寻找一种解决方案来强制将 Django 发送的路径设为/或修复我的无服务器配置以缓解此问题。

I managed to resolve this with a rather niché Django settings option.我设法用一个相当小众的 Django 设置选项解决了这个问题。 https://docs.djangoproject.com/en/4.1/ref/settings/#force-script-name https://docs.djangoproject.com/en/4.1/ref/settings/#force-script-name

While this doesn't explain why it is resolving the path to /$default/ it does mitigate this issue.虽然这并不能解释为什么要解析到/$default/的路径,但它确实缓解了这个问题。

If you're using this for a different use case than mine and your path is in a subdirectory (eg the opposite issue to mine) then you would add your path in FORCE_SCRIPT_NAME instead.如果您将此用于与我不同的用例并且您的路径位于子目录中(例如与我相反的问题),那么您将在FORCE_SCRIPT_NAME中添加您的路径。

See the link I've provided for more information.请参阅我提供的链接以获取更多信息。

Add the following to your app's settings.py , or any file you're using as your DJANGO_SETTINGS_MODULE .将以下内容添加到您应用的settings.py或您用作DJANGO_SETTINGS_MODULE的任何文件中。

# Force Django to resolve the URL to the root of the site
# This is required for API Gateway <- WSGI -> Django path resolution to work properly.
FORCE_SCRIPT_NAME = ""

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 无服务器 Lambda 和 API 网关的 CORS 问题 - CORS issues with Serverless Lambda and API Gateway Serverless API Gateway/Lambda 下载失败 PDF - Serverless API Gateway/Lambda fails to download PDF GCP 上的无服务器 API 网关 - Serverless API Gateway on GCP Terraform 无服务器应用程序与 AWS Lambda 和 API 网关未知令牌? - Terraform Serverless Applications with AWS Lambda and API Gateway unknown token? 是否可以使用 Serverless 在两个区域部署 API 网关和 Lambda? - Is it possible to deploy API Gateway and Lambda in two regions with Serverless? api网关和lambda分页 - api gateway and lambda pagination 如何使用 Lambda 访问 AWS API Gateway 请求的 HTTP 标头? - How to access HTTP headers for request to AWS API Gateway using Lambda? 在 API 网关中将 AWS Lambda 和 HTTP 端点集成在一起? - Integrating AWS Lambda and HTTP endpoint together in API Gateway? Serverless wsgi 应用和aws http api 集成失败 - Serverless wsgi application and aws http api fail to integrate API 网关 HTTP 与 aws-sam 的代理集成(不是 Lambda 代理) - API Gateway HTTP Proxy integration with aws-sam (NOT Lambda Proxy)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM