Sending JSON response in Flask "POST" Route - javascript

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.

Related

How to return error from Flask so JavaScript catches it

I have an application running a Flask backend with a React frontend and I'm trying to do some error handling for the front end and display the error message to the user. Here is a simplified version of my files:
Flask
#app.route('/api/download', methods=['POST'])
def download():
data = request.json
#Get data from external API via a post request here
try:
req = urllib.request.Request(url, data, headers)
with urllib.request.urlopen(req) as f:
res = f.read()
print("Success")
data = json.loads(res.decode())
df = pd.json_normalize(data['observationList'])
#Do some pandas magic here
retData = df.to_json(orient="records")
return retData
except Exception as e:
pprint(e)
return jsonify(message='password_update_error'),500
JavaScript
fetch("/api/download", {
method:"POST",
cache: "no-cache",
headers:{
'Content-Type': 'application/json'
},
body: JSON.stringify({
values: this.state.values
})
}).then((response) => response.json())
.then(data => {
this.setState({ sdata: data }, () => {
// click the CSVLink component to trigger the CSV download
setTimeout(() => {
this.csvLink.current.link.click();
});
})
}).catch(err => {
errorBox.innerHTML = "<span style='color: red;'>"+
"Could not get data.</span>"
})
This currently gives me two errors:
TypeError: Data should be a "String", "Array of arrays" OR "Array of objects"
TypeError: Cannot read property 'link' of null
I understand what the problem is but I don't know how to return errors so javascript catches it and then displays it in the box like I want it to. Any ideas? Thank you!
At first glance, why don't you just return the error code? It should solve the errors you're getting.
#app.route('/api/download', methods=['POST'])
def download():
data = request.json
#Get data from external API via a post request here
try:
req = urllib.request.Request(url, data, headers)
with urllib.request.urlopen(req) as f:
res = f.read()
print("Success")
data = json.loads(res.decode())
df = pd.json_normalize(data['observationList'])
#Do some pandas magic here
retData = df.to_json(orient="records")
return retData
except Exception as e:
pprint(e)
return 500

I want to route to a new web page after a post of data

I have this route defined and while the render_template has worked for me before, I can't seem to figure out why the web page stays on the existing page. I sure it hits and processes all of the way through, as I have returned a 200, Ok and received back on the javascript side. I even tried to redirect to processes the request as a GET and allow the get to render_template, but still no luck. Any ideas?
Route code
#app.route("/view", methods=["GET","POST"])
#cross_origin()
def view_playlist():
global playlist_item
if request.method == 'POST':
playlist_item = request.get_json()
print("playlist POST")
return render_template('/view.html', result= playlist_item)
# return redirect(url_for('view_playlist'))
else:
print("This is a GET")
return render_template('/view.html', result= playlist_item)
# app.send_static_file("index.html", result= playlist_item)
The javascript post is as follows
url = local + '/view'
fetch(url,{
headers: {
'Content-Type': 'application/json'
},
method: 'POST',
body:JSON.stringify(itemsPlaylist)
}).then(function(data) {
// location.replace(local + '/view.html');
})
Looking at the code can't think of any obvious reasons, but worth printing the response in then and also put in a catch block.
You could also use finally to do the routing
fetch(myRequest)
.then(function(response) {})
.catch(function(error) { console.error(error); })
.finally(function() { .. do the routing here });

Trouble Recovering Data From API with Async in React

I am trying to run an api in react and have the response returned as a variable. The api returns the title of a given webpage.
DETAILS
The api uses the following URL {with a website url appended to the end}
http://textance.herokuapp.com/title/
A manual submission of this URL http://textance.herokuapp.com/title/www.antstand.com/ will result in "Antstand the bamboo laptop stand" being displayed in the browser. I am trying to use this api to set variable in my app.
MY CURRENT CODE
async function titleAPI(props) {
const baseUrl = 'http://textance.herokuapp.com/title/'
const response = await fetch(baseUrl + props)
const data = await response
console.log('FUNCTION OUTPUT: ',data)
return data
}
titleAPI(myUrl).then(console.log)
On running the code my unexpected ouptut is below (I was expecting : "Antstand the bamboo laptop stand")
FUNCTION OUTPUT:
Response {type: "cors", url: "http://textance.herokuapp.com/title/www.antstand.com/", redirected: false, status: 200, ok: true, …}
type: "cors"
url: "http://textance.herokuapp.com/title/www.antstand.com/"
redirected: false
status: 200
ok: true
statusText: "OK"
headers: Headers {}
body: (...)
bodyUsed: false
__proto__: Response
I think Cors refers to a cross origin error.
body: (...) and type:'cors' means you are getting opaque response because of CORS. Either send 'Access-Control-Allow-Origin' to your origin ( or * for all origins ), or configure proxy on frontend to change origin header of all requests so that server could not receive it as CORS. axios has options to configure proxy. create-react-app also provides mechanism to configure proxy in development environment.
try this way it's display your expecting result
"Antstand the bamboo laptop stand" show
const titleAPI=async props =>{
const baseUrl = 'http://textance.herokuapp.com/title/'
const url = baseUrl + props
const response = await fetch(url,{
method:'GET',
mode:"cors",
credentials:"same-origin",
cache:"no-cache",
headers:{
"Content-Type":"application/json",
"Access-Control-Allow-Origin":"*",
},
redirect:"follow",
referrer:"no-referrer"
})
let data = await response.text()
return data
}
titleAPI('www.antstand.com/').then(data=>console.log(data))

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