Django集成React时遭遇CORS错误,寻求排查解决方法
我用React+Django+PostgreSQL开发时碰到CORS错误,试了多种Django CORS配置都没用,但一个返回“Hi”的简单测试接口能正常工作,不确定问题出在Django视图还是React前端。已经确认VITE_SERVER配置正确,端点也没错。
Django配置
ALLOWED_HOSTS = [] CORS_ALLOW_CREDENTIALS = True CORS_ALLOW_HEADERS = [ 'content-type', 'authorization', ] CORS_ALLOWED_ALL_ORIGINS = True ALLOWED_HOSTS = [ "127.0.0.1", ] CSRF_TRUSTED_ORIGINS = ["http://127.0.0.1:8000/"] AUTH_USER_MODEL = 'customers.CustomerAccount' # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'corsheaders', 'customers', 'dealer', 'restful_apis', ] REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_simplejwt.authentication.JWTAuthentication', ), } MIDDLEWARE = [ "corsheaders.middleware.CorsMiddleware", "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", ]
React Fetch钩子
import React from "react"; const useFetch = () => { const fetchData = async (endpoint, method, body, token) => { const res = await fetch(import.meta.env.VITE_SERVER + endpoint, { // mode: "no-cors", method, headers: { "Content-Type": "application/json", Authorization: "Bearer " + token, }, body: JSON.stringify(body), }); const data = await res.json(); let returnValue = {}; if (res.ok) { if (data.status === "error") { returnValue = { ok: false, data: data.msg }; } else { returnValue = { ok: true, data }; } } else { if (data?.errors && Array.isArray(data.errors)) { const messages = data.errors.map((item) => item.msg); returnValue = { ok: false, data: messages }; } else if (data?.status === "error") { returnValue = { ok: false, data: data.message || data.msg }; } else { console.log(data); returnValue = { ok: false, data: "An error has occurred" }; } } return returnValue; }; return fetchData; }; export default useFetch;
失效的Django视图
class CarRentalListView(views.APIView): authentication_classes = [] permission_classes = [AllowAny] def get(self, request): rentals = RentalListing.objects.all() serialized_data = [] for rental in rentals: rental_data = RentalListingSerializer(rental).data car_data = CarSerializer(rental.car_id).data combined_data = { "rentallisting_id": rental_data.get("id"), "rental_rate": rental_data.get("rental_rate"), "drive_to_malaysia": rental_data.get("drive_to_malaysia"), "listing_date": rental_data.get("listing_date"), "vehicle_id": car_data.get("car_id"), "brand": car_data.get("brand"), "model": car_data.get("model"), "colour": car_data.get("colour"), "vehicle_type": car_data.get("vehicle_type"), "vehicle_image": car_data.get("vehicle_image"), "seat_capacity": car_data.get("seat_capacity"), } serialized_data.append(combined_data) return (Response(serialized_data, status=status.HTTP_200_OK))
正常工作的测试视图
class TestRoute(views.APIView): authentication_classes = [] permission_classes = [AllowAny] def get(self, request): return Response("Hi")
React调用代码
const Home = () => { const fetchData = useFetch(); const [rentals, setRentals] = useState([]); const getRentals = async () => { const res = await fetchData("/dealer/cars/rentals", "GET", undefined); if (res.ok) { setRentals(res.data); console.log(res.data); } else { alert(JSON.stringify(res.data)); console.log(res.data); } }; useEffect(() => { getRentals(); console.log(import.meta.env.VITE_SERVER); }, []);
浏览器错误信息
Access to fetch at 'http://127.0.0.1:8000/dealer/cars/rentals' from origin 'http://localhost:5173' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
核心原因
测试接口正常但目标接口失效,说明CORS配置本身没问题,问题出在目标视图抛出了未被捕获的异常,导致Django在返回错误响应时没有经过corsheaders中间件处理,所以缺少CORS响应头。
排查步骤与修复方案
修复视图序列化逻辑
你的视图中手动拼接序列化数据,可能存在以下问题:rental.car_id如果是外键字段,正确引用应为rental.car(外键字段默认是模型名小写,car_id是存储ID的字段),直接传入ID数值会导致CarSerializer序列化失败。- 手动拼接数据容易出现字段遗漏或拼写错误,建议改用嵌套序列化器。
优化后的视图代码:
from rest_framework.exceptions import APIException class CarRentalListView(views.APIView): authentication_classes = [] permission_classes = [AllowAny] def get(self, request): try: # 预查询关联的Car模型,避免N+1数据库查询问题 rentals = RentalListing.objects.all().select_related('car') # 使用嵌套序列化器返回数据 serializer = RentalListingWithCarSerializer(rentals, many=True) return Response(serializer.data, status=status.HTTP_200_OK) except Exception as e: # 捕获所有异常,返回带CORS头的错误响应 return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)对应的嵌套序列化器示例:
class CarSerializer(serializers.ModelSerializer): class Meta: model = Car fields = ["car_id", "brand", "model", "colour", "vehicle_type", "vehicle_image", "seat_capacity"] class RentalListingWithCarSerializer(serializers.ModelSerializer): car = CarSerializer() class Meta: model = RentalListing fields = ["id", "rental_rate", "drive_to_malaysia", "listing_date", "car"]更新CSRF信任源
前端运行在http://localhost:5173,需要把该地址加入CSRF_TRUSTED_ORIGINS:CSRF_TRUSTED_ORIGINS = ["http://127.0.0.1:8000", "http://localhost:5173"]验证视图可用性
直接在浏览器访问http://127.0.0.1:8000/dealer/cars/rentals,确认是否能正常返回JSON数据。如果返回500错误,优先修复视图的逻辑错误,CORS问题会随之解决。
内容的提问来源于stack exchange,提问作者ToXiiXz

