Flask error after redirect from POST method - ajax

I am using a combination of Flask and Javascript. After user input from a web page I send a JSON object back to the Flask server. ie:
var xhr = new XMLHttpRequest();
xhr.open('POST', '/completed/');
xhr.setRequestHeader('Content-Type', 'application/json');
var stringifiedObject = dataResultToJSON(data);
xhr.send(stringifiedObject);
Then in Flask:
#main_view.route('/completed/', methods=['POST'])
def completed():
if (request.headers['Content-Type'].startswith('application/json')):
#do stuff here
return redirect(url_for("main_view.home"))
#main_view.route('/')
def home():
logger.debug(">>home")
return render_template('home.html')
When flask redirects to 'home' asfter the Ajax POST I get the following console output:
DEBUG:myapp:>>home
INFO:werkzeug:127.0.0.1 - - [24/Apr/2016 20:13:15] "GET / HTTP/1.1" 200 -
INFO:werkzeug:127.0.0.1 - - [24/Apr/2016 20:13:15] "GET /%3C!DOCTYPE%20html%3E%3C!-- (... entire web page html)
The odd thing is the second INFO statement above - I don't get this line printed when I redirect to home from anywhere else - only occurs when I redirect from the 'completed' POST method. Werkzeug logs the entire home.html web page html and I get an error in the web client:
NetworkError: 404 NOT FOUND - http://127.0.0.1:5000/%3C!DOCTYPE%20html%3E%3C!-- (... entire web page html)
I also added code=307 to the redirect as per here: Make a POST request while redirecting in flask but still got the same 404 error.
I am stuck as to how to get around this.

I think your problem is that you're POSTing data as an AJAX request (i.e. not a browser navigation, but programatically from your client). It doesn't really make much sense to tell your AJAX client to redirect after the POST completes.
You're then trying to tell the client to redirect...but the redirect request is being returned to the XMLHttpRequest.
I'm not 100% sure what you want to happen, but you'd probably be better off using a regular form post if you want the client to redirect once you've posted the data.
I believe what you're trying to do is better illustrated by the answer to this question:
How to manage a redirect request after a jQuery Ajax call

I got this working following the comment and answer above. Specifically I did:
def completed():
#other code here
return url_for("main_view.home")
and in JS:
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
var OK = 200;
if (xhr.status === OK) {
window.location.href = xhr.responseText;
}
else {
console.log ('Error: ' + xhr.status);
}
}
};

Related

Chrome extension - redirecting an https AJAX request to the same url in onBeforeRequest

I am redirecting every request to the same url in onBeforeRequest listener using redirectUrl directive. It works fine with all sites as Chrome eventually send the request to the url. But I am loosing an AJAX request for one site (the request is not sent at all, e.g. https://apiv2.abc.com/me?token=fiYoEdDLZxPJ...). If I replace return {redirectUrl: request.url}; with just return, everything is fine. Can I redirect every request to itself as the following? I tried this with and without requestHeaders permission. Your suggestions are needed please.
chrome.webRequest.onBeforeRequest.addListener(
function interceptRequest(request) {
if (request.tabId === -1) return;
console.log("In before request: " + request.requestId + ", URL: " + request.url);
return {redirectUrl: request.url};
}, {urls: ['*://*/*']}, ['blocking']);

Django Angular Authentication CSRF cached template

I am getting status code 403 when I try to log in after successfully being logged in and logged out.
Client side is written in Angular and server side is in Django.
This goes as follows:
Client requests url '/' fetches main HTML template with all required static files ( angular, bootstrap, jQuery sources and angular sources defined by me) with
<div ng-view></div> tag into which further templates will be inserted.
Via $location service is redirected to url '/#/login'
This rule from $routeProvider is executed once '/#/login' is hit:
$routeProvider.when('/login', {
templateUrl: 'login.html'
});
'login.html' is served by django view and form for logging in is rendered to the user
User logs in successfully providing proper credentials
Then user logs out, by clicking on a button, which fires '$http.get(
'/logout/'
);' and then is redirected to url '/#/login'
Here is the problem. When user fills in credential form and sends 'POST' request, 403 is returned. I thought that it is, because this routing is done only by angular and since 'login.html' template has already been requested it has been catched and can be served without hitting backend, but after logging out currently possesed CSRF cookie is stale, so that's why I am getting 403. So I tried to remove that template:
logout: function(){
var forceLoginTemplateRequest = function(){
if( $templateCache.get('login.html') !== 'undefined'){
$templateCache.remove('login.html');
}
};
var responsePromise = $http.get(
urls.logout
);
responsePromise.success(forceLoginTemplateRequest);
return responsePromise;
}
After doing that I could see client side requesting 'login.html' template always after logging out, so I thought I could provide CSRF cookie when serving that template from backend:
#urls.py
urlpatterns = patterns(
'',
...
url(r'^$', serve_home_template),
url(r'^login.html$', serve_login_template),
url(r'^login/', login_view, name='login'),
url(r'^logout/', logout_view, name='logout'),
...
)
#views.py
#ensure_csrf_cookie
def serve_login_template(request):
return render(request, "login.html")
#ensure_csrf_cookie
def serve_home_template(request):
return render(request, 'home.html')
But still it doesn't work and I am getting 403 when trying to log in after logging out. The only way I managed it to work is to simply refresh the page so that every single file, whether template or source file is requested again from the backend and CSRF cookie is updated with them.
Here is my app's run section for making sure CSRF cookie is sent with every request:
mainModule.run(['$http','$cookies', '$location', '$rootScope', 'AuthService', '$templateCache',
function($http, $cookies, $location, $rootScope, AuthService, $templateCache) {
$http.defaults.headers.common['X-CSRFToken'] = $cookies.csrftoken;
$rootScope.$on( "$routeChangeStart", function(event, next, current) {
if ( !(AuthService.isLoggedIn() == "true")){
$location.path('/login');
}
});
}]);
This could be a cache problem. Try to add the never_cache decorator to all your views:
from django.views.decorators.cache import never_cache
...
#ensure_csrf_cookie
#never_cache
def serve_login_template(request):
return render(request, "login.html")
...
I solved this problem by setting X-CSRFTOKEN header in $routeChangeStart event.
I don't exactly know how module.run phase works, but it seems that when certain event defined within it occurs everything what is defined outside this event's handler body isn't executed.
mainModule.run(['$http','$cookies', '$location', '$rootScope', 'AuthService',
function($http, $cookies, $location, $rootScope, AuthService) {
$http.defaults.headers.common['X-CSRFToken'] = $cookies.csrftoken;
$rootScope.$on( "$routeChangeStart", function(event, next, current) {
// Added this line
$http.defaults.headers.common['X-CSRFToken'] = $cookies.csrftoken;
if ( !(AuthService.isLoggedIn() == "true")){
$location.path('/login');
}
});
}]);
This works together with removing 'login.html' template from $templateCache.
Instead of removing templates on client side with $templateCache service it is also possible to set your server to serve templates and set following headers:
Cache-Control: no-cache, no-store, must-revalidate
Pragma : no-cache
Expires : 0
Another way of dealing with this problem is to simply force page refresh, however I don't like this approach, since this is not pro-single-page-app approach. :)
One solution could be to read the current, fresh csrftoken directly from the cookie and then update the stale cookie using javascript.
var fresh_token = document.cookie.match('csrftoken=([a-zA-Z0-9]{32})

How to debug the ajax request in django

I know that for example:
def home(request):
if request.method == 'POST':
k = 'p' % 1
return HttpResponse(simplejson.dumps(dict()), mimetype='application/javascript')
else:
k = 'p' % 1
return render_to_response('index.html',locals());
url(r'^$', 'app.home'),
If I use the browser to visit the home page, django will return a debug page to me and show that there is an error in k = 'p' % 1
But if I use the $.ajax() to send a post to this view, the console of chrome only show POST http://(some url here):8000/ 500 (INTERNAL SERVER ERROR)
so is there any good way to debug the second case?
I have no idea about debug the django, is there anybody have better way to debug the django?
thanks
have a look at sentry (and the corresponding raven)
(the Network tab should be able to show you the request and the corresponding response. i believe newer django versions even give you a more bare-bones version of the stacktrace if the request was ajax)
There is an error CallBack in ajax. It will spew out the actual error.
$.ajax({
type: 'POST',
url: '{% url 'url_name_for_your_view_here' %}',
data: {'csrfmiddlewaretoken': '{{csrf_token}}'},
dataType: "text",
success: function(response) {
//do something here if everything goes well
},
error: function(rs, e) {
alert(rs.responseText); //throw actual error, just for debugging purpose
alert('Oops! something went worng..'); // alert user that something goes wrong
}
});
There are a number of third party apps make debugging ajax easier. I've used this in the past with success: https://github.com/yaniv-aknin/django-ajaxerrors
Or if you prefer not use an app, chrome developer tools will likely be enough, as is suggested in this thread: Django: Are there any tools/tricks to use on debugging AJAX response?

ajax json request , always returning error

Hello i've got a problem with ajax json request. Im always getting an error, even if the requests are succeeded. At the moment i have this code:
function sumbitLoginForm(user, pass) {
if (user.trim() == '' || pass.trim() == '') {
alert("You must enter username and password!");
} else {
$.ajax({
type : 'POST',
url : 'https://url.php',
dataType : 'json',
data : {
userlogin : user,
userpass : pass
},
contentType: "application/json;",
success : function(data) {
$("#images").html("uspeshno");
},
error : function(data) {
$("#images").html("greshka");
}
});
}
return false;
}
$(document).ready(function() {
clearPageInputs();
$("#submitButton").click(function() {
sumbitLoginForm($("#username").val(), $("#password").val());
});
});
Im always getting an error , no matter what username and password i type . But the status of request is changing , if i type correct user and pass i get status 302 Moved temporarly , but when i type wrong user or pass i get status 200 OK . What am i doing wrong ?
PRG Pattern and Ajax
It looks like your server returns a HTTP 200 status code when the userid and password will not validate. This is proper behavior, as HTTP error codes not meant for application errors, but for HTTP protocol errors.
When the userid and password are matched succesfully, you are redirected to another page. This is also normal behavior, e.g. to prevent other people to re-use your login credentials using the back key.
This is called the Post/Redirect/Get pattern.
See: http://en.wikipedia.org/wiki/Post/Redirect/Get
The problem is that the PRG pattern does not play nice with Ajax applications. The redirect should be handled by the browser. It is therefore transparent for the jQuery code. The Ajax html response will be the page that is mentioned in the Location header of the 302. Your Ajax application will not be able to see that it is being redirected. So your are stuck.
In one of my projects I solved this on the server side. If I detected an Ajax call, I would not send a redirect but a normal 200 response. This only works if you have access to the server code.
If you cannot change the redirect for your Ajax calls, then you can parse the response headers or the html to see if you were being redirected and act accordingly. Probably the login will set a cookie, so you might try and look for the presence of that cookie.

Handling the Request redirects in ASP.Net MVC 2 with ajax requests

I have developed an ASP.NET MVC2 application and in that i have a login feature implemented.
I have other views that have ajax calls. At times, i find the ajax calls displaying the login page.
In an attempt to get rid of this problem, i have tried to simulate the situation, but the response code that i get from my application was 200 which is OK
I am using ASP.Net MVC2. Why am i getting 200, where i should be actually getting a 302 server redirect for login page redirection from my current view.
Guide me to handle this situation.
Why am i getting 200, where i should be actually getting a 302 server
redirect for login page redirection from my current view.
It's because jQuery follows the redirect and you end up with 200 and the login page. I would recommend you the following article which illustrates a very elegant way to configure ASP.NET to send 401 status code for unauthenticated requests assuming this request was made using an AJAX call. Then your client code will look like this:
$.ajax({
url: '/foo',
type: 'POST',
data: { foo: 'bar' },
statusCode: {
200: function (data) {
alert('200: Authenticated');
// Do whatever you was intending to do
// in case of success
},
401: function (data) {
alert('401: Unauthenticated');
// Handle the 401 error here. You can redirect to
// the login page using window.location.href
}
}
});

Resources