【问题标题】:How to output the port after the IP address in a resource URL in Django?如何在Django中的资源URL中输出IP地址后的端口?
【发布时间】:2019-09-18 18:50:57
【问题描述】:
  1. 如何将主机端口添加到序列化响应中的 URL? Django 目前为它们提供没有端口,因此链接已损坏。
  2. 或者,如果添加端口不是正确的方法,我该如何更改配置,以便在访问资源时无需在 URL 中指定端口?

输出(image_url 字段中缺少端口“:1337”)

{
    "count": 1,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": 1,
            "user": 1,
            "title": "Post 1",
            "slug": "post1",
            "image_url": "http://0.0.0.0/mediafiles/publisher/sample-image4.jpg",
            "content": "First",
            "draft": false,
            "publish": "2019-04-26",
            "updated": "2019-04-26T22:28:35.034742Z",
            "timestamp": "2019-04-26T22:28:35.034795Z"
        }
    ]
}

“image_url”字段不会正确链接,除非它包含这样的端口:

"image_url": "http://0.0.0.0:1337/mediafiles/publisher/sample-image4.jpg",

更多详情

堆栈:

  • Ubuntu
  • Docker(撰写)
  • Nginx
  • 独角兽 19.9.0
  • Django 2.1.7
  • Django REST 框架 3.9.2
  • Python 3+
  • Postgres/psycopg2

我正在使用 Django REST 框架来返回序列化对象的列表。这些对象包含一个名为“image”的 FileField,我可以输出该图像的 URL。唯一的问题是,当我在浏览器的输出中单击该链接时,如果不手动在地址中添加服务器端口,我将无法访问资源,例如

http://0.0.0.0:1337/mediafiles/publisher/sample-image4.jpg

我不确定这是 nginx 问题、Django 设置问题还是我的代码是如何配置的。我无法通过 Google 找到任何其他报告的案例(可能是因为我还是 Django 新手,尽管有以下教程,但我不确定正确的配置)。

我尝试了一些these solutions,但它们没有输出端口。

There's this question,但我没有使用 ImageField,我想为使用 FileField 的情况找到解决方案。对主要问题的评论表明添加端口也不应该是必需的,所以也许这是一个基础设施问题而不是 Django 问题?这方面的指导会很棒。

models.py

class Post(models.Model):
    class Meta:
        ordering = ('timestamp',)

    user = models.ForeignKey(User, on_delete=models.PROTECT)
    title = models.CharField(max_length=120)
    slug = models.SlugField(unique=True)
    image = models.FileField(upload_to='publisher/', null=True, blank=True)
    content = models.TextField()
    draft = models.BooleanField(default=False)
    publish = models.DateField(auto_now=False, auto_now_add=False)
    updated = models.DateTimeField(auto_now=True, auto_now_add=False)
    timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)

    def __str__(self):
        return self.title

    def __unicode__(self):
        return str(self.id)

    def get_absolute_url(self):
        return reverse("post:detail", kwargs={"slug":self.slug})

serializers.py

class PostSerializer(serializers.ModelSerializer):
    image_url = serializers.SerializerMethodField()

    class Meta:
        model = Post
        fields = [
            'id',
            'user',
            'title',
            'slug',
            'image_url',
            'content',
            'draft',
            'publish',
            'updated',
            'timestamp',
        ]

    def get_image_url(self, post):
        request = self.context.get('request')
        if post.image and hasattr(post.image, 'url'):
            image_url = post.image.url
            return request.build_absolute_uri(image_url)
        else:
            return None

urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path('blog/(?P<version>(v1|v2))/', include('blog.urls'))
    ]
...
    [
    url(r'^posts/$', PostListAPIView.as_view(), name='posts'),
    ]

if settings.DEBUG:
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

views.py

class PostListAPIView(generics.ListAPIView):
    model = Post
    queryset = Post.objects.all()
    serializer_class = PostSerializer

docker-compose.yml

version: '3.7'

services:
  web:
    build: ./app
    command: gunicorn hello_django.wsgi:application --bind 0.0.0.0:8000
    volumes:
      - ./app/:/usr/src/app/
      - static_volume:/usr/src/app/staticfiles
      - media_volume:/usr/src/app/mediafiles
    ports:
      - "8000"
    env_file: ./app/.env
    environment:
      - DB_ENGINE=django.db.backends.postgresql
      - DB_USER
      - DB_PASSWORD
      - DB_HOST=db
      - DB_PORT=5432
      - DATABASE=postgres
    depends_on:
      - db
    networks:
      - backend

  db:
    image: postgres:10.7-alpine
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    networks:
      - backend

  nginx:
    build: ./nginx
    volumes:
      - static_volume:/usr/src/app/staticfiles
      - media_volume:/usr/src/app/mediafiles
    ports:
      - "1337:80"
    depends_on:
      - web
    networks:
      - backend

networks:
  backend:
    driver: bridge

volumes:
  postgres_data:
  static_volume:
  media_volume:

nginx.conf

upstream hello_django {
    server web:8000;
}

server {

    listen 80;

    location / {
        proxy_pass http://hello_django;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_redirect off;
    }

    location /staticfiles/ {
        alias /usr/src/app/staticfiles/;
    }

    location /mediafiles/ {
        alias /usr/src/app/mediafiles/;
    }

    location /favicon.ico {
        access_log off;
        log_not_found off;
    }
}

【问题讨论】:

  • 有什么办法可以改善我的问题吗?我真的很想在这方面得到一些帮助。谢谢好心人。
  • 你真的打算在某个奇怪的端口为应用程序提供服务吗?通常在开发中我们使用端口 8000 或其他端口,但在生产中通常会自动使用端口 80(默认端口),这意味着与任何普通网站一样,明确说明端口是可选的。
  • 啊,我以为是添加特定端口的安全层。我可以使用默认值。不过,我的端口无法正常工作,这让我很烦恼,想知道出了什么问题。 (抱歉转贴。我会在完成之前清理。)
  • 好的,我确实通过从生产配置中删除端口来使事情正常进行。至少,我在 docker-compose 文件中将其更改为 80,并且 Django 生成的链接可以正常工作。我将这个问题悬而未决,因为仍然不清楚如何在生产中使用自定义端口。而且我不确定哪个部分配置不正确。

标签: django docker url nginx port


【解决方案1】:

感谢this question,我终于找到了如何修复图像 URL,这有点不同。

解决方案 1

在nginx配置的Host头中添加端口号如下:

    location / {
        proxy_pass http://hello_django;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host:1337;                               <<------- HERE
        proxy_redirect off;
    }

解决方案 2

将nginx配置中的Host头改为http_host,如下:

    location / {
        proxy_pass http://hello_django;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;                               <<------- HERE
        proxy_redirect off;
    }

无论哪种情况,现在 DRF (image link) 都会按如下方式返回图像 URL。

HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "count": 1,
    "next": null,
    "previous": null,
    "results": [
        {
            "id": 2,
            "user": 1,
            "title": "First post",
            "slug": "first",
            "image_url": "http://0.0.0.0:1337/mediafiles/publisher/background.gif",    <----HERE
            "content": "Second post content.",
            "draft": false,
            "publish": "2019-05-22",
            "updated": "2019-05-22T09:41:36.257605Z",
            "timestamp": "2019-05-22T07:58:01.471534Z"
        }
    ]
}

【讨论】:

  • 为什么还要有人去寻求解决方案 1?动态端口号处理不是总是更好吗?
  • 也许,是的。当我开始构建它时,我正在关注各种教程,这就是他们正在做的事情。所以这个答案更多的是关于“如何”而不是最佳实践。谢谢你提出来。
猜你喜欢
  • 2014-12-01
  • 2014-12-14
  • 1970-01-01
  • 1970-01-01
  • 2017-10-21
  • 2011-11-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多