I’m trying to learn Webdev and as part of my first project, I have built a simple chat-bot-app. The app consists of a Python-Flask backend (running on localhost/5000) and a Flutter app front end. The Flutter app takes our input, sends it to the Flask server, and will print us the response.
When I run the app in debug mode locally, it is working fine as expected. However, when I build a web version of the app using
flutter build web
and then try to run the app from /build/web/ by creating a Python server
python -m http.server 8081
There is a problem, the web app is launching in the browser: however, now it can not send and receive messages from the server running at localhost/5000. I’m getting an error:
flutter.js:368 Exception while loading service worker: Error: Service Worker API unavailable.
The current context is NOT secure.
Read more: https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts
at FlutterServiceWorkerLoader.loadServiceWorker (flutter.js:130:11)
at FlutterLoader.loadEntrypoint (flutter.js:366:33)
at (index):46:23
(anonymous) @ flutter.js:368
0.0.0.0/:1 Access to XMLHttpRequest at 'http://127.0.0.1:5000/api/chat' from
origin 'http://0.0.0.0:8081' has been blocked by CORS policy:
The request client is not a secure context and the resource is in more-private address space `local`.
main.dart.js:6506 Uncaught Error
at Object.alx (main.dart.js:6573:24)
at TE.$1 (main.dart.js:73160:53)
at abl.adR (main.dart.js:36153:34)
at abl.DP (main.dart.js:36155:21)
at a95.$0 (main.dart.js:35896:11)
at Object.r4 (main.dart.js:5825:40)
at az.mx (main.dart.js:35827:3)
at Object.ayw (main.dart.js:5908:8)
at a4N.$1 (main.dart.js:35938:9)
at a8N.$1 (main.dart.js:39265:21)
127.0.0.1:5000/api/chat:1 Failed to load resource: net::ERR_FAILED
It basically says I can not communicate with ‘http://127.0.0.1:5000/api/chat’ from origin ‘http://0.0.0.0:8081’ due to CORS security policy. When I try to run the same web app, by disabling the browser security, I’m no longer getting this error and the web app is able to communicate with the server.
chromium-browser --disable-web-security
How can I fix this without sacrificing security? I wish to deploy the app online after fixing this.
Why is this happening?
Here is the link to the Github repo which has the full code:
https://github.com/sri-gpt/simple_chat_app
I’m using:
- Ubuntu version 22.04 LTS,
- Flutter 3.13.0-0.2.pre
- Dart 3.1.0
- DevTools 2.25.0
- Flask 2.3.2
- Flask-Cors 4.0.0
- Python 3.9
Sample code:
Python Flask server
from flask import Flask, request, jsonify
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
@app.route('/')
def home():
return 'ServerDEployed'
@app.route('/about')
def about():
return 'About'
@app.route('/api/chat', methods=['POST'])
def chat():
data = request.get_json()
input_text = data.get('input_text')
return jsonify({"response_mal": str("response_recived:")+str(input_text)})
if __name__ == '__main__':
app.run(debug=True)
Here is the main.dart file of the Flutter app:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const ChatApp());
}
class ChatApp extends StatelessWidget {
const ChatApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Chat_app',
theme: ThemeData(
primarySwatch: Colors.indigo, // Change primary color
fontFamily: 'Montserrat', // Add custom font (Montserrat can be replaced with your preferred font)
),
home: const MyHomePage(title: 'Chat_app'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _messages = <Map<String, dynamic>>[];
final _controller = TextEditingController();
final double fontText = 24.0; // <-- This is your pseudo variable for font size
Future<void> _sendText() async {
if (_controller.text.isEmpty) {
return;
}
setState(() {
_messages.add({"text": _controller.text, "type": "outgoing"});
});
final url = Uri.parse('http://127.0.0.1:5000/api/chat');
final response = await http.post(
url,
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'input_text': _controller.text,
}),
);
if (response.statusCode == 200) {
final Map<String, dynamic> data = jsonDecode(response.body);
setState(() {
_messages.add({"text": data["response_mal"], "type": "incoming"});
_controller.text = "";
});
} else {
throw Exception('Failed to send message');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title, style: TextStyle(fontSize: fontText)), // <-- Apply font size here.
),
body: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
padding: const EdgeInsets.all(8),
itemCount: _messages.length,
itemBuilder: (_, int index) {
final message = _messages[index];
final isOutgoing = message["type"] == "outgoing";
return Container(
margin: const EdgeInsets.symmetric(vertical: 5),
padding: const EdgeInsets.all(10),
alignment:
isOutgoing ? Alignment.centerRight : Alignment.centerLeft,
child: Text(
message["text"],
style: TextStyle(fontSize: fontText), // <-- Apply font size here.
),
decoration: BoxDecoration(
color: isOutgoing ? Colors.orange[100] : Colors.blue[100],
borderRadius: BorderRadius.circular(5),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 5,
offset: Offset(0, 2),
),
],
),
);
},
),
),
Row(
children: <Widget>[
Expanded(
child: Padding(
padding: EdgeInsets.all(8.0),
child: TextField(
controller: _controller,
onSubmitted: (_) => _sendText(),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Enter your message',
),
style: TextStyle(fontSize: fontText), // <-- Apply font size here.
),
),
),
Padding(
padding: EdgeInsets.all(8.0),
child: ElevatedButton( // Change the send button to ElevatedButton
onPressed: _sendText,
child: Icon(Icons.send),
),
),
],
),
],
),
);
}
}
2
Answers
When you run your app in debug mode, it is likely the app is not enforcing CORS rules. However, when you build and serve the web app, CORS becomes a factor, especially when your Flutter app and your Flask backend are served from different origins (in this case, different ports on localhost).
Try and include
corydolphin/flask-cors
, a Flask extension for handling Cross Origin Resource Sharing (CORS), making cross-origin AJAX possible.Instead of using the default
CORS(app)
, you can specify which origins are allowed to make requests to your Flask backend. For instance:That will allow requests from your Flutter web app hosted on
http://0.0.0.0:8081
but deny others.And make sure you are binding your Flask app to
0.0.0.0
when running so that it is accessible to external requests, not just those coming fromlocalhost
.But the error
Exception while loading service worker: Error: Service Worker API unavailable.
suggests that there is a problem with service workers when served on an insecure context (HTTP).Consider using HTTPS for serving your web applications.
You can follow "Securing your Flask website with SSL for HTTPS using Let’s Encrypt" from Harrison Kinsley as an example.
Header set Access-Control-Allow-Origin "*"