I am trying to connect my flask api to my react app and all I want from that is to display the string I returned from the flask on react app.
In doing so, I used useEffect(), but my variable in react is not changed but just keeps the original state. When I use console.log to see the value that I am passing to the variable, it shows an error saying "Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0".
App.js looks like:
import React, {Component, useState, useEffect} from 'react';
import './App.css';
function App() {
const [block, setBlock] = useState(0);
useEffect(() => {
fetch('/video').then(res => res.json()).then(data => {
setBlock(data.text);
console.log(data.text);
});
}, []);
return (
<div className="App">
<header className="App-header">
<p>Text: {block}</p>
</header>
</div>
);
}
export default App;
part of flask(flask.py):
#app.route('/video')
def video():
return {'text': text}
Any insight would be greatly appreciated!
If you want to return JSON from a Flask route use flask.jsonify.
from flask import jsonify
#app.route('/video')
def video():
return jsonify({'text': text})
Related
I've setup my project, i.e. I have created a front-end in React, and a back-end in Flask.
In my front-end I call my back-end with a post method with the following code:
function POST(path, data) {
return fetch(`${fetchUrl}${path}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + RequestAccessToken(),
},
body: JSON.stringify(data)
}
)
}
Where RequestTokenAccess():
const { instance, accounts, inProgress } = useMsal();
const [accessToken, setAccessToken] = useState(null);
const name = accounts[0] && accounts[0].name;
function RequestAccessToken() {
const request = {
...loginRequest,
account: accounts[0]
};
instance.acquireTokenSilent(request).then((response) => {
setAccessToken(response.accessToken);
}).catch((e) => {
instance.acquireTokenPopup(request).then((response) => {
setAccessToken(response.accessToken);
});
});
}
And then just the following to actually make the call to the back-end:
const [data, setData] = useState()
function fetchData(e) {
e?.preventDefault();
POST('/my_app', { data: data }).then(
async (response) => {
const json = await response.json()
setData(json.return_data)
}
)
}
So for the front-end everything is working. I can get a MS Login that authorizes me so I can actually se the front-end, and I can also get a token from the RequestAccessToken function, which is given as a header to the back-end call. So everything seems to be set on the front-end part. However, the back-end calls also need to be secure is my guess, but I am not sure how that works.
Basically my app.py file looks something like:
from flask import Flask, request, jsonify
from my_app_func import MyAppFunc
app = Flask(__name__)
#app.post("/api/my_app")
def my_app():
data = request.json.get("data")
return_data = MyAppFunction(data)
return return_data
So basically, what do I need in order secure back-end calls ? I have the token as a Bearer Token in the post call. But what is the next step ? What do I actually do with it ?
I also have the same question, but couldn't find answer. Below is what works for me:
If you want to validate the user from flask, you can send the token along with your request from react.
Then within flask, validate the user by making a request to microsoft graph api.
Here is one example how to do this:
https://github.com/Azure-Samples/ms-identity-python-flask-webapp-call-graph
Another question for you is why you can directly concatenate RequestAccessToken() as a string? isn't it only call the setAccessToken? I ask because in my react app, I don't know how to export the token so that other function can use it. I ended up using the MSAL.js v2, not the one for react.
You have to register another app on the portal azure and and give permissions to the api and configure that in the another app in portal azure . Try to do something in that space.
I am trying to send a post request from react to flask using the following code:
function App() {
const [currentTime, setCurrentTime] = useState(0);
const [accessToken, setAccessToken] = useState(null);
const clicked = 'clicked';
useEffect(() => {
fetch('/time').then(res => res.json()).then(data => {
setCurrentTime(data.time);
});
}, []);
useEffect(() => {
// POST request using fetch inside useEffect React hook
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'React Hooks POST Request Example',action: 'clicked' })
};
var myParams = {
data: requestOptions
}
fetch('http://127.0.0.1:5000/login', myParams)
.then(response => response.json())
.then(data => setAccessToken(data.access_token));
// empty dependency array means this effect will only run once (like componentDidMount in classes)
}, []);
return (
<div className="App">
<div className="leftPane">
<div className="joyStick" >
<Joystick size={300} baseColor="gray" stickColor="black" ></Joystick>
</div>
<p>The current time is {currentTime}.</p>
<p>The access token is {accessToken}.</p>
</div>
And the flask code is
from __future__ import print_function
from flask import Flask, jsonify, request
from flask_cors import CORS
import time
from flask import Flask
import sys
robotIP="10.7.4.109"
PORT=9559
app = Flask(__name__)
access_token='a'
action="d"
#app.route('/time')
def get_current_time():
return {'time': time.time()}
#app.route('/login', methods=['POST'])
def nao():
nao_json = request.get_json()
if not nao_json:
return jsonify({'msg': 'Missing JSON'}), 400
action = nao_json.get('action')
access_token= action+'s'
print(access_token, file=sys.stderr)
return jsonify({'access_token': access_token}), 200
But every time I run both them both, I get the 'msg': 'Missing JSON' message I have defined and the data from react is never available in flask,even though the get request works.I am not sure what I am doing wrong here.
The problem actually is that this is a cross origin request which must be allowed by the server.
Place this function on your Python code:
#app.after_request
def set_headers(response):
response.headers["Access-Control-Allow-Origin"] = "*"
response.headers["Access-Control-Allow-Headers"] = "*"
response.headers["Access-Control-Allow-Methods"] = "*"
return response
Note:
If react is served from the same server this won't be necessary.
You should set the value of these headers to be as strict as possible on production. The above example is too permissive.
You could serve your React aplication from Flask, thus not requiring these headers to be set. You could use something like this to serve the main react file:
#app.route('/', defaults={'path': ''})
#app.route('/<string:path>')
#app.route('/<path:path>')
def index(path: str):
current_app.logger.debug(path)
return bp_main.send_static_file('path/to/dist/index.html')
Where path/to/dist/index.html would be on the static folder.
See more at:
MDN Web docs
Stackoverflow: How to enable CORS in flask
Stackoverflow: Catch all routes for Flask
I've made a simple flutter web app with TextField() and a Text() widget. When I press the button I expect it to send a query to the simple flask app made using python where it will returns the inverted text and I display that on the Text widget.
NOTE: Inverting text is not the actual task. Its just to check if I'm
able to get the data.
Python Code:
This code works when I run on the chrome.
#performing flask imports
from flask import Flask,jsonify
from flask.globals import request
app = Flask(__name__) #intance of our flask application
#Route '/' to facilitate get request from our flutter app
#app.route("/api",methods=["GET"])
def function():
d = {}
text = str(request.args["Query"])
text = text[::-1]
d["query"] = text
return jsonify(d)
if __name__ == "__main__":
app.run()
Flutter Code:
main.dart
import 'package:flutter/material.dart';
import 'api.dart';
import 'dart:convert';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String url;
var data;
String queryText = "Query";
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("PYTHON AND FLUTTER"),
),
body: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(10.0),
child: TextField(
onChanged: (value) {
url = "http://10.0.2.2:5000/api?Query=" + value.toString();
},
decoration: InputDecoration(
hintText: "Search Anything Here",
suffixIcon: GestureDetector(
onTap: () async {
data = await getData(url);
var decodedData = jsonDecode(data);
setState(() {
queryText = decodedData["Query"];
});
},
child: Icon(Icons.search))),
),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
queryText,
style: TextStyle(fontSize: 30.0, fontWeight: FontWeight.bold),
),
),
],
),
),
);
}
}
api.dart
import 'package:http/http.dart' as http;
Future getData(url) async {
http.Response response = await http.get(url);
return response.body;
}
I followed the tutorial from : This Youtube Video
I'm running the python code via Command Prompt and flutter app through VScode.
I'm getting the following errors:
Error: Expected a value of type 'Uri', but got one of type 'String'
at Object.throw_ [as throw] (http://localhost:50523/dart_sdk.js:5348:11)
at Object.castError (http://localhost:50523/dart_sdk.js:5319:15)
at Object.cast [as as] (http://localhost:50523/dart_sdk.js:5635:17)
at Function.as_C [as as] (http://localhost:50523/dart_sdk.js:5263:19)
at getData (http://localhost:50523/packages/word_prediction/api.dart.lib.js:29:47)
at getData.next (<anonymous>)
at runBody (http://localhost:50523/dart_sdk.js:39211:34)
at Object._async [as async] (http://localhost:50523/dart_sdk.js:39242:7)
at Object.getData (http://localhost:50523/packages/word_prediction/api.dart.lib.js:28:18)
at main._MyAppState.new.<anonymous> (http://localhost:50523/packages/word_prediction/main.dart.lib.js:422:48)
at Generator.next (<anonymous>)
at runBody (http://localhost:50523/dart_sdk.js:39211:34)
at Object._async [as async] (http://localhost:50523/dart_sdk.js:39242:7)
at http://localhost:50523/packages/word_prediction/main.dart.lib.js:421:210
at tap.TapGestureRecognizer.new.invokeCallback (http://localhost:50523/packages/flutter/src/gestures/recognizer.dart.lib.js:203:18)
at tap.TapGestureRecognizer.new.handleTapUp (http://localhost:50523/packages/flutter/src/gestures/tap.dart.lib.js:417:40)
at tap.TapGestureRecognizer.new.[_checkUp] (http://localhost:50523/packages/flutter/src/gestures/tap.dart.lib.js:223:12)
at tap.TapGestureRecognizer.new.acceptGesture (http://localhost:50523/packages/flutter/src/gestures/tap.dart.lib.js:199:23)
at arena.GestureArenaManager.new.sweep (http://localhost:50523/packages/flutter/src/gestures/arena.dart.lib.js:222:31)
at binding$5.WidgetsFlutterBinding.new.handleEvent (http://localhost:50523/packages/flutter/src/gestures/binding.dart.lib.js:402:27)
at binding$5.WidgetsFlutterBinding.new.dispatchEvent (http://localhost:50523/packages/flutter/src/gestures/binding.dart.lib.js:381:24)
at binding$5.WidgetsFlutterBinding.new.dispatchEvent (http://localhost:50523/packages/flutter/src/rendering/layer.dart.lib.js:6107:13)
at binding$5.WidgetsFlutterBinding.new.[_handlePointerEventImmediately] (http://localhost:50523/packages/flutter/src/gestures/binding.dart.lib.js:352:14)
at binding$5.WidgetsFlutterBinding.new.handlePointerEvent (http://localhost:50523/packages/flutter/src/gestures/binding.dart.lib.js:325:43)
at binding$5.WidgetsFlutterBinding.new.[_flushPointerEventQueue] (http://localhost:50523/packages/flutter/src/gestures/binding.dart.lib.js:314:14)
at binding$5.WidgetsFlutterBinding.new.[_handlePointerDataPacket] (http://localhost:50523/packages/flutter/src/gestures/binding.dart.lib.js:304:65)
at Object.invoke1 (http://localhost:50523/dart_sdk.js:185426:7)
at _engine.EnginePlatformDispatcher.__.invokeOnPointerDataPacket (http://localhost:50523/dart_sdk.js:165747:15)
at _engine.PointerBinding.__.[_onPointerData] (http://localhost:50523/dart_sdk.js:166405:49)
at http://localhost:50523/dart_sdk.js:166863:28
at http://localhost:50523/dart_sdk.js:166816:16
at http://localhost:50523/dart_sdk.js:166509:11
I don't know what I'm doing wrong here.
Try this:
Future getData(String url) async {
var response = await http.get(Uri.parse(url));
return response.body;
}
Along this change https://stackoverflow.com/a/66517561/13592012 mentioned by Ουιλιαμ Αρκευα.
There was another problem:
When I use Break point it shows file call by blinding.dart
This was because of CORS.
Cross Origin Request(CORS) error which was because I did not set this up in my server.
This is especially if your flutter web application is not running in the same domain as the server where you api is running. Even if its on the same machine, you will have to allow the request from certain domain and ports.
This can be done by adding the following lines to flask code:
response.headers.add("Access-Control-Allow-Origin", "*")
and
response.headers.add("Access-Control-Allow-Credentials", "true")
So the updated code will be:
#performing flask imports
from flask import Flask,jsonify
from flask.globals import request
app = Flask(__name__) #intance of our flask application
#Route '/' to facilitate get request from our flutter app
#app.route("/api",methods=["GET"])
def word_predictor():
d = {}
text = str(request.args["Query"])
text = text[::-1]
d["Query"] = text
response = jsonify(d)
response.headers.add("Access-Control-Allow-Origin", "*")
response.headers.add("Access-Control-Allow-Credentials", "true")
return response
if __name__ == "__main__":
app.run()
And, with these two changes, code works fine.
So currently I am trying to learn how to connect a flutter front end with a Python backend using flask and I am making a basic project where you basically input a number in the flutter app the number you input being the index of a list I have stored in my python app the python app will then return the number at the index you specified and it will be displayed on the screen. The issue I am having is that when I run the applications the flutter application just displays an error message. I have tried a whole bunch of things and can't figure out what is wrong
Here is the link to the screenshot of my Flutter App (Includes error message received from the server):
https://imgur.com/a/K6KYRAu
Here is my Python Code
from flask import Flask
from flask_restful import Api, Resource
app = Flask(__name__)
api = Api(app)
myList = [3,4,8,1,9,4,2,0,2,9,3,8,4,7,2,7,0,1]
#app.route('/', methods = ['GET'])
class Update(Resource):
def get(self,pos):
if(pos > len(myList)):
return {"num" : "invalid number: out of list range"}
else:
return {"num": str(myList[pos])}
api.add_resource(Update, "/Update/<int:pos>")
if __name__ == "__main__":
app.run(debug=True)
here is my Flutter Code:
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<Entry> fetchEntry() async {
final response =
await http.get('https://127.0.0.1:5000/Update/3');
if (response.statusCode == 200) {
return Entry.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load Entry');
}
}
class Entry {
final String number;
Entry({this.number});
factory Entry.fromJson(Map<String, dynamic> json) {
return Entry(
number: json['num'],
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Future<Entry> futureEntry;
#override
void initState() {
super.initState();
futureEntry = fetchEntry();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<Entry>(
future: futureEntry,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.number);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
),
);
}
}
Also I have double checked my dependencies in my pubspec.yaml file and AndroidManifest.xml file so that shouldn't be the issue. Any help would be greatly appreciated, thanks.
EDIT: for clarification I am using and android emulator for this and have tried using a different ip address for my flutter get request (I found the ip address when trying to figure out how to fix this problem and it said that ip address needs to be used for android emulators) but it still returned the same error when I did that.
Not exactly sure why this is throwing this error as you do specify the port as 5000, but a simple solution would be to change the flask port to 50162.
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=50162)
EDIT:
On further review, you might want to change your http request in the flutter code to http, not https.
I figured out the solution:
for my get request I need to be using an IP address that can be used by android the IP address that android uses is 10.0.2.2 I found this solution on this stack overflow post:
How to connect to my http://localhost web server from Android Emulator
I have a simple Flask app like so:
import msgpack
from flask import Flask, render_template
app = Flask(__name__)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/test')
def test():
return msgpack.packb([1, 2, 3])
#app.route('/ping')
def ping():
return "Pong"
if __name__ == '__main__':
app.run(debug=True, port=5000)
In the Flask route /test, I'm using msgpack to pack an array which is returned as a response. When this response is printed to the console, it looks something like this: b'\x93\x01\x02\x03'
I'm using AngularJS 1.7 in my front end which looks like:
<!doctype html>
<html ng-app="MyApp">
<body ng-cloak>
<div class="container" ng-controller="MyController">
<h1>Hello, {{text}}</h1>
<button class="btn btn-primary" ng-click="ping()">Ping</button>
<button class="btn btn-primary" ng-click="getMsgpackData()">Get Msgpack Data</button>
</div>
<script src="jquery-3.3.1.slim.min.js"></script>
<script src="/angular.js/1.7.2/angular.min.js"></script>
<script src="https://rawgithub.com/kawanet/msgpack-lite/master/dist/msgpack.min.js"></script>
<script>
var myApp = angular.module("MyApp", []);
myApp.controller("MyController", ["$scope", "$http", function($scope, $http) {
$scope.text = "World";
$scope.ping = function() {
$http.get("/ping").then(function success(response) {
$scope.text = response.data;
}, function error(response) {
console.log(response);
});
}
$scope.getMsgpackData = function() {
$http.get("/test").then(function success(response) {
console.log(response);
$scope.text = msgpack.decode(response.data);
}, function error(response) {
console.log(response);
});
}
}]);
</script>
</body>
</html>
When the getMsgpackData function is invoked in MyController, I get the following error in the browser's console:
Error: Invalid type: 0x�
at n.r [as decode] (msgpack.min.js:1)
at n.fetch (msgpack.min.js:1)
at n.s [as read] (msgpack.min.js:1)
at Object.n [as decode] (msgpack.min.js:1)
at success ((index):46)
at angular.js:17396
at m.$digest (angular.js:18557)
at m.$apply (angular.js:18945)
at k (angular.js:12799)
at V (angular.js:13056) "Possibly unhandled rejection: {}"
It seems like the first HEX value \x93 is not being decoded.
I'm using kawanet/msgpack-lite in the front end as I found that it can be used in a browser.
Please help!
The error message tells you you sent the wrong type of object to the decode() method. The buffer decoding documentation shows that only buffers, arrays and Uint8Array objects are supported.
Change the response type to arraybuffer (from the default text), then feed the response data to msgpack.decode() as a Uint8Array typed array:
$http.get("/test", {responseType: "arraybuffer"}).then(function success(response) {
var arr = new Uint8Array(response.data);
console.log(arr);
$scope.text = msgpack.decode(arr);
}, function error(response) {
console.log(response);
});
While not necessary here, you do want to set a different content type on your Flask response. Currently the /test route is advertising the content as text/html, while you really should be labelling as application/msgpack:
#app.route('/test')
def test():
return msgpack.packb([1, 2, 3]), {'content-type': 'application/msgpack'}