In flask, why request header is None in app.before_request()? - python

When I try to access the header of a request in a function adnotated with #app.before_request, it is always None. Anyone had this problem before?
Here is the function:
#app.before_request
def verifyToken():
if request.endpoint in ['myEndpoint']
auth = request.headers.get('Authorization')
if auth.startswith('Bearer '):
jwtToken = auth[7:]
try:
decoded = jwt.decode(jwtToken, 'secret_key', algorithms=['HS256'])
except jwt.ExpiredSignatureError as e:
responseObject = {
"status": "failed",
"message": "Token has expired. Please login again."
}
return jsonify(responseObject), 401

I was being 5 months in Flask, I discover Flask Embeded Middleware fires up 2 times, the first one got None value and the second one is the client request and each fire up has different request.method, the OPTIONS and <DEFAULT METHOD> of the client, why there is an OPTIONS method first in before...request and after...request befoe goes in client request?, I'm still searching for reference
and that is why I just catch it through conditional statement if request.method != 'OPTIONS': and here how it is look like
from flask import request
def my_before_request():
if request.method !== 'OPTIONS'
token = request.headers['<header var>']
# do something...
# same as after request

The OPTIONS method could have been triggered by the browser due to request being preflighted. This is done to determine if the actual request is safe to send. Refer CORS

Related

passing json request to Flask API

I am new to API. I have a below json request:
{
"Description": {
"punctuation": "yes",
"stopwords": "yes",
"lower-case":"yes",
"lemetization":"yes"
}
}
I want to pass this json to my below service:
#app.route('/user_input', methods=['POST'])
def user_input():
##Request table and Json request from user
user_request = request.get_json()
user_request_df =pd.DataFrame(user_request)
return print(df.head())
I am using the below command to call the service:
res = requests.get("http://127.0.0.1:5000/user_input", json={"Description": {"punctuation": "yes","stopwords": "yes","lower-case":"yes","lemetization":"yes"}})
After running the this line i am getting <Response [405]> error.
Here in the above example I am calling one service from another service. Similarly I want to call multiple service.
<Response [405]>
Status 405 is Method Not Allowed, this is caused by fact that you are using GET HTTP verb (requests.get) whilst also allowing only POST requests in Flask using methods=
#app.route('/user_input', methods=['POST'])
Try using requests.post instead requests.get
Edit: I noticed that you do return print(df.head()) which is same as return None as print does always return None. I am not sure what you want to do exatcly, but if you want to get string representation of df.head() then you should probably do str(df.head()).

How to send authorization header from html to flask

I have a project that use flask in backend and jinja and html for frontend.
What i need is to send a request that has an authorization header and all my routes read that header to see if its from an valid user or not?
def show_admin():
data = request.headers.get('Authorization')
# data = "TOKEN123"
# واکشی اطلاعات مورد نیاز صفحه داشبورد
content = {
'user': mydb.selectalluser(),
'doreh': mydb.selectalldoreh()
}
# چک میشود اگر توکن ارسالی توسط کاربری معتبر است یا خیر
if str(data) == "TOKEN123":
return render_template('admin/dashboard.html', content=content)
# return hello
else:
# اگر توکن معتبر باشد صفحه لود خواهد شد
return render_template('login/index.html')
In the if statement it check if the token is valid or not. but...
1. how to generate a request that included Authorization header
2. how to generate a token for login page
You can't control the client-side request header (i.e. Authorization) on the server-side. IMO, what you want is manage user login/session status, you can implement this with Flask session, the information stored on session can be obtained between requests (use Cookie):
from flask import session
#app.route('/login')
def login():
session['token'] = 'TOKEN123' # store token, use it as a dict
return 'login success'
#app.route('/admin')
def show_admin():
if session.get('token') == "TOKEN123": # get token
return 'admin page'
else:
return 'login page'
However, I would recommend using Flask-Login to handle user session manganment.

CherryPy authentication timeout

I added digest authentication to my CherryPy server, and I wanted to know on what criteria the users' authentication is revoked and they are prompted to enter credentials again. Deleting cookies doesn't force a prompt, but using Incognito or another browser does.
My config:
{ 'tools.auth_digest.on': True,
'tools.auth_digest.realm': 'localhost',
'tools.auth_digest.get_ha1': auth_digest.get_ha1_dict_plain(USERS),
'tools.auth_digest.key': key,
'tools.auth_digest.accept_charset': 'UTF-8' }
Thanks
You need to have the proper HTTP response so that the browser clear out the user credentials, basically responding with 401 Unauthorized and a challenge in how to authenticate with the WWW-Authenticate header.
Here is an implementation using a custom CherryPy tool and a Cookie that is used as way to communicate the intention to the browser and the backend (HTTP auth is stateless and we have to go back and forth to deauth and redirect).
import cherrypy
from cherrypy.lib import auth_digest
REALM = 'localhost'
KEY = '24684651368351320asd1wdasd'
CHARSET = 'UTF-8'
#cherrypy.tools.register('before_handler')
def with_logout_handler():
if cherrypy.request.cookie.get('Unauthorize') is not None:
response = cherrypy.response
response.headers['WWW-Authenticate'] = auth_digest.www_authenticate(
realm=REALM,
key=KEY,
accept_charset=CHARSET
)
# delete the cookie that was used to mark the intention to logout
response.cookie['Unauthorize'] = 1
response.cookie['Unauthorize']['expires'] = 0
raise cherrypy.HTTPError(
401, 'You are not authorized to access that resource')
class App:
#cherrypy.expose
#cherrypy.tools.with_logout_handler()
def index(self):
return ('Welcome {}! Do you want to logout?'
.format(cherrypy.request.login))
#cherrypy.expose
def logout(self):
"""
Set a cookie to give it a clue to the index method to
remove the user credentials from the following requests.
This will be handled by the tool `with_logout_handler`.
"""
cherrypy.response.cookie['Unauthorize'] = 1
raise cherrypy.HTTPRedirect("/")
def main():
users = {
'foo': 'bar'
}
cherrypy.quickstart(App(), config={
'/': {
'tools.auth_digest.on': True,
'tools.auth_digest.realm': REALM,
'tools.auth_digest.get_ha1': auth_digest.get_ha1_dict_plain(users),
'tools.auth_digest.key': KEY,
'tools.auth_digest.accept_charset': CHARSET
},
})
if __name__ == '__main__':
main()

Alamofire 5 (Beta 6): Parameters of PUT Request do not arrive in Flask-Restful

UPDATE
For me the Problem got fixed as soon as I was putting "encoding: URLEncoding(destination: .queryString)" in my request. Maybe this helps somebody else. link
I struggled the whole day to find the problem in my Alamofire PUT Request or the Flask Restful API. Request like GET, DELETE and POST are working fine with Alamofire, except the PUT Request.
When I'm using PUT Requests in combination with Postman and Flask-Restful everything is also working fine. But as soon as I'm trying to achieve the same Result with Alamofire, I'm not getting any parameters in Flask. I tried to illustrate this in the code examples.
So in short my example illustrates the following:
DELETE Request(Same with GET and POST)
Postman: success
Alamofire: success
PUT Request
Postman: success
Alamofire: failure (parameter dictionary empty in Flask-Restful)
Here is my Python Code [API Server]:
from flask import Flask, request, jsonify
from flask_restful import Resource, Api, reqparse
app = Flask(__name__)
api = Api(app)
class Stackoverflow(Resource):
def delete(self):
print(request.args)
if request.args.get('test-key') is None:
return jsonify({"message": "failure"})
else:
return jsonify({"message": "success"})
def put(self):
print(request.args)
if request.args.get('test-key') is None:
return jsonify({"message": "failure"})
else:
return jsonify({"message": "success"})
api.add_resource(Stackoverflow, '/stackoverflow')
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0')
If I'm using Postman, I get this result (like expected):
Result in Postman
But now I'm trying to do the same with Alamofire in Swift. Same Server, nothing changed.
SWIFT demo Code [IOS APP]:
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view
simplePUTRequest()
simpleDELETERequest()
}
func simplePUTRequest(){
AF.request("http://localhost:5000/stackoverflow", method: .put, parameters: ["test-key":"testvalue"])
.validate(statusCode: 200..<300)
.responseJSON { response in
if let data = response.data {
print("Result PUT Request:")
print(String(data: data, encoding: .utf8)!)
//print(utf8Text)
}else{
}
}
}
func simpleDELETERequest(){
AF.request("http://localhost:5000/stackoverflow", method: .delete, parameters: ["test-key":"testvalue"])
.validate(statusCode: 200..<300)
.responseJSON { response in
if let data = response.data {
print("Result DELETE Request:")
print(String(data: data, encoding: .utf8)!)
//print(utf8Text)
}else{
}
}
}
Xcode Console:
Result PUT Request:
{
"message": "failure"
}
Result DELETE Request:
{
"message": "success"
}
python Console (both Alamofire Requests):
ImmutableMultiDict([])
127.0.0.1 - - [15/Jun/2019 21:17:31] "PUT /stackoverflow HTTP/1.1" 200 -
ImmutableMultiDict([('test-key', 'testvalue')])
127.0.0.1 - - [15/Jun/2019 21:17:31] "DELETE /stackoverflow?test-key=testvalue HTTP/1.1" 200 -
As you can see, I'm getting the success message only while using the DELETE method.
Till now I tried using different encodings like URLEncoding.httpbody and URLEncoding.default, but nothing really helped.
For me it seems like it's a Alamofire/Swift Problem, because in Postman the same request method is working fine.
I would really appreciate your help, because I'm stuck and don't know anything further to do. I hope I didn't misunderstood something essential.
Thank you in advance!
I am currently using the same version AlamoFire, and when I use the PUT method, I use it as follows:
let request = AF.request(url, method: .put, parameters: ["uid": uid],
encoding: JSONEncoding.default, headers: headers)
request.responseJSON(completionHandler: { response in
guard response.error == nil else {
//Handle error
}
if let json = response.value as? [String: Any]
// Handle result.
}
The only difference to your post is that I used the encoding option. You can try to put the option and see what happens.
It looks like your server is expecting your PUT parameters to be URL form encoded into the URL. You may be hitting the version of the request method that uses JSON encoding by default, so adding encoder: URLEncodedFormParameterEncoder.default at the end of your request call should fix that. A future release will make that the default, as it's safe across all request types.
If that's not the issue, I suggest you investigate more closely to see what the differences between the requests may be. Since you control the server you should have easy access to the traffic.

line bot - how to get started

i've just started working with line-bot and followed the tutorial here: https://developers.line.biz/en/docs/messaging-api/building-bot/
However, I still don't understand how I can connect with my line app account, to send messages, and have these messages appear back in python.
The below is the script I copied from line tutorial.
from flask import Flask, request, abort
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import MessageEvent, TextMessage, TextSendMessage
app = Flask(__name__)
line_bot_api = LineBotApi('foo', timeout=20)
handler = WebhookHandler('bar')
user_profile = 'far'
#app.route("/", methods=['GET'])
def home():
profile = line_bot_api.get_profile(user_profile)
print(profile.display_name)
print(profile.user_id)
print(profile.picture_url)
print(profile.status_message)
return '<div><h1>ok</h1></div>'
#app.route("/callback", methods=['POST'])
def callback():
# get X-Line-Signature header value
signature = request.headers['X-Line-Signature']
# get request body as text
body = request.get_data(as_text=True)
app.logger.info("Request body: " + body)
# handle webhook body
try:
handler.handle(body, signature)
except InvalidSignatureError:
abort(400)
return 'OK'
#handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text='hello world'))
if __name__ == "__main__":
app.run(debug=True)
What am I missing, or how can I connect with the line app to send and receive messages?
I followed that tutorial and was able to successfully create a bot that just echoes messages in uppercase:
Your question is how to "connect" your bot's code with the LINE app. The three most important parts of the tutorial are probably:
Adding the bot as a friend, you do this by scanning its QR code with the LINE app
When you create a channel for your bot, you need to enable "Webhooks" and provide an https endpoint which is where LINE will send the interaction events your bot receives. To keep this simple for the purposes of this answer, I created an AWS Lambda function and exposed it via API Gateway as an endpoint that looked something like:
https://asdfasdf.execute-api.us-east-1.amazonaws.com/default/MyLineBotFunction. This is what I entered as the Webhook URL for my bot.
Once you are successfully receiving message events, responding simply requires posting to the LINE API with the unique replyToken that came with the message.
Here is the Lambda function code for my simple yell-back-in-caps bot:
import json
from botocore.vendored import requests
def lambda_handler(event, context):
if 'body' in event:
message_event = json.loads(event['body'])['events'][0]
reply_token = message_event['replyToken']
message_text = message_event['message']['text']
requests.post('https://api.line.me/v2/bot/message/reply',
data=json.dumps({
'replyToken': reply_token,
'messages': [{'type': 'text', 'text': message_text.upper()}]
}),
headers={
# TODO: Put your channel access token in the Authorization header
'Authorization': 'Bearer YOUR_CHANNEL_ACCESS_TOKEN_HERE',
'Content-Type': 'application/json'
}
)
return {
'statusCode': 200
}

Categories

Resources