Problems with CORS. Flask & # 8596; Angularjs

Launching a new project with the angularjs client application and a flash application providing api. I use mongodb as a database. I had to immediately rule out jsonp, as I would need the POST feature across different ports. So, we have localhost: 9000 for the angular application and localhost: 9001 for the flash application.

I went through and made the changes necessary for CORS in my API, as well as in my angular files. See Source below. The first problem I ran into was that there is an error that CORS resolves the header does not recognize localhost in Chrome. I updated my hosts file to use moneybooks.dev and this worked for my GET requests without using JSONP.

Now, to the problems that I am facing. When sending a POST request, its message is Origin http://moneybooks.dev:9000 is not allowed by Access-Control-Allow-Origin What? GET may pass, but the POST is rejected. I see that the request went into the flask, but it returns HTTP 400. I need help creating POST requests.

Another problem that may be related is that sometimes, for my GET requests, the GET request does not work at all. As in BudgetCtrl function. In # / budgetgets / budgetID, the budget name sometimes doesn't load at all. I check the flag log and cannot see the request. Then I click the Refresh button, I see a request, the name of the budget is displayed on the page, however, I see an error in the flack log. [Errno 10053] An established connection was aborted by the software in your host machine. A connection error that appears only in the flag log when a GET request is successful.

Are these issues related? Can anyone see what I'm doing wrong?

app.js

 'use strict'; angular.module('MoneybooksApp', ['ui.bootstrap', 'ngResource']) .config(['$routeProvider', '$httpProvider', function ($routeProvider, $httpProvider) { $httpProvider.defaults.useXDomain = true; delete $httpProvider.defaults.headers.common['X-Requested-With']; $routeProvider .when('/', { templateUrl: 'views/main.html', controller: 'MainCtrl' }) .otherwise({ redirectTo: '/' }); }]); 

budgets.js

 'use strict'; angular.module('MoneybooksApp') .config(['$routeProvider', function ($routeProvider) { $routeProvider .when('/budgets', { templateUrl: 'views/budgets-list.html', controller: 'BudgetListCtrl' }) .when('/budgets/:budgetID', { templateUrl: 'views/budget.html', controller: 'BudgetCtrl' }); }]) .controller('BudgetListCtrl', function ($scope, $http, $resource) { $scope.budgets = []; var init = function () { $scope.loadBudgets(); } $scope.loadBudgets = function() { $http.get('http://moneybooks.dev:9001/api/budgets') .success(function (data) { $scope.budgets = data; }) .error(function (data) { console.error(data); }); }; init(); }) .controller('BudgetCtrl', function ($scope, $http, $routeParams, $resource) { $scope.budget = {}; var init = function () { $scope.loadBudget(); }; $scope.loadBudget = function() { $http.get('http://moneybooks.dev:9001/api/budgets/'+$routeParams['budgetID']) .success(function (data) { $scope.budget = data; }) .error(function (data) { console.error(data); }); }; init(); }) .controller('TransactionCtrl', function ($scope, $http, $routeParams, $resource) { $scope.transactions = []; $scope.editing = false; $scope.editingID; var init = function () {}; $scope.syncUp = function () { $http.post('http://moneybooks.dev:9001/api/budgets/'+$routeParams['budgetID']+'/transactions', {transactions: $scope.transactions}); }; $scope.syncDown = function () { $http.get('http://moneybooks.dev:9001/api/budgets/'+$$routeParams['budgetID']+'/transactions') .success(function (transactions) { $scope.transactions = transactions; }); }; $scope.add = function() { $scope.transactions.push({ amount: $scope.amount, description: $scope.description, datetime: $scope.datetime }); reset(); $scope.defaultSort(); }; $scope.edit = function(index) { var transaction = $scope.transactions[index]; $scope.amount = transaction.amount; $scope.description = transaction.description; $scope.datetime = transaction.datetime; $scope.inserting = false; $scope.editing = true; $scope.editingID = index; }; $scope.save = function() { $scope.transactions[$scope.editingID].amount = $scope.amount; $scope.transactions[$scope.editingID].description = $scope.description; $scope.transactions[$scope.editingID].datetime = $scope.datetime; reset(); $scope.defaultSort(); }; var reset = function() { $scope.editing = false; $scope.editingID = undefined; $scope.amount = ''; $scope.description = ''; $scope.datetime = ''; }; $scope.cancel = function() { reset(); }; $scope.remove = function(index) { $scope.transactions.splice(index, 1); if ($scope.editing) { reset(); } }; $scope.defaultSort = function() { var sortFunction = function(a, b) { var a_date = new Date(a['datetime']); var b_date = new Date(b['datetime']); if (a['datetime'] === b['datetime']) { var x = a['amount'], y = b['amount']; return x > y ? -1 : x < y ? 1 : 0; } else { return a_date - b_date } }; $scope.transactions.sort(sortFunction); }; $scope.descriptionSuggestions = function() { var suggestions = []; return $.map($scope.transactions, function(transaction) { if ($.inArray(transaction.description, suggestions) === -1){ suggestions.push(transaction.description); return transaction.description; } }); }; $scope.dateSuggestions = function () { var suggestions = []; return $.map($scope.transactions, function(transaction) { if ($.inArray(transaction.datetime, suggestions) === -1){ suggestions.push(transaction.datetime); return transaction.datetime; } }); } $scope.getRunningTotal = function(index) { var runningTotal = 0; var selectedTransactions = $scope.transactions.slice(0, index+1); angular.forEach(selectedTransactions, function(transaction, index){ runningTotal += transaction.amount; }); return runningTotal; }; init(); $(function(){ (function($){ var header = $('#budget-header'); var budget = $('#budget'); var pos = header.offset(); $(window).scroll(function(){ if ($(this).scrollTop() > pos.top && header.css('position') == 'static') { header.css({ position: 'fixed', width: header.width(), top: 0 }).addClass('pinned'); budget.css({ 'margin-top': '+='+header.height() }); } else if ($(this).scrollTop() < pos.top && header.css('position') == 'fixed') { header.css({ position: 'static' }).removeClass('pinned'); budget.css({ 'margin-top': '-='+header.height() }); } }); })(jQuery); }); }); 

API.py

 from flask import Flask, Response, Blueprint, request from pymongo import MongoClient from bson.json_util import dumps from decorators import crossdomain from bson.objectid import ObjectId try: import json except ImportError: import simplejson as json class APIEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, objectid.ObjectID): return str(obj) app = Flask(__name__) client = MongoClient() db = client['moneybooks'] api = Blueprint('api', __name__, url_prefix="/api") @api.route('/budgets', methods=['GET', 'POST', 'OPTIONS']) @crossdomain(origin='*', methods=['GET', 'POST', 'OPTIONS'], headers=['X-Requested-With', 'Content-Type', 'Origin']) def budgets(): if request.method == "POST": budget_id = db.budgets.insert({ 'name': request.form['name'] }) budget_json = dumps(db.budgets.find_one({'_id': budget_id}), cls=APIEncoder) if request.method == "GET": budget_json = dumps(db.budgets.find(), cls=APIEncoder) return Response(budget_json, mimetype='application/json') @api.route('/budgets/<budget_id>', methods=['GET', 'OPTIONS']) @crossdomain(origin='*', methods=['GET', 'OPTIONS'], headers=['X-Requested-With', 'Content-Type', 'Origin']) def budget(budget_id): budget_json = dumps(db.budgets.find_one({'_id': ObjectId(budget_id)}), cls=APIEncoder) return Response(budget_json, mimetype='application/json') @api.route('/budgets/<budget_id>/transactions', methods=['GET', 'POST', 'OPTIONS']) @crossdomain(origin='*', methods=['GET', 'POST', 'OPTIONS'], headers=['X-Requested-With', 'Content-Type', 'Origin']) def transactions(budget_id): if request.method == "POST": db.budgets.update({ '_id': ObjectId(budget_id) }, { '$set': { 'transactions': request.form['transactions'] } }); budget_json = dumps(db.budgets.find_one({'_id': ObjectId(budget_id)}), cls=APIEncoder) if request.method == "GET": budget_json = dumps(db.budgets.find_one({'_id': ObjectId(budget_id)}).transactions, cls=APIEncoder) return Response(budget_json, mimetype='application/json') app.register_blueprint(api) if __name__ == '__main__': app.config['debug'] = True app.config['PROPAGATE_EXCEPTIONS'] = True app.run() 

decorators.py

 from datetime import timedelta from flask import make_response, request, current_app from functools import update_wrapper def crossdomain(origin=None, methods=None, headers=None, max_age=21600, attach_to_all=True, automatic_options=True): if methods is not None: methods = ', '.join(sorted(x.upper() for x in methods)) if headers is not None and not isinstance(headers, basestring): headers = ', '.join(x.upper() for x in headers) if isinstance(max_age, timedelta): max_age = max_age.total_seconds() def get_methods(): if methods is not None: return methods options_resp = current_app.make_default_options_response() return options_resp.headers['allow'] def decorator(f): def wrapped_function(*args, **kwargs): if automatic_options and request.method == 'OPTIONS': resp = current_app.make_default_options_response() else: resp = make_response(f(*args, **kwargs)) if not attach_to_all and request.method != 'OPTIONS': return resp h = resp.headers h['Access-Control-Allow-Origin'] = origin h['Access-Control-Allow-Methods'] = get_methods() h['Access-Control-Max-Age'] = str(max_age) if headers is not None: h['Access-Control-Allow-Headers'] = headers return resp f.provide_automatic_options = False f.required_methods = ['OPTIONS'] return update_wrapper(wrapped_function, f) return decorator 

Edit

Exit the Chrome Chrome console.

Console:

XMLHttpRequest cannot load http://moneybooks.dev:9001/api/budgets/5223e780f58e4d20509b4b8b/transactions. Origin http://moneybooks.dev:9000 is not allowed by Access-Control-Allow-Origin.

Net

 Name: transactions /api/budgets/5223e780f58e4d20509b4b8b Method: POST Status: (canceled) Type: Pending Initiator: angular.js:9499 Size: 13 B / 0 B Latency: 21 ms 
+7
javascript python angularjs flask mongodb
source share
3 answers

As @TheSharpieOne noted, a CORS error is probably a red nibble caused by a Chrome Dev Tools error. If this was a real CORS problem, calling OPTIONS before the flight should have returned the same error.

I suppose your 400 error may come from request.form['transactions'] in the POST request handler. request.form is a MultiDict data structure and according to the documentation at http://werkzeug.pocoo.org/docs/datastructures/#werkzeug.datastructures.MultiDict :

Starting with Werkzeug 0.3, the KeyError raised by this class is also a subclass of the HTTP BadRequest exception and will display a page for 400 BAD REQUEST if they fall into the exception for all HTTP exceptions.

I believe that if you check the "transaction" key in request.forms.keys() , you will find that it does not exist. Note that the content type for POST application/json not x-www-form-urlencoded . According to the documentation at http://flask.pocoo.org/docs/api/#flask.Request.get_json you need to get the request data using the request.get_json() function when the request type is mimetype application/json .

+5
source share

Is a posting message post? I had a similar problem when the body was zero. If so, either adding an empty body ("") when the object is false, or adding a ContentLength header as 0, both seemed to work.

 $scope.syncUp = function () { var objToSend = $scope.transactions ? { transactions: $scope.transactions } : ""; $http.post('http://moneybooks.dev:9001/api/budgets/'+$routeParams['budgetID']+'/transactions', objToSend); }; 
0
source share

Make sure app.js is included in the .js budget file on the HTML page

-2
source share

All Articles