英文:
Django reverse() behind API Gateway/Proxy
问题
我的Django REST API部署在一个API网关(Kong)后面。
我想在我的API视图(APIViews)中使用reserve()
来保留一些URL。
我想要请求帮助以获取正确的URL格式。
基于API网关的基本路径。
通信流程:
客户端(请求基本路径) <-> Kong(转发到上游) <-> Apache(反向代理) <-> Django
Kong定义了一个基本路径和一个上游,以将客户端请求转发给Django。
Kong在HTTP头中包含了X_FORWARDED_HOST
和X_FORWARDED_PATH
。
X_FORWARDED_PATH包含网关的基本路径。
X_FORWARDED_HOST包含网关的URL。
网关基本路径是:
/gateway-basepath
上游路径是:
mydomain.com/py/api/v1
基本上,没有网关,Django的reverse()
为users端点创建以下URL:
mydomain.com/py/api/v1/users/
在API网关的情况下,Django创建了以下路径:
apigatewayurl.com/gateway-basepath/py/api/v1/users/
Django考虑了X_FORWARDED_HOST,但没有考虑X_FORWARDED_PATH。
我需要以下结果:
apigatewayurl.com/gateway-basepath/users
否则,在API网关内无法使用Django URL解析。
我会感激任何帮助。
urls.py
from rest_framework.views import APIView
from rest_framework import routers
from . import views
class APIRootView(APIView):
def get(self, request, format=None):
return Response({
'users': reverse('user-list', request=request, format=format),
})
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
urlpatterns = [
path('api/v1/', APIRootView.as_view(), name="api_root"),
]
urlpatterns += router.urls
views.py
from rest_framework import viewsets
from django.contrib.auth import models as django_models
from .serializers import UserSerializer
class UserViewSet(viewsets.ModelViewSet):
queryset = django_models.User.objects.all()
serializer_class = UserSerializer
serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ["url", "username", "email", "is_staff"]
英文:
My Django REST API is deployed behind an API Gateway (Kong).
I want to reserve()
some urls in my APIViews
.
I would like to ask for help to get the right url format.
Based on basepath of the API gateway.
Communication flow:
Client (requesting basepath) <-> Kong (forwarding to upstream) <-> Apache (Reverse Proxy) <-> Django
Kong defines a basepath and an upstream to forward client request to Django.
Kong includes X_FORWARDED_HOST
and X_FORWARDED_PATH
in the HTTP header.
X_FORWARDED_PATH contains the basepath of the gateway.
X_FORWARDED_HOST contains the gateway-url.
Gateway basepath is:
/gateway-basepath
Upstream path is:
mydomain.com/py/api/v1
Basically, without gateway, Django reverse()
creates following url for users endpoint:
mydomain.com/py/api/v1/users/
With the API gateway, the Django creates follow path:
apigatewayurl.com/gateway-basepath/py/api/v1/users/
Django is considering X_FORWARDED_HOST, but not X_FORWARDED_PATH
I need following result:
apigatewayurl.com/gateway-basepath/users
Otherwise the Django url resolving is not usable within the api gateway.
I would appreciate any help.
urls.py
from rest_framework.views import APIView
from rest_framework import routers
from . import views
class APIRootView(APIView):
def get(self, request, format=None):
return Response({
'users': reverse('user-list', request=request, format=format),
})
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
urlpatterns = [
path('api/v1/', APIRootView.as_view(), name="api_root"),
]
urlpatterns += router.urls
views.py
from rest_framework import viewsets
from django.contrib.auth import models as django_models
from .serializers import UserSerializer
class UserViewSet(viewsets.ModelViewSet):
queryset = django_models.User.objects.all()
serializer_class = UserSerializer
serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ["url", "username", "email", "is_staff"]
答案1
得分: 1
我找到了一个解决方法。
Django Rest Framework 调用 django.http
中的 [build_absolute_uri()]
(链接)来构建绝对URL。
变量 location
包含生成的URL路径。
在不更改核心库的情况下,我使用了monkey patching来解决这个问题。
请注意,这是一个破坏性的更改。
如果Django被更新,您需要检查这个函数。
settings.py
import django.http
from django.utils.encoding import iri_to_uri
from urllib.parse import urljoin, urlsplit
def build_absolute_uri(self, location=None):
if location is None:
location = "//%s" % self.get_full_path()
else:
# 强制转换懒惰的位置。
location = str(location)
# !!!!!!!!!!!!!!!!!!!!!
# 我在这里添加了我的自定义内容
# 修改 `location`
# !!!!!!!!!!!!!!!!!!!!!
location = location.replace(self.META["REQUEST_URI"], self.META["X_FORWARDED_PATH"])
bits = urlsplit(location)
if not (bits.scheme and bits.netloc):
if (
bits.path.startswith("/")
and not bits.scheme
and not bits.netloc
and "/./" not in bits.path
and "/../" not in bits.path
):
location = self._current_scheme_host + location.removeprefix("//")
else:
location = urljoin(self._current_scheme_host + self.path, location)
return iri_to_uri(location)
# 执行monkey patch并使用自己的函数覆盖核心功能
django.http.request.HttpRequest.build_absolute_uri = build_absolute_uri
英文:
I found a workaround to solve it.
Django Rest Framework calls [build_absolute_uri()]
(Link) from django.http
to build the absolute url.
The variable location
contains the generated url paths.
Without changing the core library, I used monkepatching to solve it.
Be aware, that this is a breaking change.
If Django will be updated, you have to check this function.
settings.py
import django.http
from django.utils.encoding import iri_to_uri
from urllib.parse import urljoin, urlsplit
def build_absolute_uri(self, location=None):
if location is None:
location = "//%s" % self.get_full_path()
else:
# Coerce lazy locations.
location = str(location)
# !!!!!!!!!!!!!!!!!!!!!
# I ADD HERE MY CUSTOMIZATION
# MODIFYING `location`
# !!!!!!!!!!!!!!!!!!!!!
location = location.replace(self.META["REQUEST_URI"], self.META["X_FORWARDED_PATH"])
bits = urlsplit(location)
if not (bits.scheme and bits.netloc):
if (
bits.path.startswith("/")
and not bits.scheme
and not bits.netloc
and "/./" not in bits.path
and "/../" not in bits.path
):
location = self._current_scheme_host + location.removeprefix("//")
else:
location = urljoin(self._current_scheme_host + self.path, location)
return iri_to_uri(location)
# do monkeypatch and overwrite core functionality with own function
django.http.request.HttpRequest.build_absolute_uri = build_absolute_uri
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论