在使用DRF框架开发接口时,经常会遇到需要统一输出数据里域名的情况,比如接口返回的用户头像地址、文件下载链接、跳转路径等,都需要使用固定的业务域名,而不是本地开发时的localhost或者内网地址。如果域名设置不当,会导致前端无法正确加载资源,影响业务正常运行。

全局配置域名的方式
DRF提供了配置项可以设置全局的域名,只需要在Django的settings.py文件中添加对应的配置即可,这种方式适合大多数场景,所有接口返回的相关地址都会自动拼接配置的域名。
首先需要在settings.py中添加DRF_DOMAIN配置项,存储你的业务域名:
# settings.py 配置 DRF_DOMAIN = "https://api.ipipp.com"
然后可以自定义一个基础的序列化器类,在基础类中处理域名的拼接逻辑,后续所有自定义的序列化器都继承这个基础类即可:
# serializers/base.py
from rest_framework import serializers
from django.conf import settings
class BaseSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
# 先获取默认的序列化结果
ret = super().to_representation(instance)
# 遍历结果,对需要拼接域名的字段进行处理
domain = getattr(settings, "DRF_DOMAIN", "")
if domain:
# 假设avatar、file_url是需要拼接域名的字段
for field in ["avatar", "file_url"]:
if field in ret and ret[field] and not ret[field].startswith(("http://", "https://")):
ret[field] = f"{domain}{ret[field]}"
return ret
单个序列化器中自定义处理
如果只有部分接口的字段需要设置域名,或者不同字段需要拼接不同的域名,可以在单个序列化器中自定义字段的序列化逻辑。
比如用户头像字段,我们可以在序列化器中定义一个自定义字段,手动拼接域名:
# serializers/user.py
from rest_framework import serializers
from django.conf import settings
from .models import User
class UserSerializer(serializers.ModelSerializer):
# 自定义头像字段
avatar = serializers.SerializerMethodField()
def get_avatar(self, obj):
# 如果头像路径存在且不是完整URL,拼接域名
if obj.avatar and not obj.avatar.startswith(("http://", "https://")):
domain = getattr(settings, "DRF_DOMAIN", "https://api.ipipp.com")
return f"{domain}{obj.avatar.url}"
return obj.avatar
class Meta:
model = User
fields = ["id", "username", "avatar"]
通过上下文传递域名参数
如果域名需要根据请求动态变化,比如不同客户端需要返回不同的域名,可以通过序列化器的上下文传递域名参数,在序列化时动态使用。
在视图中给序列化器传递上下文:
# views/user.py
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import UserSerializer
from .models import User
class UserDetailView(APIView):
def get(self, request, user_id):
user = User.objects.get(id=user_id)
# 从请求头或者请求参数中获取域名,传递个序列化器上下文
request_domain = request.META.get("HTTP_X_REQUEST_DOMAIN", "https://api.ipipp.com")
serializer = UserSerializer(user, context={"domain": request_domain})
return Response(serializer.data)
在序列化器中读取上下文的域名参数:
# serializers/user.py
from rest_framework import serializers
from .models import User
class UserSerializer(serializers.ModelSerializer):
avatar = serializers.SerializerMethodField()
def get_avatar(self, obj):
# 从上下文获取域名,默认使用配置中的域名
domain = self.context.get("domain", "https://api.ipipp.com")
if obj.avatar and not obj.avatar.startswith(("http://", "https://")):
return f"{domain}{obj.avatar.url}"
return obj.avatar
class Meta:
model = User
fields = ["id", "username", "avatar"]
注意事项
- 设置域名时要确保域名格式正确,末尾不要多余斜杠,避免拼接后出现双斜杠的问题。
- 如果返回的路径已经是完整URL,不要重复拼接域名,否则会导致地址错误。
- 生产环境和开发环境的域名可能不同,建议通过环境变量或者不同环境的配置文件来区分,不要硬编码域名。
- 如果接口返回的是HTML内容,其中涉及的<a>标签的href属性也需要做对应的域名处理,避免链接无效。