Why i am getting 400 Bad Request error when sending json data in Flask? - flask

I am trying to write a small restful api application, i am using Chrome Postman extension for sending requests to the app .
I believe that my code does not have mistakes but every time i am sending post request a 400 Bad Request error raising , here is my code:
#api_route.route('/api', methods=['GET'])
def api():
return jsonify({'message':'Api v1.0'})
#api_route.route('/api', methods=['POST'])
def create_user():
data = request.get_json()
if data:
hashed_password = generate_password_hash(data['password'], method='sha256')
api = Api(email=data['email'], password=hashed_password)
db.session.add(api)
db.session.commit()
return jsonify({'message', 'New User Created!'})
The json data that i am sending looks like this:
{"email" : "Test", "password" : "123123123"}
Why i am getting the 400 error ??
Update:
Screenshots for the requests using Postman:
GET Request
POST Request
Here i am initiating api route inside api controller :
from flask import Blueprint
api_route = Blueprint(
'api',
__name__
)
from . import views
then i am registering it inside def create_app() function :
from .api import api_route
app.register_blueprint(api_route)
Here are the extensions that i am using in my application:
toolbar = DebugToolbarExtension()
assets_env = Environment()
cache = Cache()
moment = Moment()
htmlminify = HTMLMIN()
csrf = CSRFProtect()
jac = JAC()
googlemap = GoogleMaps()
session = Session()
principal = Principal()

I solved the problem, i've initiated CSRFProtect with app so i need to include X-CSRFToken in all my requests, so i have two choices:
1 - To include the csrf_token in request.headers for all the requests
2 - Using #csrf.exempt decorator that coming with flask_wtf.csrf
For now i am using #csrf.exempt, so it become like this:
#api_route.route('/api', methods=['GET','POST'])
#csrf.exempt
def create_user():
if request.method == 'GET':
return jsonify({'message' : 'API v1.0'})
elif request.method == 'POST':
data = request.get_json()
hashed_password = generate_password_hash(data['password'], method='sha256')
new_user_api = Api(email=data['email'], password=hashed_password)
db.session.add(new_user_api)
db.session.commit()
return jsonify({'message' : 'New user created!'})
return return jsonify({'message' : 'No user has been added!'})
Thanks for #MrPyCharm for his interests , salute :) .

A good approach would be to structure your views as follows:
Instead of creating view with same route for different request methods, you can handle the request methods in the same view:
#api_route.route('/api', methods=['GET', 'POST'])
def api():
if request.method == 'GET':
return jsonify({'message':'Api v1.0'})
else:
data = request.get_json(force=True)
if data:
hashed_password = generate_password_hash(data['password'], method='sha256')
api = Api(email=data['email'], password=hashed_password)
db.session.add(api)
db.session.commit()
return jsonify({'message': 'New User Created!'})
# Just in case the if condition didn't satisfy
return None

A note for anyone else experiencing this with PostMan and Flask - you will also hit a HTTP 404 if your URL in PostMan is HTTPS but your Flask app only handles HTTP.

Related

testing stripe on-boarding django with mock

i am having trouble trying to mock test the on-boarding process of stripe connect. I am just learning how to use mock and i am struggling with the StripeAuthorizeCallbackView. the process is as follows: A user reaches the StripeAuthorizeView which sends them to the stripe api to sign up for an account. Once they successfully sign up for an account their redirected back to my platform and stripe sends a temporary code which i then send back to stripe with my api keys. Once i have sent the information back to stripe they then return me credentials for the user being the stripe_user_id.
Here is the two views in question:
import urllib
import requests
class StripeAuthorizeView(LoginRequiredMixin, View):
def get(self, request):
url = 'https://connect.stripe.com/express/oauth/authorize?'
user = self.request.user
if user.account_type == 'Business':
business_type = 'company'
else:
business_type = 'individual'
params = {
'response_type': 'code',
'scope': 'read_write',
'client_id': settings.STRIPE_CONNECT_CLIENT_ID,
'redirect_uri': f'http://127.0.0.1:8000/accounts/stripe/oauth/callback',
'stripe_user[email]' : user.email,
'stripe_user[business_type]' : business_type,
'stripe_user[url]' : 'http://127.0.0.1:8000/accounts/user/%s/' %user.pk,
}
url = f'{url}?{urllib.parse.urlencode(params)}'
return redirect(url)
lass StripeAuthorizeCallbackView(LoginRequiredMixin, View):
def get(self, request):
code = request.GET.get('code')
if code:
data = {
'client_secret': settings.STRIPE_SECRET_KEY,
'grant_type': 'authorization_code',
'client_id': settings.STRIPE_CONNECT_CLIENT_ID,
'code': code
}
url = 'https://connect.stripe.com/oauth/token'
resp = requests.post(url, params=data)
stripe_user_id = resp.json()['stripe_user_id']
stripe_access_token = resp.json()['access_token']
stripe_refresh_token = resp.json()['refresh_token']
user = self.request.user
user.stripe_access_token = stripe_access_token
user.stripe_user_id = stripe_user_id
user.stripe_refresh_token = stripe_refresh_token
user.save()
notify.send(sender=user, recipient=user,
verb='You have succesfully linked a stripe account. You can now take payments for sales.',
level='info')
redirect_url = reverse('account', kwargs={'pk': user.pk})
response = redirect(redirect_url)
return response
else:
user = self.request.user
notify.send(sender=user, recipient=user,
verb='Your attempt to link a stripe account failed. Please contact customer support.',
level='warning')
url = reverse('account', kwargs={'pk': user.pk})
response = redirect(url)
return response
I am not very worried about testing the StripeAuthorizeView a lot. I am more trying to figure out how to test the StripeAuthorizeCallbackView. All i can figure out is that i will need to mock both the code returned and then mock the following requests.post. This test is important to confirm my platform is linking the users credentials after the on-boarding process. Any help on this will be greatly appricated.
edit:
So far i have the following :
#classmethod
def setUpTestData(cls):
cls.test_user = User.objects.create_user(
password='test',
full_name='test name',
email='test#test.com',
address='1 test st',
suburb='test',
state='NSW',
post_code='2000',
contact_number='0433335333' )
#patch('requests.get')
def test_authorizecallback_creates_stripe_details(self, get_mock):
code = requests.get('code')
user = self.test_user
self.client.login(email='test#test.com', password='test')
mocked = ({'stripe_user_id' : '4444','stripe_access_token' : '2222',
'stripe_refresh_token' : '1111' })
with mock.patch('requests.post', mock.Mock(return_value=mocked)):
response = self.client.get('/accounts/stripe/oauth/callback/',
{'code' : '1234'})
self.assertEqual(user.stripe_access_token, '222')
message = list(response.context.get('messages'))[0]
however i keep getting:
File "C:\Users\typef\Desktop\Projects\python_env\fox-listed\Fox-Listed\fox-listed\user\views.py", line 142, in get
stripe_user_id = resp.json()['stripe_user_id']
AttributeError: 'dict' object has no attribute 'json'
the actual response that the StripeAuthorizeCallBackView gives is:
{'access_token': 'sk_test_1KyTG74Ouw65KYTR1O03WjNA00viNjcIfO', 'livemode': False, 'refresh_token': 'rt_H3Vrhd0XbSH7zbmqfDyMNwolgt1Gd7r4ESBDBr5a4VkCzTRT', 'token_type': 'bearer', 'stripe_publishable_key': 'pk_test_**********', 'stripe_user_id': 'acct_1GVOpAF7ag87i2I6', 'scope': 'express'}
Looks like i got it, if there is a flaw here let me know but here is what i have:
class TestStripeAuthorizeCallbackView:
#patch('user.views.requests')
def test_authorizecallback_creates_stripe_details(self, requests_mock):
json = { 'stripe_user_id' : '4444', 'access_token' : '2222', 'refresh_token' : '1111'}
requests_mock.post.return_value.json.return_value = json
user = mixer.blend('user.CustomUser', stripe_user_id=None, access_token=None, refresh_token=None)
req = RequestFactory().get('/', data={'code' : '1234'})
middleware = SessionMiddleware()
middleware.process_request(req)
req.session.save()
messages = FallbackStorage(req)
setattr(req, '_messages', messages)
req.user = user
resp = StripeAuthorizeCallbackView.as_view()(req)
assert resp.status_code == 302 ,'should redirect to success url'
assert user.stripe_user_id == '4444', 'should assign stripe_user_id to user'
assert user.stripe_access_token == '2222', 'should assign an access_token'
assert user.stripe_refresh_token == '1111', 'should assign a refresh_token'
What you're describing isn't mocking so much as it is end-to-end testing, connecting actual test accounts, which you can do.
As long as you're using a test client_id then when you are redirected to Stripe to create the account you can skip the form via a link and get directed back to your site with a real (test mode) oauth code.
Essentially you can set this up and actually go through the flow to create & connect new disposable test Stripe accounts.

Django rest framework API calling to another API [duplicate]

Is there any way to make a RESTful api call from django view?
I am trying to pass header and parameters along a url from the django views. I am googling from half an hour but could not find anything interesting.
Any help would be appreciated
Yes of course there is. You could use urllib2.urlopen but I prefer requests.
import requests
def my_django_view(request):
if request.method == 'POST':
r = requests.post('https://www.somedomain.com/some/url/save', params=request.POST)
else:
r = requests.get('https://www.somedomain.com/some/url/save', params=request.GET)
if r.status_code == 200:
return HttpResponse('Yay, it worked')
return HttpResponse('Could not save data')
The requests library is a very simple API over the top of urllib3, everything you need to know about making a request using it can be found here.
Yes i am posting my source code it may help you
import requests
def my_django_view(request):
url = "https://test"
header = {
"Content-Type":"application/json",
"X-Client-Id":"6786787678f7dd8we77e787",
"X-Client-Secret":"96777676767585",
}
payload = {
"subscriptionId" :"3456745",
"planId" : "check",
"returnUrl": "https://www.linkedin.com/in/itsharshyadav/"
}
result = requests.post(url, data=json.dumps(payload), headers=header)
if result.status_code == 200:
return HttpResponse('Successful')
return HttpResponse('Something went wrong')
In case of Get API
import requests
def my_django_view(request):
url = "https://test"
header = {
"Content-Type":"application/json",
"X-Client-Id":"6786787678f7dd8we77e787",
"X-Client-Secret":"96777676767585",
}
result = requests.get(url,headers=header)
if result.status_code == 200:
return HttpResponse('Successful')
return HttpResponse('Something went wrong')
## POST Data To Django Server using python script ##
def sendDataToServer(server_url, people_count,store_id, brand_id, time_slot, footfall_time):
try:
result = requests.post(url="url", data={"people_count": people_count, "store_id": store_id, "brand_id": brand_id,"time_slot": time_slot, "footfall_time": footfall_time})
print(result)
lJsonResult = result.json()
if lJsonResult['ResponseCode'] == 200:
print("Data Send")
info("Data Sent to the server successfully: ")
except Exception as e:
print("Failed to send json to server....", e)

on data.put() i need to display to the user that the data has been successfully submitted or failure incase pof one

am using python and google app engine majorly on jinja2 templates
i could like when a user registers a new account, they get a popup indicating that their registration is successful of even any alert on the very interface before moving to the next registration step.
def post(self):
user = (str(users.get_current_user().email()))
userquery = Users.query(Users.email == user)
count = userquery.count()
if count == 0:
#test if user is admin or employee
qry = Users.query()
count = qry.count()
if count == 0:
privilage = 'admin'
db_put = Users(
f_name=self.request.get("f_name"),
l_name = self.request.get("l_name"),
org = self.request.get("org"),
email=users.get_current_user().email(),
privilage = privilage
)
db_put.put()
How are you calling this POST method? Are you sending a form there directly (use method 1) or is this being done with an AJAX call (use method 2)?
Method 1
You can redirect to a GET page where you render a template with a success or error message for Jinja to use. This would however involve a page change.
import webapp2
class MyHandler(webapp2.RequestHandler):
def get(self): # Let's assume /someurl is mapped to this handler.
template_values = {}
notification = self.request.get('notification')
if notification:
template_values['notification'] = notification
self.response.set_status(200)
self.response.headers['Content-Type'] = 'text/html; charset=utf-8'
# Need to get the template from jinja and set it as template variable.
self.response.out.write(template.render(template_values))
def post(self):
# Do all your stuff here.
self.redirect('/someurl?notification=Success')
Alternatively you can set the parameters directly on the request instead of passing them as URI parameters:
def post(self):
# Do all your stuff here.
self.redirect('/someurl, params={'notification': 'Success'})
Method 2
In this method you can send back a JSON response with a success or error message. The caller (whatever function in your javascript that submitted the request to the backend) can use that to render a butterbar message or other popup notification of your choosing:
import json
import webapp2
class MyHandler(webapp2.RequestHandler):
def post(self):
# Do all your stuff here.
self.response.set_status(200)
self.response.headers['Content-Type'] = 'application/json; charset=utf-8'
self.response.headers['Content-Disposition'] = 'attachment'
self.response.out.write(json.JsonEncoder(sort_keys=True).encode('Success'))
For the latter, make sure you think about cross-site scripting (XSS) vulnerabilities and perhaps add a JSON prefix.

python - Flask test_client() doesn't have request.authorization with pytest

I have problem when testing my flask app with pytest.
App is required basic auth which is parameters of request.authorization in flask.
But with pytest, flask.test_client() doesn't have request.authorization.
Here's a code of fixture:
#pytest.yield_fixture(scope='session')
def app()
app = create_app()
# some setup code
ctx = app.app_context()
ctx.push()
yield app
ctx.pop()
# some teadown code
#pytest.fixture
def test_client(app)
return app.test_client()
Here's a code of test:
def test_index(test_client):
res = test_client.get("/", headers={"Authorization": "Basic {user}".format(user=b64encode(b"test_user"))})
assert res.status_code == 200
When I run this test, I got this error:
E assert 401 == 200
E + where 401 = <Response streamed [401 UNAUTHORIZED]>.status_code
Not only auth failure, but also request.authorization doesn't have any value(None).
Why this happen? Is there any solution?
Thanks.
The credentials for HTTP Basic authentication must have a username and a password separated by a colon. If you're still using python 2, try this:
def test_index(test_client):
credentials = b64encode(b"test_user:test_password")
res = test_client.get("/", headers={"Authorization": "Basic {}".format(credentials)})
assert res.status_code == 200
Python 3 is a little stricter about data sanity, so you have to make sure that the bytes are properly decoded before sending them to the server:
def test_index(test_client):
credentials = b64encode(b"test_user:test_password").decode('utf-8')
res = test_client.get("/", headers={"Authorization": f"Basic {credentials}"})
assert res.status_code == 200
I found this solution. Maybe it can help someone:
from requests.auth import _basic_auth_str
headers = {
'Authorization': _basic_auth_str(username, password),
}
You just have to use the library 'requests'
from requests.auth import _basic_auth_str
headers = {
'Authorization': _basic_auth_str(username, password)
}
This works for me on both python 3.6 and 2.7 whereas the following only works for me on 2.7:
res = test_client.get("/", headers={"Authorization": "Basic {user}".format(user=b64encode(b"test_user:test_password"))})
If you are using new version of python (in my case 3.7) you should decode base64 string. It returns bytes and after stringify it looks like
b'basestring' which is not correct.
>>> base64.b64encode(b"user:password")
b'dXNlcjpwYXNzd29yZA=='
>>> base64.b64encode(b"user:password").decode()
'dXNlcjpwYXNzd29yZA=='
So, now my tests look like
class TestServer(unittest.TestCase):
def setUp(self) -> None:
self.client = app.test_client()
user_credentials = base64.b64encode(b"user:password").decode()
self.headers = {"Authorization": "Basic {}".format(user_credentials)}
Here is how I have wrote unit tests for API's that require authentication with custom token.
###### In your conftest.py file have the below methods
from connexion import FlaskApp
logging.basicConfig(level=logging.DEBUG)
API_FOLDER = pathlib.Path(__file__).parent / '..'
#pytest.fixture(scope="session")
def insecure_client(): # This is used for route tests that DO NOT require authorization.
cxn_app = FlaskApp(__name__,
port=5001,
specification_dir=API_FOLDER,
debug=True,
options={"debug": True, "swagger_ui": False})
cxn_app.add_api('your_api.yaml', resolver=RestyPlusResolver('api.routes'))
cxn_app._spec_file = 'your_api.yaml'
# connection stores the Flask app at app
cxn_app.app.config['SOME_KEY'] = config.CONFIG['SOME_KEY']
flask_jwt.JWT(cxn_app.app, None, None)
flask_cors.CORS(cxn_app.app)
cxn_app.app.app_context()
return cxn_app.app.test_client()
#pytest.fixture(scope="session")
def secure_client(): # This is used for route tests that REQUIRE authorization.
cxn_app = FlaskApp(__name__,
port=5001,
specification_dir=API_FOLDER,
debug=True,
options={"debug": True, "swagger_ui": False})
cxn_app.add_api('open_api.yaml', resolver=RestyPlusResolver('api.routes'))
cxn_app._spec_file = 'openapi.yaml'
# connection stores the Flask app at app
cxn_app.app.config['SOME_KEY'] = config.CONFIG['SOME_KEY']
flask_jwt.JWT(cxn_app.app, None, None)
flask_cors.CORS(cxn_app.app)
cxn_app.app.app_context()
client = cxn_app.app.test_client()
json_dict = {'user': 'your_username', 'password': 'your_pwd'}
# call the auth to get a token which can be used for API calls that require authentication.
# see below on how this is used in pytest of a route.
response = client.post('/auth', data=json.dumps(json_dict), content_type='application/json')
data = json_of_response(response)
setattr(client, '__token', data['token'])
return client
def post_json(client, url, json_dict):
"""Send dictionary json_dict as a json to the specified url """
return client.post(url, data=json.dumps(json_dict), content_type='application/json')
def json_of_response(response):
"""Decode json from response"""
return json.loads(response.data.decode('utf8'))
### Example Pytest of API that requires authentication.
def test_my_post(mocker, secure_client):
json_dict = {'id': 'TEST_01', 'phone': 'PHONE_02'}
mocker.patch('yourapi.services.User.create_user', return_value=("Success", 201))
response = secure_client.post('/user', data=json.dumps(json_dict), content_type='application/json', headers={'X-Auth':secure_client.__token})
data = json_of_response(response)
assert response.status_code == 201
assert data == "Success"

Django : Calling a Url with get method

I have the url and corresponding view as follows.
url(r'^(?P<token>.*?)/ack$', views.api_ACK, name='device-api_ack')
def api_ACK(request, token):
"""
Process the ACK request comming from the device
"""
logger.info('-> api_ACK', extra={'request': request, 'token' : token, 'url': request.get_full_path()})
logger.debug(request)
if request.method == 'GET':
# verify the request
action, err_msg = api_verify_request(token=token, action_code=Action.AC_ACKNOWLEDGE)
return api_send_answer(action, err_msg)
I want to call api_ACK function with request method as GET from another view api_send_answer
I am creating one url in /device/LEAB86JFOZ6R7W4F69CBIMVBYB9SFZVC/ack in api_send_answer view as follows..
def api_send_answer(action, err_msg, provisional_answer=None):
last_action = create_action(session,action=Action.AC_ACKNOWLEDGE,token=last_action.next_token,timer=500)
url = ''.join (['/device/',last_action.next_token ,'/',Action.AC_ACKNOWLEDGE])
logger.debug('Request Url')
logger.debug(url)
response = api_ACK(request=url,token=last_action.next_token) # This is wrong
Now from api_send_answer it is redirecting to api_ACK view, but how to call api_ACK with request method as GET?
Please help..Any suggestions would be helpful to me
This line
response = api_ACK(request=url,token=last_action.next_token) is wrong because view expects HttpRequest object and you give him url instead.
if you need to return view response to user, you can use redirect:
def api_send_answer(action, err_msg, provisional_answer=None):
last_action = create_action(session,action=Action.AC_ACKNOWLEDGE,token=last_action.next_token,timer=500)
url = ''.join (['/device/',last_action.next_token ,'/',Action.AC_ACKNOWLEDGE])
logger.debug('Request Url')
logger.debug(url)
return HttpResponseRedirect(url)
if you need to do something else with view response you have to use HttpRequest object not url as parameter.