Receiving JSON from AngularJS in Flask - python

I have an AngularJS app, which is sending HTTP PUT data to flask; I can see that the data arrives at the server correctly... however, for some reason, my flask method can't read it when Angular makes an HTTP PUT to the server...
Flask code:
#app.route('/api/thing/hostform', methods=['GET'])
#login_required
def get_blank_host_row():
retval = [host_row(host_name="RANDOM_HOST")]
return Response(dumps(retval), mimetype='application/json')
#app.route('/api/thing/hostform', methods=['PUT'])
#login_required
def append_blank_host_row():
retval = request.form.get('hosts', "!! ERROR !!")
print "PUT RESULT", retval
retval.append(host_row())
return Response(dumps(retval), mimetype='application/json')
"formsubmit_add2" correctly GETs from /api/thing/hostform; however, for some reason request.form.get('hosts', "!! ERROR !!") always errors out with an HTTP 500 error as you can see below...
10.93.10.120 - - [11/Apr/2014 13:15:22] "GET /formsubmit_add2 HTTP/1.1" 200 -
10.93.10.120 - - [11/Apr/2014 13:15:22] "GET /api/thing/hostform HTTP/1.1" 200 -
PUT RESULT !! ERROR !!
10.93.10.120 - - [11/Apr/2014 13:15:25] "PUT /api/thing/hostform HTTP/1.1" 500 -
For anyone who is curious, request.json is None when I get the HTTP PUT...
Question
How can I correctly receive what AngularJS is sending to flask?
Wireshark dump of the HTTP PUT:
This is a wireshark dump of the HTTP PUT from AngularJS...
Hypertext Transfer Protocol
PUT /api/thing/hostform HTTP/1.1\r\n
[Expert Info (Chat/Sequence): PUT /api/thing/hostform HTTP/1.1\r\n]
[Message: PUT /api/thing/hostform HTTP/1.1\r\n]
[Severity level: Chat]
[Group: Sequence]
Request Method: PUT
Request URI: /api/thing/hostform
Request Version: HTTP/1.1
Host: tsunami:5000\r\n
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:28.0) Gecko/20100101 Firefox/28.0\r\n
Accept: application/json, text/plain, */*\r\n
Accept-Language: en-US,en;q=0.5\r\n
Accept-Encoding: gzip, deflate\r\n
Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n
Referer: http://server_name:5000/formsubmit_add2\r\n
Content-Length: 164\r\n
[Content length: 164]
Connection: keep-alive\r\n
\r\n
[Full request URI: http://server_name:5000/api/thing/hostform]
Line-based text data: application/x-www-form-urlencoded
{"hosts":[{"real_ip_addr":"","switch_port":"","host_nic_role":"Console",
"host_name":"RANDOM_HOST","host_nic_name":"","switch_name":"",
"altn_ip_addr":"192.0.2.42"}]}
AngularJS $http PUT:
$scope.add_row = function (data) {
// build an ajax request with $scope.data
var send = $http({
method : "PUT",
url : "/api/thing/hostform",
headers : {"Content-Type":
"application/x-www-form-urlencoded; charset=UTF-8"},
data: {"hosts": data},
});
send.success(
// something irrelevant here
);

You are using the wrong mimetype for JSON. Rather than application/x-www-form-urlencoded, it should be application/json.

From the Flask Request class documentation:
json
If the mimetype is application/json this will contain the parsed JSON data. Otherwise this will be None.
The get_json() method should be used instead.
I think your problem appears because request.form doesn't contain hosts key (contains something else), because your mimetype is not json (application/json).
For a more precise answer please write what comes in request.form?

Related

CSRF validation works from Postman but not React/Axios

It's honestly a catastrophic blow to the ego I haven't already figured this out--spent 6 hours so far...
I have a React app running off a Django Rest Framework backend. For the password reset functionality I am using Django's builtin view (auth_views.PasswordResetView). I have a form in React that accepts an email and sends a post request to reset the users password. The way I send the data in postman is:
Url: http://192.168.0.85:8000/reset_password
Body: {'email': 'blabla#bla.com'}
Headers: {'X-CSRFToken': OGH9iUEtGPqntMYifQ5kiin2ufV9tK39tbp9Wmh6tLST0DXCXSkY8mOvyq5AjjnZ}
...and it works like a charm! Until I try to replicate the same exact call using axios in React:
axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.xsrfHeaderName = 'X-CSRFToken';
axios.post('http://192.168.0.85:8000/reset_password',{'email': 'blabla#bla.com'},{headers}))
...this results in a 403 error with this message from the backend:
WARNING:django.security.csrf:Forbidden (CSRF cookie not set.): /reset_password
I've spent 6 hours googling and experimenting and can't figure out for the life of me why the Axios request would be any different than the Postman one. This problem is particularly infuriating because #csrf_exempt won't work on the django builtin views.
Here are the headers from a successful request in Postman:
X-CSRFToken: OGH9iUEtGPqntMYifQ5kiin2ufV9tK39tbp9Wmh6tLST0DXCXSkY8mOvyq5AjjnZ
User-Agent: PostmanRuntime/7.28.0
Accept: */*
Postman-Token: 91cbbfe7-b434-45fc-97ec-7d442c091070
Host: 192.168.0.85:8000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 33
Cookie: csrftoken=gawXPEZQfN4qC4aKPhiIvK5qpjvkp4WCVFeXt6Ct2JwW9V94xjxmlOwTtuFLfDgs
...and from the failed request using Axios from React:
baseURL: "http://192.168.0.85:8000"
data: "{\"email\":\"blabla#bla.com\"}"
headers:
Accept: "*/*"
Authorization: ""
Content-Type: "application/json"
X-CSRFToken: "udd4yAGCJuTxw95kCdHYwwEQmGwu1bwxqEQjZhI56v1qWY143S4IPCrUSvk9xkV8"
__proto__: Object
maxBodyLength: -1
maxContentLength: -1
method: "post"
timeout: 0
transformRequest: [ƒ]
transformResponse: [ƒ]
url: "/reset_password"
validateStatus: ƒ validateStatus(status)
xsrfCookieName: "csrftoken"
xsrfHeaderName: "X-CSRFToken"
__proto__: Object
isAxiosError: true
Does this answer your question, https://stackoverflow.com/a/66550363/7838574 ?
With emphasis on the settings.py portion. That is what tripped me up about a week ago.
Honestly great job keeping at this and asking for help! Programming can be insanely frustrating and once the hurdle is behind you very rewarding!

Two part post request to get CSRF Token then make another request

I'm attempting to access data by first logging in to the following site:
https://accessns.nscorp.com/accessNS/login/
It looks like it sends this requests to the backend:
Request URL: https://accessns.nscorp.com/accessNS/rest/auth/login
Request Method: POST
Status Code: 200 OK
Remote Address:167.121.11.85:443
Referrer Policy: no-referrer-when-downgrade
When I send that request in python, I'm able to get a response back with a CSRF Token.
Next, I would like to access:
Request URL:
https://accessns.nscorp.com/accessNS/rest/backend/ServicesIndustrial/services/industrial2/v2/onsite/details
Request Method: POST
Status Code: 200 OK Remote Address:
167.121.11.85:443
Referrer Policy: no-referrer-when-downgrade
Which takes the following headers:
Accept: /
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 58
Content-Type: application/json
Cookie: [some cookies]
CSRFTOKEN: [from auth request above]
Host: accessns.nscorp.com
Origin: https://accessns.nscorp.com
Referer: https://accessns.nscorp.com/accessNS/legacy/
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36
Payload:
{userId: "username", classCode: "classcode", stationCode: "stationcode"}
My code fails when I attempt to use CSRF Token and cookies from the authorization request into the secondary request, but it doesn't fail when I manually login to the site, get the token/cookies, and hard code them into python. In fact, all I need in terms of headers for the secodary request is CSRF Token and cookies to get a viable response.
with requests.Session() as s:
login = {'Id': 'username', 'pwd': 'password'}
auth = s.post('https://accessns.nscorp.com/accessNS/rest/auth/login',
json=login)
headers = {}
headers['CSRFTOKEN'] = auth.json()['response']['token']
headers['Cookie'] = '; '.join('='.join((i.name, i.value)) for i in
auth.cookies)
payload = {'userId': 'username', 'classCode': 'classcode',
'stationCode': 'stationcode'}
url =
'https://accessns.nscorp.com/accessNS/rest/backend/ServicesIndustrial/
services/industrial2/v2/onsite/details'
inv= s.post(url, json=payload, headers=headers)
print(inv.json())
I expect to get a json response with the data I've requests, however, using my code, I get:
{'time': 1559217902355, 'message': 'Object reference not set to an instance of an object.. Reference number: 1559217902355', 'cause': 'Invalid user input', 'isError': True}, which to me seems like it's an issue with my headers/payload, or I'm missing an intermediate step.
When I hardcode the csrf token/cookies and don't use a requests session, I get the response I want.
Figured it out, my code works fine. Turns out the secondary request was being made to the wrong URL, which is strange because that's what's listed in every developer console I've checked.

How to send/receive URL parameters in a HTTP POST correctly?

I am using cakephp 2.4.5. I want to send a HTTP POST with URL parameters. I am using python 2.7 request module to send the HTTP POST. Please assume the payload is structured correctly as I have tested that part.
URL_post = http://127.0.0.1/webroot/TestFunc?identity_number=S111A/post
r = requests.post(URL_post, payload)
On the cakephp side, the controller looks something like this;
public function TestFunc($id=null)
{
$identity_number = $this->request->query['identity_number'];
$this->request->data['Model']['associated_id']=$identity_number;
$this->Model->saveAll($this->request->data, array('deep' => true));
}
I have tested that the query is not received correctly. However, if I am not using HTTP POST and just throwing in a normal URL, the query can be received correctly.
What have I done wrong?
The query part of the url is sent correctly:
import requests
requests.post('http://localhost/webroot/TestFunc?identity_number=S111A/post',
{'Model': 'data'})
The Request
POST /webroot/TestFunc?identity_number=S111A/post HTTP/1.1
Host: localhost
User-Agent: python-requests/2.2.1 CPython/3.4 Linux/3.2
Accept: */*
Accept-Encoding: gzip, deflate, compress
Content-Type: application/x-www-form-urlencoded
Content-Length: 10
Model=data
You could also make the requests using params:
requests.post('http://localhost/webroot/TestFunc',
data={'Model': 'data'},
params={'identity_number': 'S111A/post'})
The only difference is that S111A/post is sent as S111A%2Fpost (the same url in the end).
Look at http://docs.python-requests.org/en/latest/user/quickstart/#passing-parameters-in-urls.
payload = {"identity_number": "S111A/post"}
URL_post = "http://127.0.0.1/webroot/TestFunc"
req = requests.post(URL_post, params=payload)
print(req.status_code)

Get full raw http request (complete with headers and body)

In a appengine project im trying to get the whole http request inside an webapp2.RequestHandler:
class ConnectedHandler(webapp2.RequestHandler):
def post(self):
logging.info("Someone connected: " + self.request.get('from'))
# How to get the raw http request from self.request?
Having looked through the documentation im begining to think its not possible
The result im looking for is something like this (What i would call a http request anyways):
POST /6473924464345088 HTTP/1.1
Accept: application/json
Accept-Encoding: gzip, deflate, compress
Content-Type: application/json; charset=utf-8
Host: localhost:10083
User-Agent: HTTPie/0.3.0
{
"u": "a"
}
Edit: Updated the example
Is there another cleaver way to access this data when using webapp2 ?
This should get you exactly that:
class MainPage(webapp2.RequestHandler):
def post(self):
self.response.write('Just received:\n\n' + str(self.request))

403 FORBIDDEN in django agon-rating

I have 403 FORBIDDEN error in agon-ratings plugin when submit rating.
I have read the doc. But csrf token exists in header:
Request Headersview source
Accept */*
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Cache-Control no-cache
Connection keep-alive
Content-Length 22
Content-Type application/x-www-form-urlencoded; charset=UTF-8
Cookie csrftoken=6C7zHmrBufWbiYeTXwRkCWC9hDfdxGoW; sessionid=4d6b6977721fcb97f6903d0aaab5e632
Host localhost:8000
Pragma no-cache
Referer http://localhost:8000/news/40/asdas/
User-Agent Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:23.0) Gecko/20100101 Firefox/23.0
X-Requested-With XMLHttpRequest
Any help with this issue will be appreciated.
Thanks in advance
I guess you are doing a post via ajax request, if thats correct than you need to send the csrf token as part of POST data or via the X-CSRFToken header.
I dont see any of that in the request header you posted.
Django docs you linked have a working example about how to do this (and if you use jQuery its mostly a copy and paste job)
You are using POST method. And with POST method, you need to write {% csrf_token %} in HTML inside form element or pass csrftoken in ajax request i.e https://docs.djangoproject.com/en/dev/ref/contrib/csrf/.
It is compulsory when you write CSRFMiddleware and csrf context processor in your django settings.

Categories

Resources