I have tried almost every way out there. But none of them seems to be working. Everytime I get 415 (Unsupported media type) error. It certainly worked while I used fetch in the beginning. So, I can assure you that backend is working fine. Tried adding/removing Content-Type
and other suggested methods in stackoverflow/github communities, but all of them only tested my patience endurance. Hoping to get helped. Thank you in advance!
api.js:
import axios from "axios";
const API = axios.create(
{
baseURL: 'http://127.0.0.1:8000/myapi/',
}
)
export default API;
RenterScan.tsx
import React, { useRef, useState } from "react";
import { Button, TextInput, View, Alert, Image, Text, ScrollView } from "react-native";
import { CameraView, useCameraPermissions } from "expo-camera";
import { useNavigation } from "expo-router";
import AsyncStorage from "@react-native-async-storage/async-storage";
import API from "../utils/api";
import { refreshAuthToken } from "../utils/auth";
export default function RegisterGuest({ route }: any) {
const navigation: any = useNavigation();
const [permission, requestPermission] = useCameraPermissions();
const [scanned, setScanned] = useState(false);
const [showForm, setShowForm] = useState(false);
const [photoUri, setPhotoUri] = useState<string | undefined>();
const [formData, setFormData] = useState<Record<string, string>>({});
const room = route.params;
const cameraRef = useRef<CameraView>(null);
const getMimeType = (uri: string | undefined) => {
const ext = uri?.split('.').pop()?.toLowerCase();
switch (ext) {
case "png": return "image/png";
case "jpg":
case "jpeg": return "image/jpeg";
default: return "image/jpeg";
}
};
const getFileName = (uri: string | undefined) => {
if (!uri) return "photo.jpg";
const parts = uri.split('/');
return parts[parts.length - 1] || "photo.jpg";
};
const takeAndScanPhoto = async () => {
if (!cameraRef.current) return;
const photo = await cameraRef.current.takePictureAsync();
setPhotoUri(photo?.uri);
setScanned(true);
const data = new FormData();
data.append("img", {
uri: photo?.uri,
name: getFileName(photo?.uri),
type: getMimeType(photo?.uri),
} as any);
const postScan = async () => {
const token = await AsyncStorage.getItem("access_token");
const res = await API.post("/scan-guest/", data, {
headers: { Authorization: `Bearer ${token}` },
});
if (res.data?.guest_id) {
navigation.navigate("GuestProfile", {
room_id: room.number,
guest_id: res.data.guest_id,
});
} else {
Alert.alert("No match found. Please fill out the form.");
setShowForm(true);
}
};
try {
await postScan();
} catch (err: any) {
if (err?.response?.status === 401) {
await refreshAuthToken();
await postScan();
} else {
setShowForm(true);
}
}
};
const handleFieldChange = (key: string, value: string) => {
setFormData((prev) => ({ ...prev, [key]: value }));
};
const registerGuest = async () => {
const data = new FormData();
Object.entries(formData).forEach(([k, v]) => data.append(k, v));
if (photoUri) {
data.append("img", {
uri: photoUri,
name: getFileName(photoUri),
type: getMimeType(photoUri),
} as any);
}
data.append("room_id", room.number);
const postRegister = async () => {
const token = await AsyncStorage.getItem("access_token");
const res = await API.post("/register-guest/", data, {
headers: { Authorization: `Bearer ${token}` },
});
Alert.alert("Guest registered successfully.");
navigation.navigate("GuestProfile", {
room_id: room.number,
guest_id: res.data.guest_id,
});
};
try {
await postRegister();
} catch (err: any) {
if (err?.response?.status === 401) {
await refreshAuthToken();
await postRegister();
} else {
Alert.alert("Registration failed.");
}
}
};
if (permission === null) return <View />;
if (!permission.granted) {
return (
<View className="flex-1 justify-center items-center">
<Text>Camera access is required</Text>
<Button title="Grant Permission" onPress={requestPermission} />
</View>
);
}
return (
<View className="flex-1 bg-white">
{!scanned && (
<>
<CameraView
className="flex-1"
ref={cameraRef}
enableTorch={true}
flash="on"
/>
<View className="p-4 bg-gray-100">
<Text className="mb-2 text-sm text-gray-700">
Make sure the guest’s face is clearly visible.
</Text>
<Button title="Scan Guest" onPress={takeAndScanPhoto} />
</View>
</>
)}
{showForm && (
<ScrollView className="p-4 space-y-4">
<Text className="text-xl text-center font-semibold">Guest Registration</Text>
{photoUri && (
<Image
source={{ uri: photoUri }}
className="w-32 h-32 rounded-full self-center mb-4"
/>
)}
{[
{ field: "first_name", placeholder: "First Name" },
{ field: "last_name", placeholder: "Last Name" },
{ field: "email", placeholder: "Email" },
{ field: "phone", placeholder: "Phone" },
{ field: "country", placeholder: "Country" },
{ field: "dob", placeholder: "DOB (YYYY-MM-DD)" },
{ field: "id_proof", placeholder: "ID Proof Number" },
].map(({ field, placeholder }) => (
<TextInput
key={field}
placeholder={placeholder}
onChangeText={(val) => handleFieldChange(field, val)}
className="border border-gray-300 rounded-md px-4 py-2"
/>
))}
<View className="mt-4">
<Button title="Register" onPress={registerGuest} />
</View>
</ScrollView>
)}
</View>
);
}
I recently resolved this issue, so posting an answer in case anyone is searching for the same issue hereafter.
So the first issue was, I was using web build
with expo
and viewing it in my browser. And, as per expo-camera
's documentation, Web support always send base64
string because local file system paths are unavailable on browsers. You can read more about this here.
Secondly, there is an issue with axios
as well with sending formData
. It converts formData
to another format while sending the data, you can learn more about this issue here. For me, this answer worked. Simply to prevent this unexpected implementation, you can use transformRequest: formData => formData
while sending data with axios
. So, adding the code below and using Expo Go/Android Emulator instead of browser solved my issue.
const res = await API.post("/scan-guest/", data, {
headers: { Authorization: `Bearer ${token}` },
transformRequest: data => data,
});
and
const postRegister = async () => {
const token = await AsyncStorage.getItem("access_token");
const res = await API.post("/register-guest/", data, {
headers: { Authorization: `Bearer ${token}` },
transformRequest: data => data,
});
data
here in my case is the formData
.