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

跨域Fetch调用PHP无报错但无返回数据问题求助

跨域Fetch请求无数据返回问题排查与解决

问题现象

  • 同服务器场景:客户端http://localhost:8888/myclient.html调用同主机PHP接口http://localhost:8888/myphp.php(MAMP环境),正常返回JSON数据。
  • 跨域场景:客户端http://127.0.0.1:5000调用上述PHP接口,无报错但完全没有数据返回。

客户端代码

<form target="_self" name="formulario" id="formulario">
    <!-- Estamos usando lista desordenada para los campos de formulario
    Es también habitual hacerlo con los elementos de los menús de navegación
    -->
    <ul>
        <!-- 
            H1 y H2 aquí van dentro de la etqueta UL para que no se solape con los campos,
            ya que este UL está en posición absoluta
         -->
        <h2>Envíanos tus datos</h2>
        <h1>Cubre el formulario</h1>
        <!-- 
            Empiezan los campos de formulario
         -->
        <li>
            <!-- Campo obligatorio tipo texto-->
            <label for="nombre"><span class="required">*</span> Tu nombre</label><br>
            <input type="text" name="nombre" id="nombre" required>

        </li>
        <li>
            <!-- Campo tipo numérico-->
            <label for="edad">Tu edad</label><br>
            <input type="number" name="edad">

        </li>
        <li>
            <!-- Campo obligatorio tipo Email-->
            <label for="email"><span class="required">*</span> Email</label><br>
            <input type="email" name="email" id="email" required>

        </li>
        <li>
            <!-- Campo tipo Tel (teléfono)-->
            Teléfono <br>(formato xxx yyy zzz):<br>
            <input type="tel" name="tel">
        </li>
        <li>
            <!-- inputs tipo Radio-button Llevan el mismo nombre 
                para que se reciba un solo dato. Es un solo campo con dos inputs que
                se marca además como obligatorio
            -->
            <span class="required">*</span> ¿Cómo vendrás al centro?
            <hr>
            <input type="radio" id="vehiculo" name="comoviene" value="vehiculo" required>
            <label for="vehiculo">Tengo vehículo</label><br>
            <input type="radio" id="andando" name="comoviene" value="andando">
            <label for="andando">Voy andando</label>
        </li>
        <li>
            <!-- inputs tipo checkbox. Cada uno es un campo.
            -->
            ¿ Qué vehículos tienes ?<br>
            <input type="checkbox" id="coche" name="tipo" value="coche">
            <label for="coche">Tengo coche</label><br>
            <input type="checkbox" id="moto" name="moto" value="moto">
            <label for="moto">Tengo moto </label><br>
            <input type="checkbox" id="barco" name="barco" value="barco">
            <label for="barco">Tengo barco</label>
        </li>
        <li>
            <!-- Campo tipo select, que crea un menú desplegable con las ociones
            -->
            ¿Qué curso quieres?
            <label for="curso">Elige uno</label>

            <select name="curso" id="curso">
                <option value="">Elige uno...</option>
                <option value="HTML">HTML</option>
                <option value="CSS">CSS</option>
                <!-- Este sería el valor seleccionado por defecto
            -->
                <option value="JS" selected>JAVASCRIPT</option>
            </select>
        </li>
        <li>
            <!-- Botón de enviar.
                Es un input pero no envía valores, 
                aunque tiene un value que es el texto que se ve en el botón,  
                y por defecto (si no se pone) es "submit".
            -->
            <input type="submit" name="Enviar" value="Enviar">

        </li>
    </ul>
</form>
<script>
    let formul = document.getElementById('formulario');
    let path = "http://localhost:8888/CLASE/CURSO-IFCD0110/hola-background/";
    formul.onsubmit = function (e) {
        e.preventDefault();
        async function envio() {
            const datos = new FormData(e.target);
            let toSend = Object.fromEntries(datos);
            //  console.log(toSend)
            let enviar = await fetch(path + 'form.php',
                {
                    method: 'POST',
                    /*
                    * We also need to stringify the values, turn the
                    * JavaScript object to a single string that is accepted
                    * as JSON. So we are sending one string that contains
                    * all our values
                    */
                    mode: 'no-cors',
                    body: JSON.stringify(toSend),
                    headers: {
                        'Content-Type': 'application/json'
                        // 'Content-Type': 'application/x-www-form-urlencoded',
                    }
                });
            let escribir = await enviar.json();
            document.body.innerHTML = JSON.stringify(escribir)
        }
        envio();
  }
</script>

服务器端代码

<?php
 header("Access-Control-Allow-Origin: *");
 header("Content-Type: application/json; charset=UTF-8");
 header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, FETCH, OPTIONS");
 header("Access-Control-Allow-Headers: Content-Type, Access- 
 Control-Allow-Headers, Authorization, X-Requested-With");
 $data = json_decode(file_get_contents('php://input'), true);

 echo json_encode($data);
exit;
?>

问题根源与解决方案

1. 移除mode: 'no-cors'配置

mode: 'no-cors'会强制让浏览器限制JavaScript读取响应内容,即便服务器正常返回数据,前端也无法获取到。这是跨域场景无数据返回的核心原因。

修改后的fetch配置:

let enviar = await fetch(path + 'form.php',
    {
        method: 'POST',
        body: JSON.stringify(toSend),
        headers: {
            'Content-Type': 'application/json'
        }
    });

2. 修复PHP服务器CORS配置

原PHP代码中Access-Control-Allow-Headers行存在换行错误,同时需要处理OPTIONS预检请求(跨域POST请求会先发送OPTIONS预检):

<?php
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

// 处理OPTIONS预检请求,直接返回成功
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(200);
    exit;
}

$data = json_decode(file_get_contents('php://input'), true);
echo json_encode($data);
exit;
?>

3. 添加错误捕获与调试

在客户端代码中加入错误捕获,避免静默失败,方便排查问题:

async function envio() {
    try {
        const datos = new FormData(e.target);
        let toSend = Object.fromEntries(datos);
        let enviar = await fetch(path + 'form.php',
            {
                method: 'POST',
                body: JSON.stringify(toSend),
                headers: {
                    'Content-Type': 'application/json'
                }
            });
        
        // 检查HTTP响应状态
        if (!enviar.ok) {
            throw new Error(`请求失败,状态码: ${enviar.status}`);
        }
        
        let escribir = await enviar.json();
        document.body.innerHTML = JSON.stringify(escribir);
    } catch (error) {
        console.error('请求出错:', error);
        document.body.innerHTML = `请求出错: ${error.message}`;
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 03:54:27