skip to Main Content

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:
Web app screenshot

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


  1. However, when I run the Chromium browser by disabling web security, $chromium-browser --disable-web-security, the app is working fine. So it seems that this is caused by some CORS security policy.
    How can I fix this without sacrificing security?

    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:

    from flask_cors import CORS
    
    cors = CORS(app, resources={r"/api/*": {"origins": "http://0.0.0.0:8081"}})
    

    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 from localhost.

    if __name__ == '__main__':
        app.run(host='0.0.0.0', debug=True)
    

    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.

    Login or Signup to reply.
  2. Header set Access-Control-Allow-Origin "*"

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search