You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django单页应用首页Jobs加载失败,重试后恢复问题求助

问题分析与解决方案

核心问题定位

报错Unexpected token '<', "<!doctype "... is not valid JSON说明前端请求Jobs数据接口时,服务器返回的是HTML页面(而非预期的JSON数据)。结合"多次点击/刷新后恢复、其他页面正常"的现象,大概率是后端路由匹配优先级冲突或Nginx请求转发的偶发异常,导致API请求被错误路由到了单页应用的入口HTML。


排查与修复步骤

1. 调整Django后端路由优先级

确保API接口路由在单页应用入口路由之前定义,避免Django误将API请求匹配到首页HTML。

示例urls.py修改:

# urls.py
from django.urls import path, re_path
from django.views.generic import TemplateView

# 先定义Jobs的API接口路由
path('api/jobs/', views.get_jobs_data, name='api_jobs'),

# 再定义单页应用的兜底路由(匹配所有未被前面路由命中的路径)
re_path(r'^.*$', TemplateView.as_view(template_name='index.html')),

注意:如果你的API路由是/jobs/而非带/api/前缀,极可能和前端路由/jobs/冲突,导致Django优先返回HTML。建议给所有API接口统一加上/api/前缀做区分。

2. 修正Nginx的转发与缓存逻辑

Nginx的缓存或路由匹配规则可能偶发将API请求转发到静态HTML文件,而非Django后端。

推荐Nginx配置调整:

server {
    listen 443 ssl;
    server_name pythonyard.com;

    # 静态文件直接返回,不转发到Django
    location /static/ {
        alias /path/to/your/project/static/;
        expires 30d;
    }

    # API请求单独处理,优先转发到Django后端
    location /api/ {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        # 禁用API请求的缓存,避免返回旧的HTML内容
        proxy_no_cache 1;
        proxy_cache_bypass 1;
    }

    # 单页应用其他请求返回入口HTML
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        # 仅当请求路径不存在时返回入口HTML,避免API请求被误匹配
        try_files $uri $uri/ /index.html;
    }
}

3. 优化前端请求逻辑

前端调用locationHandler加载Jobs内容时,增加请求校验和重试逻辑,处理偶发的转发异常:

示例前端代码修改:

async function loadJobsData() {
    try {
        const response = await fetch('/api/jobs/');
        // 先校验响应状态,再解析JSON
        if (!response.ok) {
            throw new Error(`请求失败:${response.status}`);
        }
        // 明确要求JSON格式,避免返回HTML
        if (!response.headers.get('content-type')?.includes('application/json')) {
            throw new Error('返回内容不是JSON');
        }
        const data = await response.json();
        locationHandler(data); // 填充Jobs内容
    } catch (error) {
        console.error('Jobs数据加载失败,重试中:', error);
        // 短延迟后重试一次,解决偶发的转发问题
        setTimeout(loadJobsData, 500);
    }
}

// 首页加载时自动触发请求
window.addEventListener('load', () => {
    const currentPath = window.location.pathname;
    if (currentPath === '/' || currentPath === '/jobs/') {
        loadJobsData();
    }
});

4. 校验Django CSRF配置(若API用POST)

如果Jobs接口是POST请求,确保前端正确携带CSRF token,避免因验证失败返回HTML错误页:

// 从Cookie中获取CSRF token
function getCSRFToken() {
    let token = null;
    const cookies = document.cookie.split(';');
    for (const cookie of cookies) {
        const [key, value] = cookie.trim().split('=');
        if (key === 'csrftoken') {
            token = decodeURIComponent(value);
            break;
        }
    }
    return token;
}

// 请求时携带CSRF token和JSON接受头
const response = await fetch('/api/jobs/', {
    method: 'POST',
    headers: {
        'X-CSRFToken': getCSRFToken(),
        'Accept': 'application/json',
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({/* 请求参数 */})
});

验证方法

  1. 重启Nginx和Django服务:sudo systemctl restart nginx、sudo systemctl restart gunicorn(若使用gunicorn部署)
  2. 清空浏览器缓存,打开控制台"网络"面板,监控Jobs页面加载时的API请求,确认返回内容为JSON而非HTML
  3. 多次刷新首页,验证是否还出现加载失败的情况

内容的提问来源于stack exchange,提问作者Xceptions

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 17:01:20