React Native + Expo环境下Axios发送Multipart/Formdata报415(Unsupported media type)错误求助
我完全懂你遇到这个415错误的崩溃感——试遍了各种方法都不管用,之前用Fetch还好好的,后端又确认没问题,这种卡壳真的特别磨人😮💨 结合你的代码和React Native + Expo的环境特性,我整理了几个针对性的解决方案,你可以逐一尝试:
一、先排查Axios自动处理Content-Type的问题
Axios在发送FormData时,会自动设置正确的multipart/form-data请求头并生成boundary,但如果手动设置了Content-Type或者全局头干扰了这个逻辑,就会导致后端无法解析请求。
代码修改点:
在postScan和postRegister函数中,确保不手动设置Content-Type,并强制删除可能存在的全局配置:
// 以postScan函数为例,registerGuest里的postRegister同理修改 const postScan = async () => { const token = await AsyncStorage.getItem("access_token"); try { const res = await API.post("/scan-guest/", data, { headers: { Authorization: `Bearer ${token}`, // 绝对不要手动设置Content-Type!!! }, transformRequest: (data, headers) => { // 删除全局可能存在的Content-Type,让Axios自动处理 delete headers.common['Content-Type']; return data; } }); // 后续逻辑保持不变 } catch (err: any) { // 错误处理逻辑保持不变 } };
二、修正React Native中FormData的文件构造逻辑
React Native的FormData和Web端实现有差异,特别是Expo相机返回的图片URI(Android可能是content://开头,iOS是file://),需要确保文件项格式完全符合要求。
步骤1:引入Expo File System处理URI
先安装依赖(如果没装的话):
npx expo install expo-file-system
步骤2:修改图片URI的处理逻辑
在takeAndScanPhoto和registerGuest中,先把相机返回的URI转换成标准的file://格式:
import * as FileSystem from 'expo-file-system'; // 以takeAndScanPhoto为例: const takeAndScanPhoto = async () => { if (!cameraRef.current) return; // 让相机直接把图片存在缓存目录,确保返回file://格式的URI const photo = await cameraRef.current.takePictureAsync({ quality: 0.8, base64: false, uri: `${FileSystem.cacheDirectory}scan_${Date.now()}.jpg` }); // 验证URI有效性 const fileInfo = await FileSystem.getInfoAsync(photo.uri); const validFileUri = fileInfo.uri; setPhotoUri(validFileUri); setScanned(true); const data = new FormData(); data.append("img", { uri: validFileUri, name: getFileName(validFileUri), type: getMimeType(validFileUri), } as any); // 后续postScan逻辑保持不变 };
三、尝试让Axios使用Fetch适配器
既然你之前用Fetch是正常的,那可以让Axios复用Fetch的请求逻辑,避免XMLHttpRequest适配器在React Native中处理Multipart请求的兼容性问题:
步骤1:安装Fetch适配器
npm install @vespaiach/axios-fetch-adapter
步骤2:修改api.js配置
import axios from "axios"; import fetchAdapter from '@vespaiach/axios-fetch-adapter'; const API = axios.create({ baseURL: 'http://127.0.0.1:8000/myapi/', // 强制Axios使用Fetch发送请求,复用已验证的兼容逻辑 adapter: fetchAdapter }); export default API;
这个方法相当于让Axios“伪装”成Fetch发送请求,成本最低,大概率能直接解决问题。
四、最后验证:用Fetch对比测试
如果上面的方法都不行,可以暂时把Axios换回Fetch(和你之前的工作版本对比),确认请求格式是否正确:
// 替换postScan里的Axios请求为Fetch const postScan = async () => { const token = await AsyncStorage.getItem("access_token"); const response = await fetch('http://127.0.0.1:8000/myapi/scan-guest/', { method: 'POST', headers: { Authorization: `Bearer ${token}`, // Fetch会自动处理FormData的Content-Type,不要手动设置 }, body: data }); if (response.ok) { const resData = await response.json(); // 后续逻辑保持不变 } else { // 错误处理保持不变 } };
如果Fetch能正常工作,那问题肯定出在Axios的配置或FormData处理上,回到前三个方案排查即可。
总结
最可能的原因是:
- Axios的自动Content-Type配置被干扰,导致请求头没有正确携带boundary
- React Native的FormData对Expo相机返回的URI格式不兼容
- Axios默认的XMLHttpRequest适配器在React Native中处理Multipart请求的bug
优先尝试方案三(Fetch适配器),因为它直接复用了你已经验证过的兼容逻辑,成本最低;如果不行再试方案一和方案二。
内容来源于stack exchange

