Unexpected token < in JSON when using fetch() - javascript

I am trying to connect my React signup page to my Django API so when a user signs up a user profile is created in Django.
I get this error on my console when I try to create a new user:
Signup.js:33 POST http://127.0.0.1:8000/api/v1/users/profiles/?format=api 400 (Bad Request)
onSubmit # Signup.js:33
callCallback # react-dom.development.js:188
invokeGuardedCallbackDev # react-dom.development.js:237
VM1121:5 Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 4
This is what I see in network inspector:
POST /api/v1/users/profiles/?format=api
HTTP 400 Bad Request
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"detail": "JSON parse error - Expecting value: line 1 column 2 (char 1)"
}
This is the code I have on signup.js:
const onSubmit = e => {
e.preventDefault();
const user = {
email: email,
name: name,
location: 'Example',
password: password,
user_id: 1
};
console.log(user);
fetch('http://127.0.0.1:8000/api/v1/users/profiles/?format=api', {
method: 'POST',
headers: {
'Content-Type':'application/json'
},
body: JSON.stringify(user)
})
.then(res => res.json())
.then(data => {
if (data.key) {
localStorage.clear();
localStorage.setItem('token',data.key);
window.location.replace('http://localhost:3000/profile/');
} else {
setEmail('');
setName('');
setPassword('');
localStorage.clear();
setErrors(true);
}
});
};
and in settings.py:
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_METHODS = [
"DELETE",
"GET",
"OPTIONS",
"PATCH",
"POST",
"PUT",
]
I assume I am using the right endpoint. When I run my django server and visit this URL this is what I see:
GET /api/v1/users/profiles/?format=api
HTTP 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"user_id": 1,
"name": "",
"location": "",
"password": "",
"email": "",
"signup_confirmation": false
}
]
UPDATE
As suggested by one of the answers I tried adding this to my Signup.js
fetch('http://127.0.0.1:8000/api/v1/users/profiles/?format=api')
.then(res => res.text())
.then(text => console.log(text))
This is what I get on my console now
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="robots" content="NONE,NOARCHIVE" />
<title>Profile List – Django REST framework</title>
...

As you have provided on network logs, it gives you a "400 Bad Request" you might have to check if you are sending the request to a correct endpoint and whether the requests methods and details have been correctly specified in the fetch call.
This error is a common error and in most cases is caused when the server returns HTML instead of JSON. Since the error specifies it cannot identify "<" which is usually caused due to the html tags being detected upfront.
To fix this error you need to figure out why you're getting HTML (or something else) instead of the JSON you expected. To do this you need to log the data you're trying to parse to the console like shown below.
fetch('http://127.0.0.1:8000/api/v1/users/profiles/?format=api')
.then(res => res.text()) // convert to plain text
.then(text => console.log(text)) // then log it out

Related

Uncaught (in promise) SyntaxError: Unexpected end of input - API [duplicate]

I tried a ReactJS fetch call to a REST-API and want to handle the response. The call works, i get a response, which i can see in Chrome Dev Tools:
function getAllCourses() {
fetch('http://localhost:8080/course', {
method: 'POST',
mode: 'no-cors',
credentials: 'same-origin',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
objectClass: 'course',
crud: '2'
})
}).then(function (response) {
console.log(response);
return response.json();
}).catch(function (err) {
console.log(err)
});
}
When i try to handle the response, i got a "SyntaxError: Unexpected end of input" at
return response.json();
The console.log looks like this:
My Response JSON looks like this, it is valid, i checked it with jsonlint:
[
{
"0x1": {
"users": [],
"lectures": [],
"owner": "0x2",
"title": "WWI 14 SEA",
"description": null,
"objectClass": "course",
"id": "course_00001"
},
"0x2": {
"username": "system",
"lectures": [],
"course": null,
"solutions": [],
"exercises": [],
"roles": [
"0x3",
"0x4",
"0x5"
],
"objectClass": "user",
"id": "user_00001"
},
"0x3": {
"roleName": "ROLE_ADMIN",
"objectClass": "role",
"id": "role_00001"
},
"0x4": {
"roleName": "ROLE_STUDENT",
"objectClass": "role",
"id": "role_00002"
},
"0x5": {
"roleName": "ROLE_DOCENT",
"objectClass": "role",
"id": "role_00003"
}
}
]
You need to remove the mode: 'no-cors' setting from your request. Setting no-cors mode is exactly the cause of the problem you’re having.
A no-cors request makes the response type opaque. The log snippet in the question shows that. Opaque means your frontend JavaScript code can’t see the response body or headers.
https://developer.mozilla.org/en-US/docs/Web/API/Request/mode explains:
no-cors — JavaScript may not access any properties of the resulting Response
So the effect of setting no-cors mode is essentially to tell browsers, “Don’t let frontend JavaScript code access the response body or headers under any circumstances.”
People sometimes try setting no-cors mode when a response doesn’t include the Access-Control-Allow-Origin response header or else because the request is one that triggers a CORS preflight, and so your browser does an OPTIONS preflight.
But using no-cors mode isn’t a solution to those problems. The solution is either to:
configure the server to which you’re making the request such that it sends the Access-Control-Allow-Origin response header, and such that it handles OPTIONS requests
or set up a CORS proxy using code from https://github.com/Rob--W/cors-anywhere/ or such; see the How to use a CORS proxy to get around “No Access-Control-Allow-Origin header” problems section of the answer at No 'Access-Control-Allow-Origin' header is present on the requested resource—when trying to get data from a REST API
In your then you should check if the response is OK before returning response.json:
.then(function (response) {
if (!response.ok) {
return Promise.reject('some reason');
}
return response.json();
})
If you want to have the error message in your rejected promise, you can do something like:
.then(function (response) {
if (!response.ok) {
return response.text().then(result => Promise.reject(new Error(result)));
}
return response.json();
})
I know this answer might be super late and might have been resolved but i just had the same issue today and I just needed to add a ',' at the end of the headers hash and i stopped getting the error
export function addContacts(formData) {
return(dispatch) => {
dispatch({type: 'POSTING_CONTACTS'});
console.log(formData)
return fetch(uri, {
method: 'POST',
body: JSON.stringify({contact: {name: formData.name, phone_number: formData.phoneNumber}}),
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
})
.then(response => {
return response.json()
}).then(responseJSON => {
console.log(responseJSON)
return dispatch({type: 'ADD_CONTACT', payload: responseJSON});
})
}
}
You can avoid the problem with CORS policy by adding in the header of php or another server endpoint the row:
<?php
header('Access-Control-Allow-Origin: *');
//or
header('Access-Control-Allow-Origin: http://example.com');
// Reading JSON POST using PHP
$json = file_get_contents('php://input');
$jsonObj = json_decode($json);
// Use $jsonObj
print_r($jsonObj->message);
...
// End php
?>
Model of working fetch code with POST request is:
const data = {
optPost: 'myAPI',
message: 'We make a research of fetch'
};
const endpoint = 'http://example.com/php/phpGetPost.php';
fetch(endpoint, {
method: 'POST',
body: JSON.stringify(data)
})
.then((resp) => resp.json())
.then(function(response) {
console.info('fetch()', response);
return response;
});
Simply copy the following code and paste it on your web.config file under <system.webServer> tag.
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
</customHeaders>
</httpProtocol>

How to assign a json object in a particular localhost route to a variable in javascript

I am quite new to learning web development and have been stuck for hours and my knowledge of Javascript is not that well. I have a route localhost:8080/api/employee/ so that when I enter this route in my browser it shows a JSON
[
{
"id":1,
"employee_name":"Anders",
"department_id":4
},
{
"id":3,
"employee_name":"Brownea",
"department_id":17
},
{
"id":4,
"employee_name":"Chow",
"department_id":19
}
]
In the more specific route, localhost:8080/api/employee/{id}, it shows for instance id: 1
{data:
{
"id":1,
"employee_name":"Anders",
"department_id":4
}
}
I am trying to assign a json object in a particular localhost route to a variable in javascript.
Currently the problem is the number 3
In the javascript, I have tried this:
const fileHandler = async () => {
const res = await fetch("localhost:8080/api/employee/1")
.then((res) => {
console.log(res)
}
}
In the console it display:
XHRGEThttp://localhost:8080/api/employee/1
CORS Missing Allow Origin
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/api/employee/1. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 404.
Uncaught (in promise) TypeError: NetworkError when attempting to fetch resource. EmployeeRegis.js:34
I then try to do this in the javascript:
const res = await fetch("localhost:8080/api/employee/1", {
method: "GET",
mode: "no-cors",
})
.then((res) => {
console.log(res)
}
The console shows:
XHRGEThttp://localhost:8080/api/employee/1
[HTTP/1.1 404 Not Found 7ms]
Response { type: "opaque", url: "", redirected: false, status: 0, ok: false, statusText: "", headers: Headers, body: null, bodyUsed: false }
EmployeeRegis.js:34
I also try the mm107 answer and do this:
const res = await fetch("http://localhost:8080/api/employee/1", {
method: "GET",
mode: "no-cors",
}).then(res => {
console.log(res)
console.log(res.json())
res.json()
}).then(data => {
console.log(data)
})
It still return some error in console:
Response { type: "opaque", url: "", redirected: false, status: 0, ok: false, statusText: "", headers: Headers, body: null, bodyUsed: false }
EmployeeRegis.js:37
Promise { <state>: "pending" }
<state>: "rejected"
<reason>: SyntaxError: JSON.parse: unexpected end of data at line 1 column 1 of the JSON data
​​columnNumber: 0
fileName: ""
lineNumber: 0
message: "JSON.parse: unexpected end of data at line 1 column 1 of the JSON data"
stack: ""
<prototype>: SyntaxError.prototype { stack: "", … }
<prototype>: Promise.prototype { … }
EmployeeRegis.js:39
undefined EmployeeRegis.js:43
Uncaught (in promise) SyntaxError: JSON.parse: unexpected end of data at line 1 column 1 of the JSON data 2
I am using Golang as the backend, here is the handler (using GoFiber and gorm):
func HandlerEmployeeID(ctx *fiber.Ctx) error {
employeeID := ctx.Params("id")
var emply entity.Employee
error := database.DB.First(&emply, "id = ?", employeeID).Error
if error != nil {
return ctx.Status(404).JSON(fiber.Map{
"message": "failed get employee by the id",
})
}
// Previously in number 1 and 2, I pathetically return 404 status not found
// return ctx.Status(404).JSON(fiber.Map{
// "message": "successfully get employee by the id",
// "data": emply,
// })
// In the number 3, I have update it like this
return ctx.JSON(fiber.Map{
"message": "successfully get employee by the id",
"data": emply,
})
}
What I want on console.log(resSomething) returns this:
{data:
{
"id":1,
"employee_name":"Anders",
"department_id":4
}
}
How to do that?
Updated:
I just know that the mode: "no-cors" will not result in my expected response body. But, if I am not using that mode, it will keep returning an error in the console. I have tried to run parallelly lcp --proxyUrl http://localhost:8080/api/employee/1 but the CORS issue is still not handled.
You have to parse the response to JSON:
const res = await fetch("localhost:8080/api/employee/1", {
method: "GET",
mode: "no-cors",
})
.then(res => res.json())
.then(data => console.log(data))
And for the 404, it's probably some problem in the backend.
Allow
Options Method to work in your backend
Ensure your backend allows APIs to be called from another localhost.
The answer in first comment
const res = await fetch("localhost:8080/api/employee/1", {
method: "GET",
mode: "no-cors",
})
.then(res => res.json())
.then(data => console.log(data))
Is one part of the problem.

Sending JSON response in Flask "POST" Route

For reference, I've looked at these three questions:
Return JSON response from Flask view
Sending JSON and status code with a Flask response
Return a requests.Response object from Flask
As the title states, I am trying to return a JSON response from a flask post method.
The flask route is as follows:
#app.route('/login', methods=['POST'])
def login_route():
content = request.json
jwt = get_authorization(content['username'], content['password'])
return {'session-token': jwt} , 200
Where in my web application I would like to store the session-token in cookies (for now).
Currently on the web side I have the following code:
static postWithJSONResponse(url, body, onFetchComplete, onFetchFailure) {
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
})
.then((data) => onFetchComplete(data))
.catch(error => onFetchFailure(error));
}
componentDidMount() {
NetworkUtils.postWithJSONResponse('/login', this.state.userObject,
(data) => {
console.log(data);
},
(error) => {
});
}
Where userObject consists of username and password attributes.
I've tried a few variations of my flask route given the previous searches listed above:
Variations based on this question
from flask import jsonify
#app.route('/login', post)
def login_route():
content = request.json
d = {}
d['session-token'] = get_authorization(content['username'], content['password'])
return jsonify(d)
This variation from the same post:
#app.route('/login', post)
def login_route():
content = request.json
jwt = get_authorization(content['username'], content['password'])
app.response_class(
response=json.dumps({'session-token': jwt}),
status=200,
mimetype='application/json'
)
And lastly this attempt in which it was recommended to try creating a new response object:
from flask import Flask, request, make_response
#app.route('/login', methods=['POST'])
def login_route():
content = request.json
jwt = get_authorization(content['username'], content['password'])
resp = make_response(json.dumps({'session-token': jwt}), 200)
resp.headers['Content-Type'] = 'application/json'
return resp
All 3 of these attempts return the following json response objects to my chrome console:
Response {type: "basic", url: "http://localhost:3000/login", redirected: false, status: 200, ok: true, …}
body: ReadableStream
locked: false
__proto__: ReadableStream
bodyUsed: false
headers: Headers
__proto__: Headers
ok: true
redirected: false
status: 200
statusText: "OK"
type: "basic"
url: "http://localhost:3000/login"
__proto__: Response
As you can see, the response object doesn't contain a single entry for session-token. I'm really not sure what could be going wrong here and I'm not certain if the issue is with the flask route or the javascript anymore. Needless to say I could use some help and any recommendations would be appreciated. I am using Flask version 1.1.2.
The fetch API in Javascript is nice in the sense that it gives you options you're seeing when you log it like potentially accessing the body as stream (any day now TC 39...), but it's totally over-engineered for the 90% case. The magic incantation to remember is as follows:
New-fangled fancy-pantsed Javascript
// can only do this inside an async function
const resp = await fetch(someURL);
const jsonData = await resp.json();
// step 3, profit
(Very slightly) Old(er) school Javascript
fetch(someURL)
.then(resp => resp.json())
.then(jsonData => /* profit! */);
They are functionally equivalent. Use the newer one if you always wanted to sit at the cool kid's table at lunchtime in school. Use the other if you're a stodgy old curmudgeon like myself who complains about the kids these days with their cool tables and lunchtimes.

Using the fetch API to query an endpoint to get a json response and getting back an empty response error

Please forgive the long debug info and code. I figure its all relevant.
Im trying to query an api endpoint to get a ticket back. I query using postman and get the ticket response back as text that is shown in the headers tab. However, I want to use java script to get that ticket back and do other stuff with it. When i run the script, it logs an error to the console:
SyntaxError: Unexpected end of input
at FetchDemo.html:48
(anonymous) # FetchDemo.html:54
Promise.catch (async)
getTicket # FetchDemo.html:53
the response i get is this:
type: "opaque"
url: ""
redirected: false
status: 0
ok: false
statusText: ""
headers: Headers {}
body: (...)
bodyUsed: false
__proto__: Response
My code is below:
<script>
let URL = 'https://jsonplaceholder.typicode.com/posts/';
let TabURL = 'http://15.222.0.10/trusted/?username=admin';
document.getElementById("getTicket").addEventListener('click',getTicket);
function getTicket() {
console.log("in side getTicket");
//setup an options array to pass into the fetch call
let options = {
method: 'POST',
mode: 'no-cors',
headers: {
'Accept' : 'application-json,*/*',
'Content-Type' : 'application:json'
}
};
console.log(TabURL);
console.log(options);
fetch (TabURL,options)
.then(response => {
console.log(response);
return response.json();
})
.then(response => {
console.log(response);
})
.catch(error => {
console.error(error);
});
}
</script>
From the specs:
An opaque filtered response is a filtered response whose type is "opaque", URL list is the empty list, status is 0, status message is the empty byte sequence, header list is empty, and body is null.
The reason you're getting an opaque response, is because you use the no-cors flag. If you want to do cross-domain requests such as these, you need CORS.
https://fetch.spec.whatwg.org/#concept-filtered-response-opaque
https://developer.mozilla.org/en-US/docs/Web/API/Response/type

How can I log in to an API and catch login errors using fetch on React Native?

I'm making an application that requires login to an API. I have a login form which sends the ID number and password to the API, and the API should respond like this:
[
{
"user_id":"032984",
"user_number":"140521351",
"token":"990nZtMtEUUMY"
}
]
If there is a login error, the API responds with:
[
{
"ERROR": "INVALID PASSWORD | NOT FOUND 1SELECT user_id, lastname, password, user_number FROM user where user_number = 'INVALIDVALUE'",
},
]
I want to be able to catch a login error with an if statement, like if there is the ERROR object in this JSON, display an alert, else login and save the user_id and token to variables I can use in different screens of the app to send more requests to the API, get those responses in JSON, and show the data I need.
How can I make this happen?
So far, here's the code for my login function:
// login function
_userLogin = () => {
this.setState({ isLoggingIn: true, message: '' });
// send request to API properly
fetch("https://api.company.com/v4/users/json.php", {
method: "POST",
// our headers
headers: {
'Content-Type': 'application/json',
'Connection': 'close',
'Accept': '*/*',
'User-Agent': 'InternalApp/0.1 (InternalApp; ReactNative) Expo/33',
'Accept-Language': 'en-US;q=1.0',
'Accept-Encoding': 'gzip, deflate'
},
// body of the request with number/password
body: JSON.stringify({
user_number: this.state.number,
password: this.state.password,
}),
})
.then(response => {
return response.json(); // make it json?!
}).then(responseData => {
// debug messages
console.log(responseData);
console.log("Moving on to parsing JSON"); // CODE WORKS TO HERE
// parse json
var jsonObj = JSON.parse(responseData); // CODE STUCK HERE
// debug messages
console.log("JSON parsed");
if (jsonObj.ERROR)
console.log("Error caught");
else
this.setState(prevState => ({
credentialJson: prevState.credentialJson = responseData,
isLoggingIn: false,
}))
this.props.onLoginPress();
})
};
I'm really new to React Native and StackOverflow, please excuse any formatting issues with the question. I hope I've provided enough detail.
Based on your comments to this answer and the output of console.log(responseData) your responseData is an Array and your data is an Object inside the first array element. Access your data through responseData[0]. For example:
responseData[0].token
//Should return "990nZtMtEUUMY"
Here is how you would check if there is an error set:
if(responseData[0].ERROR){}
Your fetch library fetch returns a Promise so if the API actually throws an error you can add a catch statement.
fetch(url).then().catch((error) => {
console.log("Error", error);
});
The other thing is the reason your code is halting at JSON.parse is that you already parsed the json in a previous .then clause (response.json()) so your trying to parse an object, not a string, which JSON.parse expects.

Categories

Resources