[英]How to change the host in next key in a paginated URL in django rest framework?
I have a ModelSerializer in Django Rest Framework with paginated responses.我在 Django Rest 框架中有一个带有分页响应的 ModelSerializer。 So I have deployed it with gunicorn in a docker container.所以我用 gunicorn 将它部署在 docker 容器中。
gunicorn -c gunicorn_config.py app.wsgi --bind 0.0.0.0:5000
Now the problem is in the paginated responses.现在问题出在分页响应中。 The next
key is something like. next
键是这样的。
next: "http://0.0.0.0:5000/admin/users/?page=2&per_page=10"
In my client-side where I am consuming these APIs, I just check the next
key and fetch the next response.在我使用这些 API 的客户端中,我只需检查next
键并获取下一个响应。 But since the next
key has the host as 0.0.0.0:5000
hence it will cause API call failure.但由于next
键的主机为0.0.0.0:5000
,因此会导致 API 调用失败。 And the purpose is not served for the next
key.并且不为next
密钥服务。
So at the moment, my API server is running in a separate docker container.所以目前,我的 API 服务器正在一个单独的 docker 容器中运行。 Which is set up via the reverse proxy in nginx
.这是通过nginx
中的反向代理设置的。
The next
link in the DRF paginator is generated using the hostname from the request. DRF 分页器中的next
链接是使用请求中的主机名生成的。 This is how the hostname is determined in the request:这是在请求中确定主机名的方式:
def _get_raw_host(self):
"""
Return the HTTP host using the environment or request headers. Skip
allowed hosts protection, so may return an insecure host.
"""
# We try three options, in order of decreasing preference.
if settings.USE_X_FORWARDED_HOST and (
'HTTP_X_FORWARDED_HOST' in self.META):
host = self.META['HTTP_X_FORWARDED_HOST']
elif 'HTTP_HOST' in self.META:
host = self.META['HTTP_HOST']
else:
# Reconstruct the host using the algorithm from PEP 333.
host = self.META['SERVER_NAME']
server_port = self.get_port()
if server_port != ('443' if self.is_secure() else '80'):
host = '%s:%s' % (host, server_port)
return host
So, check if the HTTP_X_FORWARDED_HOST
header sets the correct hostname you need and if so set USE_X_FORWARDED_HOST
to True
in your settings.因此,请检查HTTP_X_FORWARDED_HOST
header 是否设置了您需要的正确主机名,如果是,请在您的设置中将USE_X_FORWARDED_HOST
设置为True
。 Also make sure that the hostname you need is added to ALLOWED_HOSTS
.还要确保将您需要的主机名添加到ALLOWED_HOSTS
中。
You could also override the get_next_link()
method in the PageNumberPagination
class to supply the needed host/domain name您还可以覆盖PageNumberPagination
class 中的get_next_link()
方法以提供所需的主机/域名
So I made a custom pagination class extending PageNumberPagination
所以我做了一个自定义分页 class 扩展PageNumberPagination
from rest_framework.pagination import PageNumberPagination
def replace_query_param(url, key, val):
"""
Given a URL and a key/val pair, set or replace an item in the query
parameters of the URL, and return the new URL.
"""
(scheme, netloc, path, query, fragment) = parse.urlsplit(force_str(url))
scheme = "https"
netloc = "api.example.com"
query_dict = parse.parse_qs(query, keep_blank_values=True)
query_dict[force_str(key)] = [force_str(val)]
query = parse.urlencode(sorted(list(query_dict.items())), doseq=True)
return parse.urlunsplit((scheme, netloc, path, query, fragment))
def remove_query_param(url, key):
"""
Given a URL and a key/val pair, remove an item in the query
parameters of the URL, and return the new URL.
"""
(scheme, netloc, path, query, fragment) = parse.urlsplit(force_str(url))
scheme = "https"
netloc = "api.example.com"
query_dict = parse.parse_qs(query, keep_blank_values=True)
query_dict.pop(key, None)
query = parse.urlencode(sorted(list(query_dict.items())), doseq=True)
return parse.urlunsplit((scheme, netloc, path, query, fragment))
class LargeResultsSetPagination(PageNumberPagination):
page_size = 1000
page_size_query_param = 'per_page'
max_page_size = 1000
def get_next_link(self):
if not self.page.has_next():
return None
url = self.request.build_absolute_uri()
page_number = self.page.next_page_number()
return replace_query_param(url, self.page_query_param, page_number)
def get_previous_link(self):
if not self.page.has_previous():
return None
url = self.request.build_absolute_uri()
page_number = self.page.previous_page_number()
if page_number == 1:
return remove_query_param(url, self.page_query_param)
return replace_query_param(url, self.page_query_param, page_number)
Now I am using this pagination class in all my ViewSets
现在我在我的所有视图集中使用这个分页ViewSets
class TestViewSet(viewsets.ModelViewSet):
permission_classes = [permissions.IsAuthenticated]
queryset = Test.objects.all().order_by("pk")
serializer_class = test_serializers.TestSerializer
pagination_class = LargeResultsSetPagination
search_fields = ['name', 'description', 'follow_up', 'follow_up_type']
filter_backends = (filters.SearchFilter,)
And it does the job, the original inspiration https://stackoverflow.com/a/62422235/5884045它完成了工作,原始灵感https://stackoverflow.com/a/62422235/5884045
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.