Django rest framework API calling to another API [duplicate] - django

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)

Related

Request in flask API deployed in Cloud Run doesn't work

I have an application in Flask that should receive an image per POST request. The request works with the server running locally, but it does not work on my API implemented by Cloud Run :(
I would appreciate any pointers!
Regards, Helena.
#app.route("/predict", methods=["GET", "POST"])
def predict():
# initialize the data dictionary that will be returned from the view
data = {"success": False}
# ensure an image was properly uploaded to our endpoint
if flask.request.method == 'POST':
_file = flask.request.files['image']
if _file:
# read the image in PIL format
thermal_image = flask.request.files['image'].read()
# get thermal region on embedded image
try:
image = get_cropped_embedded(thermal_image)
#print(image)
except Exception as e:
print(e)
data = {"Success": 'Be positive!'}
return flask.jsonify(data)
################ More code
data.update({
"path": path,
"success": True
})
else:
logging.info("No hot point found out. Exitting without classification.")
return flask.jsonify(data)
# return the data dictionary as a JSON response
return flask.jsonify(data)
if __name__ == "__main__":
app.run('0.0.0.0', port=5000)
desired result
result

Why i am getting 400 Bad Request error when sending json data in 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.

django and python requests - getting a 403 on a post request

I am using requests to log into my Django site for testing (and yes, I know about the Django TestClient, but I need plain http here). I can log in and, as long as I do get requests, everything is OK.
When I try to use post instead, I get a 403 from the csrf middleware. I've worked around that for now by using a #crsf_exempt on my view, but would prefer a longer term solution.
This is my code:
with requests.Session() as ses:
try:
data = {
'username': self.username,
'password': self.password,
}
ses.get(login_url)
try:
csrftoken = ses.cookies["csrftoken"]
except Exception, e:
raise
data.update(csrfmiddlewaretoken=csrftoken)
_login_response = ses.post(login_url, data=data)
logger.info("ses.cookies:%s" % (ses.cookies))
assert 200 <= _login_response.status_code < 300, "_login_response.status_code:%s" % (_login_response.status_code)
response = ses.post(
full_url,
data=data,
)
return self._process_response(response)
The login works fine, and I can see the csrf token here.
INFO:tests.helper_fetch:ses.cookies:<RequestsCookieJar[<Cookie csrftoken=TmM97gnNHs4YCgQPzfNztrAWY3KcysAg for localhost.local/>, <Cookie sessionid=kj6wfmta
However, the middleware sees cookies as empty.
INFO:django.middleware.csrf:request.COOKIES:{}
I've added the logging code to it:
def process_view(self, request, callback, callback_args, callback_kwargs):
if getattr(request, 'csrf_processing_done', False):
return None
try:
csrf_token = _sanitize_token(
request.COOKIES[settings.CSRF_COOKIE_NAME])
# Use same token next time
request.META['CSRF_COOKIE'] = csrf_token
except KeyError:
# import pdb
# pdb.set_trace()
import logging
logger = logging.getLogger(__name__)
logger.info("request.COOKIES:%s" % (request.COOKIES))
Am I missing something with way I call request's session.post? I tried adding cookie to it, made no difference. But I can totally see why crsf middleware is bugging out. I thought the cookies were part of the session, so why are they missing in my second post?
response = ses.post(
self.res.full_url,
data=data,
cookies=ses.cookies,
)
This variation, inspired by How to send cookies in a post request with the Python Requests library?, also did not result in anything being passed to csrf middleware:
response = ses.post(
self.res.full_url,
data=data,
cookies=dict(csrftoken=csrftoken),
)
For subsequent requests after the login, try supplying it as header X-CSRFToken instead.
The following worked for me:
with requests.Session() as sesssion:
response = session.get(login_url)
response.raise_for_status() # raises HTTPError if: 400 <= status_code < 600
csrf = session.cookies['csrftoken']
data = {
'username': self.username,
'password': self.password,
'csrfmiddlewaretoken': csrf
}
response = session.post(login_url, data=data)
response.raise_for_status()
headers = {'X-CSRFToken': csrf, 'Referer': url}
response = session.post('another_url', data={}, headers=headers)
response.raise_for_status()
return response # At this point we probably made it
Docs reference: https://docs.djangoproject.com/en/dev/ref/csrf/#csrf-ajax
You could also try to use this decorator on your view, instead of the csrf_exempt. I tried to reproduce your issue, and this worked as well for me.
from django.views.decorators.csrf import ensure_csrf_cookie`
#ensure_csrf_cookie
def your_login_view(request):
# your view code

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-nocaptcha-recaptcha always shows additional verification box

I installed django-nocaptcha-recaptcha and integrated it into my form:
from nocaptcha_recaptcha.fields import NoReCaptchaField
class ClientForm(forms.ModelForm):
captcha = NoReCaptchaField()
It shows up fine on the form, but whenever I click on it an additional dialog pops up asking to enter some text and verify. It happens every time. I tested it from another computer on another network and it still asks for additional verification after clicking the box.
This is what it looks like: additional verification dialog box
Here's how I'm handling the form:
#xframe_options_exempt
def registration(request):
if request.method == 'POST':
clientform = ClientForm(request.POST)
# check whether it's valid:
if clientform.is_valid():
new_client = clientform.save()
...
What am I doing wrong? Is it a problem with django-nocaptcha-recaptcha? Should I use something else?
P.S. I'm using django 1.7.1 with python 3.4
Another alternative: Minimalist and non framework dependant.
This is the code, in case you want to rewrite it.
'''
NO-CAPTCHA VERSION: 1.0
PYTHON VERSION: 3.x
'''
import json
from urllib.request import Request, urlopen
from urllib.parse import urlencode
VERIFY_SERVER = "www.google.com"
class RecaptchaResponse(object):
def __init__(self, is_valid, error_code=None):
self.is_valid = is_valid
self.error_code = error_code
def __repr__(self):
return "Recaptcha response: %s %s" % (
self.is_valid, self.error_code)
def __str__(self):
return self.__repr__()
def displayhtml(site_key, language=''):
"""Gets the HTML to display for reCAPTCHA
site_key -- The site key
language -- The language code for the widget.
"""
return """<script src="https://www.google.com/recaptcha/api.js?hl=%(LanguageCode)s" async="async" defer="defer"></script>
<div class="g-recaptcha" data-sitekey="%(SiteKey)s"></div>
""" % {
'LanguageCode': language,
'SiteKey': site_key,
}
def submit(response,
secret_key,
remote_ip,
verify_server=VERIFY_SERVER):
"""
Submits a reCAPTCHA request for verification. Returns RecaptchaResponse
for the request
response -- The value of response from the form
secret_key -- your reCAPTCHA secret key
remote_ip -- the user's ip address
"""
if not(response and len(response)):
return RecaptchaResponse(is_valid=False, error_code='incorrect-captcha-sol')
def encode_if_necessary(s):
if isinstance(s, str):
return s.encode('utf-8')
return s
params = urlencode({
'secret': encode_if_necessary(secret_key),
'remoteip': encode_if_necessary(remote_ip),
'response': encode_if_necessary(response),
})
params = params.encode('utf-8')
request = Request(
url="https://%s/recaptcha/api/siteverify" % verify_server,
data=params,
headers={
"Content-type": "application/x-www-form-urlencoded",
"User-agent": "reCAPTCHA Python"
}
)
httpresp = urlopen(request)
return_values = json.loads(httpresp.read().decode('utf-8'))
httpresp.close()
return_code = return_values['success']
if return_code:
return RecaptchaResponse(is_valid=True)
else:
return RecaptchaResponse(is_valid=False, error_code=return_values['error-codes'])
Restart the server and don't forget to clear your browser's cache. Hope this helps.