POST 500 (Internal Server Error) when I use the JEditable plugin - javascript

I'm trying to use JEditable plugin in my Symfony2 application. PYS entity is a a films and TV shows entity; then I've got Usuario and Critica entities. I want to manage the user's critics with the plugin. I've analyzed more and more examples, but I can not get it to work. The value (in this case the title of critic) is update in the template but not in the db; when I refresh the browser the old value appears.
THE ERROR:
This is my JS:
$('.edit').editable(function(value, settings) {
var data = {};
data[this.id] = value;
console.log(path);
console.log(data);
$.post(path, data);
return(value);
}, {
indicator : 'Saving...',
tooltip : 'Click to edit...'
});
This is the route:
critica_ajax:
locales: { es: "/gestion-critica/{pysStr}/", en: "/manage-critic/{pysStr}/" }
defaults: { _controller: UsuarioBundle:Default:gestionarCritica }
This is the controller:
public function gestionarCriticaAction($pysStr)
{
$em = $this->getDoctrine()->getManager();
$pys = $em->getRepository('PYSBundle:Pys')->findPys($pysStr);
$usuario = $this->get('security.context')->getToken()->getUser();
$critica = $em->getRepository('UsuarioBundle:Usuario')->findCritica($usuario, $pys);
if(!$critica)
{
$critica = new Critica($usuario, $pys);
}
$criTitulo = $this->request->get('value');
$critica->setCriTitulo($criTitulo);
$critica->setCriContenido($criContenido);
$critica->setCriFecha(new \DateTime("now"));
$em->persist($critica);
$em->flush();
return new Response($criTitulo);
}
The Twig template:
<h2 class="edit">{{ critica.criTitulo }}</h2>
<script>
var path = "{{ path('critica_ajax', { 'pysStr': pelicula.pysStr}) }}";
</script>
EDIT (The Symfony's return)
Notice: Undefined property: Filmboot\UsuarioBundle\Controller\DefaultController::$request
in C:\Programming\xampp\htdocs\filmboot\src\Filmboot\UsuarioBundle\Controller\DefaultController.php line 236
THIS IS THE LINE 236: $criTitulo = $this->request->get('value');
at ErrorHandler ->handle ('8', 'Undefined property: Filmboot\UsuarioBundle\Controller\DefaultController::$request', 'C:\Programming\xampp\htdocs\filmboot\src\Filmboot\UsuarioBundle\Controller\DefaultController.php', '236', array('pysStr' => 'machete', 'em' => object(EntityManager), 'pys' => object(Pys), 'usuario' => object(Usuario), 'critica' => object(Critica)))
in C:\Programming\xampp\htdocs\filmboot\src\Filmboot\UsuarioBundle\Controller\DefaultController.php at line 236 +
at DefaultController ->gestionarCriticaAction ('machete')
at call_user_func_array (array(object(DefaultController), 'gestionarCriticaAction'), array('machete'))
in C:\Programming\xampp\htdocs\filmboot\app\bootstrap.php.cache at line 1003 +
at HttpKernel ->handleRaw (object(Request), '1')
in C:\Programming\xampp\htdocs\filmboot\app\bootstrap.php.cache at line 977 +
at HttpKernel ->handle (object(Request), '1', true)
in C:\Programming\xampp\htdocs\filmboot\app\bootstrap.php.cache at line 1103 +
at ContainerAwareHttpKernel ->handle (object(Request), '1', true)
in C:\Programming\xampp\htdocs\filmboot\app\bootstrap.php.cache at line 413 +
at Kernel ->handle (object(Request))
in C:\Programming\xampp\htdocs\filmboot\web\app_dev.php at line 26 +

You need to get the request like this :
$request = $this->getRequest();
instead of
$request = $this->request;
the request is returned using a method its not a class property

Related

TYPO3 ajax The default controller for extension and plugin can not be determined

I have TYPO3 7.6.18 and I trying ajax on front end and I get error 'The default controller for extension \"fefiles\" and plugin \"piphoto\" can not be determined'.
setup.txt
ajaxCall = PAGE
ajaxCall {
typeNum = 22222
config {
disableAllHeaderCode = 1
xhtml_cleaning = 0
admPanel = 0
additionalHeaders = Content-type: text/plain
no_cache = 1
debug = 0
}
10 = USER
10 {
userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
extensionName = fefiles
pluginName = Piphoto
vendorName = Istar
controller = Photo
action = ajaxHandler
}
}
ext_tables.php
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin(
$_EXTKEY,
'Piphoto',
'Upload Photo'
);
js
jQuery(function($) {
$(".send-photo-comment").click(function (e) {
e.preventDefault();
$.ajax({
type: "POST",
url: "?id=0&type=22222",
data: {},
success: function(msg){
console.log(msg);
}
});
})
})
ext_localconf
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
'Istar.' . $_EXTKEY,
'Piphoto',
[
'Photo' => 'list, ajaxHandler, show, new, create, edit, update, delete',
],
// non-cacheable actions
[
'Photo' => 'list, ajaxHandler, show, new, create, edit, update, delete',
]
);
Help me please)
How does your ext_localconf.php look like?
Should include something like:
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
'Istar.' . $_EXTKEY,
'Piphoto,
array(
'Photo' => 'ajaxhandler'
)
);
This basically means that you need to define a controller in your ext_localconf.php. Usually the first entry is taken as default controller/action. Here's an example:
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
'YourVendor.' . $_EXTKEY,
// Plugin name
'Pi1',
// An array of controller-action combinations. The first one found is the default one.
array(
'YourController' => 'index,new,create,edit,update'
),
// An array of non-cachable controller-action-combinations (they must already be enabled)
array(
'YourController' => 'new,create,edit,update'
)
);

VueJS doesn't work on mobile

I have a problem with running VueJS on mobile devices. I created a weather prediction app on copepen.io
Here is the link for the project:
http://codepen.io/techcater/pen/xOZmgv
HTML code:
<div class="container-fluid text-center">
<h1>Your Local Weather</h1>
<p>
{{location}}
</p>
<p>
{{temperature}}
<a #click="changeDegree">{{degree}}</a>
</p>
<p>
{{weather | capitalize}}
</p>
<img :src="iconURL" alt="" />
<br>
by Dale Nguyen
<!-- <pre>{{$data | json}}</pre> -->
</div>
JS code:
new Vue({
el: '.container-fluid',
data: {
location: "",
temperature: "",
degree: "C",
weather: "",
iconURL: ""
},
created: function(){
this.getWeather();
},
methods: {
getWeather: function(){
var that = this;
this.$http.get("http://ipinfo.io").then((response) => {
console.log(response.data);
that.location = response.data.city + ", " + response.data.country;
// Get weather informaiton
var api = 'ebd4d312f85a230d5dc1db91e20c2ace';
var city = response.data.city;
var url = "http://api.openweathermap.org/data/2.5/weather?q={CITY}&APPID={APIKEY}&units=metric";
url = url.replace("{CITY}",city);
url = url.replace("{APIKEY}", api);
that.$http.post(url,{dataType: 'jsonp'},{
headers : {
'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8'
}}).then((response) => {
console.log(response.data);
that.temperature = response.data.main.temp;
that.weather = response.data.weather[0]['description'];
that.iconURL = "http://openweathermap.org/img/w/" + response.data.weather[0]['icon'] + ".png";
}, (response) => {
// error callback
});
}, (response) => {
console.log(response.data);
});
},
changeDegree: function() {
if(this.degree == "C"){
this.degree = "F";
this.temperature = Math.round((this.temperature*9/5 + 32)*100)/100;
}else {
this.degree = "C";
this.temperature = Math.round(((this.temperature - 32)*5 /9)* 100)/100;
}
}
}
})
It works well on my laptop but not on mobile. At first, I thought that it is because of Codepen. It may cause something when running through the site. However, when I created a project on my website, it also doesn't work.
Can you help to find the issue? Thanks,
Your code seems to be working well, except that on codepen it gives me error XMLHttpRequest cannot load http://ipinfo.io/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://s.codepen.io' is therefore not allowed access..
You can put your domain name on headers options to enable cross-origin, here is example:
this.$http.get('http://ipinfo.io', {
'headers': {
'Origin': 'http://yourdomain.com'
}
})
See example: http://bozue.com/weather.html
I also noticed you put vue.min.js and vue-resource.js scripts in wrong order that might trigger some error, vue.min.js should be on the first place.
I found a solution for this. I works on my mobile now. I believe that I will work on other browses too. The problem is that some browsers doesn't recognize the operation ">", so I changed it.
Here is the new code:
getWeather: function(){
var that = this;
this.$http.get('http://ipinfo.io', {'headers': {
'Origin': 'http://yourdomain.com'}
}).then(function(response) {
console.log(response.data);
that.location = response.data.city + ", " + response.data.country;
// Get weather informaiton
var api = 'ebd4d312f85a230d5dc1db91e20c2ace';
var city = response.data.city;
var url = "https://crossorigin.me/http://api.openweathermap.org/data/2.5/weather?q={CITY}&APPID={APIKEY}&units=metric";
url = url.replace("{CITY}",city);
url = url.replace("{APIKEY}", api);
that.$http.post(url,{dataType: 'jsonp'},{
headers : {
'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8'
}}).then(function(response) {
console.log(response.data);
that.temperature = response.data.main.temp;
that.weather = response.data.weather[0]['description'];
that.iconURL = "http://openweathermap.org/img/w/" + response.data.weather[0]['icon'] + ".png";
}).then(function(){
// error callback
});
}).then(function(){
console.log(response.data);
});
},

Deal with the result of postJSON

I'm trying to implement something with jQuery and Vue.js:
And here is my script part:
<script>
function initVM(result) {
// alert(result.num)
var vm2 = new Vue({
el: '#vm2',
data: {
// ③bind one item of the result as example
rrr: result.num
}
});
$('#vm2').show();
}
$(function() {
var vm = new Vue({
el: '#vm',
data: {
content: ''
},
methods: {
submit: function(event) {
event.preventDefault();
var
$form = $('#vm'),
content = this.content.trim();
// ①post textarea content to backend
$form.postJSON('/api/parse', {
content: content
}, function(err, result) {
if (err) {
$form.showFormError(err);
}
else {
// ②receive a result dictionary
$('#vm').hide();
initVM(result);
}
});
}
}
});
});
</script>
Here is my html part:
<html>
<form id="vm", v-on="submit: submit">
<textarea v-model="content" name="content"></textarea>
<button type="submit">Have a try!</button>
</form>
<div id="vm2" style="diplay:none;">
<!-- ④show the result-->
The result:
{{ rrr }}
</div>
</html>
Here is the definition of postJSON
<script>
// ...
postJSON: function (url, data, callback) {
if (arguments.length===2) {
callback = data;
data = {};
}
return this.each(function () {
var $form = $(this);
$form.showFormError();
$form.showFormLoading(true);
_httpJSON('POST', url, data, function (err, r) {
if (err) {
$form.showFormError(err);
$form.showFormLoading(false);
}
callback && callback(err, r);
});
});
// ...
// _httpJSON
function _httpJSON(method, url, data, callback) {
var opt = {
type: method,
dataType: 'json'
};
if (method==='GET') {
opt.url = url + '?' + data;
}
if (method==='POST') {
opt.url = url;
opt.data = JSON.stringify(data || {});
opt.contentType = 'application/json';
}
$.ajax(opt).done(function (r) {
if (r && r.error) {
return callback(r);
}
return callback(null, r);
}).fail(function (jqXHR, textStatus) {
return callback({'error': 'http_bad_response', 'data': '' + jqXHR.status, 'message': 'something wrong! (HTTP ' + jqXHR.status + ')'});
});
}
</script>
The process can be described as:
Post the content to backend
Receive the result and hide the form
Create a new Vue with the result
Show the result in a div which is binding with the new Vue instance
Actually, I do receive the result successfully, which is proved by the alert(result.num) statement, but nothing find in vm2's div except The result:
Where is the problem? Or please be free to teach me a simpler approach if there is, because I don't think what I am using is a good one.
Here's questioner.
Finally I found where is the problem.
The problem lays in Mustache: {{ }}
I use jinja2, a template engine for Python and Vue.js, a MVVM frontend framework. Both of them use {{ }} as delimiters.
So if anyone got trapped in the same situation with me, which I don't think there will be, please:
app.jinja_env.variable_start_string = '{{ '
app.jinja_env.variable_end_string = ' }}' # change jinjia2 config
OR
Vue.config.delimiters = ['${', '}'] # change vue config

Error: .$save is not a function (AngularJS)

The non-GET instance action $save doesn't work in my example. I always get the Error, that $save is not a function. The problem is, I don't know where I have to define the $scope.example = new Resource();, because in my example I'm using 2 Controllers. One for the table list with objects and the other one for my modal window, where you can take CRUD operations. The CRUD operations are defined in an angular service.
The code is structured as follows:
Servie of Resource:
...
return {
name: $resource(baseUrl + '/api/name/:Id', {
Id: '#Id'
}, {
'update': {
method: 'PUT'
}
}),
...
Service of CRUD:
...
return {
create: function (newName) {
return newName.$save();
},
...
Ctrl of modal window:
$scope.selected = new resService.name();
$scope.createItem = function (newName) {
CrudService.create(newName).then(
function () {
$scope.dataSuccess = 'Person created.';
$scope.newName = null;
},
function (err) {
$scope.dataError = err.data.ModelState;
});
}
}
$scope.form = [{
label: 'Firstname',
fieldType: 'text',
name: 'Fname',
id: 'fname-id',
propertyName: 'fname',
disabled: false,
pattern: /^[a-zA-Z]{4}[a-zA-Z]*/,
required: true,
errRequired: 'Firstname is required.',
errPattern: 'Firstname has at least 4 letters.'
},
...];
The view with form:
<form class="form-horizontal" name="editForm" novalidate>
<div class="form-group-sm has-feedback" ng-repeat="elem in form" ng-class="{ 'has-error' : hasError(editForm, elem.name), 'has-success' : hasSuccess(editForm, elem.name) }">
<label class="control-label" for="{{elem.id}}">{{elem.label}}</label>
<input type="{{elem.fieldType}}"
class="form-control"
placeholder="{{elem.label}}"
name="{{elem.name}}"
id="{{elem.id}}"
ng-model="selected[elem.propertyName]"
ng-disabled="{{elem.disabled}}"
ng-pattern="elem.pattern"
ng-required="{{elem.required}}"
/>
<p class="help-block" ng-if="elem.errRequired" ng-show="editForm[elem.name].$error.required && editForm[elem.name].$touched">{{elem.errRequired}}</p>
<p class="help-block" ng-if="elem.errPattern" ng-show="editForm[elem.name].$error.pattern">{{elem.errPattern}}</p>
EDIT:
I'm getting a new Error. The console tells, that I have to use track by expression. But I was trying to use the form view without generating and then works. But I need the generated form view (the example view above).
Error Message:
Error: ngRepeat:dupes
Duplicate Key in Repeater
Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys.
If you wan't to create a new object you need the choose the service between the Services choice (factory, service, providers).
The difference between a factory and a service, is about syntax. Just syntax.
.factory(function(){
//Private variables and functions
var x = "ez";
function getX(){
return x;
}
//Public functions (or variables)
return {
a : "test",
getA : function(){
return a;
}
}
})
//Service example
.service(function(){
//Handled by Angular:
//new() is used to create a new object
//Private functions and variables
var x = "test";
function getX(){
return x;
}
//Public funcitons (and variables)
this.a = function(){
"test";
};
this.getA = function(){
return a;
};
//Handeled by AngularJS
//return this;
});
Everything that is returned in the factory is available.
The service automaticaly creates a new object when calling it, which makes available the object ("this")
Calling a service or a factory remains the same:
var a = service.getA();
var a = factory.getA();
EDIT
Notice also that you can decide if your promise is going to the next error or success call.
Just as an exmaple:
xhr()
.then(success1, error1)
.then(success2, error2)
.then(success3, error3)
...
success and error are all callback functions.
By using $q you can go to the next success or error, wathever the callback.
QUESTION CODE
. factory ( 'YourFacotry' , [ '$resource' ,
function ( $resource ) {
return $resource ( '/api/note/:id' , { id : '#id' },
{
markAsDone :
{
url : '/api/note/:id/done' ,
method : 'POST' ,
isArray : true
}
});
}]);
Ctrl of modal window:
$scope.createItem = function () { //Forgot $scope here!
CrudService.query().then(
function () {
$scope.dataSuccess = 'Person created';
$scope.newName = null;
},
function (err) {
$scope.dataError = err.data.ModelState;
});
}
}

Issues with CORS. Flask <-> AngularJS

Starting a new project with a angularjs client app and a flask app providing the api. I'm using mongodb as the database. I had to immediately rule out jsonp since I would need the ability to POST across different ports. So we have localhost:9000 for the angular app and localhost:9001 for the flask app.
I went through and made the changed needed for CORS in my API as well as my angular files. See source below. First issue I ran in to was that there is a bug that CORS allow header does not recognize localhost in Chrome. I updated my hosts file so I could use moneybooks.dev and this worked for my GET requests without using JSONP.
Now, to the issues I'm facing. When submitting a POST request, its stating Origin http://moneybooks.dev:9000 is not allowed by Access-Control-Allow-Origin What? GET can go through but POST is declined. I see the request come through to flask but it returns HTTP 400. I need help making POST requests work.
Another issue, which may be related, is that on my GET requests, sometimes the GET request doesn't fire at all. Like in BudgetCtrl the loadBudget function. On #/budgets/budgetID the name of the budget will sometimes not load at all. I check the flask log and don't see a request coming through. Then I click refresh, I see the request, the budget name appears on the page however in the flask log I see an error. [Errno 10053] An established connection was aborted by the software in your host machine. Its a connection error that only appears in the flask log when the GET request succeeds.
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
Output from chrome dev 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.
Network
Name: transactions /api/budgets/5223e780f58e4d20509b4b8b
Method: POST
Status: (canceled)
Type: Pending
Initiator: angular.js:9499
Size: 13 B / 0 B
Latency: 21 ms
As #TheSharpieOne pointed out, the CORS error is likely a red herring caused by a Chrome Dev Tools bug. If it was an actual CORS issue, the pre-flight OPTIONS call should have returned the same error.
I believe your 400 error may be coming from request.form['transactions'] in the handler for the POST request. request.form is a MultiDict datastructure and according to the documentation at http://werkzeug.pocoo.org/docs/datastructures/#werkzeug.datastructures.MultiDict:
From Werkzeug 0.3 onwards, the KeyError raised by this class is also a subclass of the BadRequest HTTP exception and will render a page for a 400 BAD REQUEST if caught in a catch-all for HTTP exceptions.
I believe that if you check for the 'transactions' key in request.forms.keys(), you'll find that it does not exist. Note that the content type for the POST is application/json not x-www-form-urlencoded. According to the documentation at http://flask.pocoo.org/docs/api/#flask.Request.get_json, you'll want to get the request data using the request.get_json() function when the request mimetype is application/json.
Is the POST sending content ? I had a similar issuee when the body was null. If it is, adding either an empty body ("") when the object is falsy, or adding the 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);
};
Ensure app.js is included before budget.js in your HTML page

Categories