Set the HTTP status text in a Flask response - python

How can I set the HTTP status text for a response in Flask?
I know I can return a string with a status code
#app.route('/knockknock')
def knockknock():
return "sleeping, try later", 500
But that sets the body, I'd like to change the HTTP status text from "INTERNAL SERVER ERROR" to "sleeping, try later".
It seems this is not possible in Flask/Werkzeug. It's not mentioned anywhere. But maybe I'm missing something?

The following will work in Flask. Flask can use either a status code or description while returning response. The description can obviously contain the code as well (from the client side it is identical). However note that this doesn't generate the default error description from the Flask side.
from flask import Flask
app = Flask(__name__)
#app.route('/knockknock')
def knockknock():
return "", "500 sleeping, try later"
Here is the output from the curl command,
curl -i http://127.0.0.1:5000/knockknock
HTTP/1.0 500 sleeping, try later
Content-Type: text/html; charset=utf-8

I'm not an expert on this, but I'm afraid this will only be possible through monkeypatching.
Flask returns werkzeug Response objects, and it seems that the status code reasons are hardcoded in http.py.
Again, some monkeypatching might be enough to change this for your application however.

Maybe this document will be helpful,you can customorize your own error page.
http://flask.pocoo.org/docs/0.12/patterns/errorpages/
by review the source code, in app.py can find
if status is not None:
if isinstance(status, string_types):
rv.status = status
else:
rv.status_code = status
so using
#app.route('/test')
def tt():
return "status error", "500 msg"
works.
Sorry for the misunderstanding.

Related

Raise Airflow Exception to Fail Task from CURL request

I am using airflow to schedule and automate Python scripts housed on a Ubuntu server. The DAG triggers a CURL request that hits a Flask API on the same machine which actually runs the script. Here is a high level overview of the flow:
Airflow --> Curl request --> Flask API --> Python Script
DAG Task:
t2 = BashOperator (
task_id='extract_pcty_data',
bash_command=f"""curl -d '{dataset}' -H 'Content-Type: application/json' -X POST {base_url}{endpoint}""",
)
Endpoint Registration:
api.add_resource(paylocity, "/api/v1/application/paylocity")
Resource Object:
class paylocity(Resource):
def __init__(self):
self.reqparse = reqparse.RequestParser()
def get(self):
return 200
def post(self):
try:
if request.json:
data = request.json
query = data['dataset']
pcty = PaylocityAPI()
pcty.auth()
pcty.get_employees()
pcty.get_paystatements()
pcty.load_dataset()
pcty.clean_up()
return 200
except Exception as e:
print(traceback.print_exc(e))
raise ValueError(e)
The issue I am running into, is that the script will fail for some reason which gets caught by the try/catch block and then raises the value error - but it does not cause the script to fail because the HTTP request response returned is 500 - Internal Server Error. What I am looking for is a simple and elegant way to interpret an HTTP response that is not 200 - OK as a "failure" and raising something like a ValueError or AirflowException to cause the task to fail. Any guidance or support would be greatly appreciated!
To those of you from Google looking for a simple and elegant answer to this or a similar question. Curl has a few flags that allow you to specify how you want the fail behavior of a request to act. For my specific scenario: --fail was the most appropriate. There is also --fail-with-body that allows you to get the content of the fail response rather than just the non-zero exit code. From their docs:
-f, --fail
(HTTP) Fail fast with no output at all on server errors. This is useful to enable scripts and users to better deal with failed attempts. In normal cases when an HTTP server fails to deliver a document, it returns an HTML document stating so (which often also describes why and more). This flag will prevent curl from outputting that and return error 22.
This method is not fail-safe and there are occasions where non-successful response codes will slip through, especially when authentication is involved (response codes 401 and 407).
Example:
curl --fail https://example.com

requests.exceptions.HTTPError: 401 Client Error atlassian-python-api

I am trying to connect to a Confluence page using the python wrapper on the API (as I am not familiar with any of this) but I keep getting the following error:
requests.exceptions.HTTPError: 401 Client Error
I know that people talk about this being caused by the necessity of using an API token but the page runs on an old version of Confluence and I have been told that we cannot use access tokens.
So has anyone any other idea? Here's a small code:
from atlassian import Confluence
confluence = Confluence(
url='https://address',
username='name',
password='pwd'
)
confluence.create_page(
space='Test',
title='A title',
body='something')
I have tried to use an older version of atlassian-python-api just in case there was some conflict but it got me the same error.
Your code looks ok. Authenticating to Confluence using Basic Auth should work without generating an API token, afaik.
The 401 status definitely suggests a problem with the authentication though. The obvious reason for this would be of course wrong credentials, but I assume that you have double checked that the credentials work when interactively logging into confluence with a browser.
To get a better sense of the error, you can import logging to debug your requests and response:
from atlassian import Confluence
import logging
logging.basicConfig(filename='conf_connect.log', filemode='w', level=logging.DEBUG)
try:
c = Confluence(url='https://conf.yoursystem.com', username='name', password='pwd')
# atlassian API does not raise error on init if credentials are wrong, this only happens on the first request
c.get_user_details_by_username('name')
except Exception as e:
logging.error(e)
The Confluence module internally also uses logging, so the requests and responses will appear in your conf_connect.log logfile:
DEBUG:atlassian.rest_client:curl --silent -X GET -H 'Content-Type: application/json' -H 'Accept: application/json' 'https://conf.yoursystem.com/rest/api/user?username=name'
DEBUG:urllib3.connectionpool:Starting new HTTPS connection (1): conf.yoursystem.com:443
DEBUG:urllib3.connectionpool:https://conf.yoursystem.com:443 "GET /rest/api/user?username=name HTTP/1.1" 401 751
DEBUG:atlassian.rest_client:HTTP: GET rest/api/user -> 401
DEBUG:atlassian.rest_client:HTTP: Response text -> <!doctype html><html lang="en"><head><title>HTTP Status 401 – Unauthorized</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 401 – Unauthorized</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Message</b> Basic Authentication Failure - Reason : AUTHENTICATED_FAILED</p><p><b>Description</b> The request has not been applied because it lacks valid authentication credentials for the target resource.</p><hr class="line" /><h3>Apache Tomcat/9.0.33</h3></body></html>
ERROR:root:401 Client Error: for url: https://conf.yoursystem.com/rest/api/user?username=name
The response body may include some information on the reason:
HTTP Status 401 – UnauthorizedType Status ReportMessage Basic Authentication Failure - Reason : AUTHENTICATED_FAILEDDescription The request has not been applied because it lacks valid authentication credentials for the target resource.
The reason AUTHENTICATED_FAILED suggests something is likely wrong with your credentials. If you want to dig deeper into that, you can use this SO answer to also display the headers that are being sent with your request.
However, if your reason is AUTHENTICATION_DENIED the problem is likely the following: If you have too many failed authentication attempts in a row, a CAPTCHA challenge is triggered, and this error will occur until the Failed Login Count is reset. This can easily happen when you are developing a script and test it frequently. To remedy this, either open a browser and manually (re-)logon to Confluence, completing the CAPTCHA, or resolve it from the Confluence User Management.

Return an HTTP fail (e.g. 500) response with JSON body from flask?

As of flask 1.1.x, it is easy to return a JSON response body along with an HTTP success code, such as
return {"success": 1, "message": "You did it!"}, 200
However, returning an HTTP failure status the same way
return {"success": 0, "message": "You screwed up."}, 400
only seems to return a response header and message, (e.g. HTTP/1.0 INVALID REQUEST)
The Content-type header (correctly) says application/json, but, the body is empty. This happens for any non-2xx class status code.
Flask documentation implies I should be able to do this, but, nothing I've tried seems to work. This includes creating specialized error handlers like
#app.errorhandler(400)
def custom400(error):
return jsonify({'message': error})
and then calling abort(400, 'my error message'), which doesn't work since the error argument to the handler isn't the string passed to abort, but, is always an error object of whatever type the HTTP error is (e.g. 410 -> <GONE Gone>.
Is this "normal" HTTP response behaviour, or is flask doing something weird?
It turns out this is more PEBKAC than anything else. I had set a breakpoint inside my $.ajax.error handler, looking at the returned response in the browser (chrome) debug window when the breakpoint fired.
But,the body of the response isn't made available to the debug window until AFTER this handler exists. Once I continued execution of the error handler, the JSON response body is came through as expected.

failed API call - error 400

I've written a simple API call using requests and am getting an error 400 on executing the call. Can someone please tell me where I am going wrong? Thanks for the help. Here's the code i wrote -
import requests
params={
'api_key':'gozbsSP1fxqNSS5YjcFM7qcjjKch1tBB',
'api_secret':'HklHJCzfO87YyIC9DudGArVKJtioEhbO',
'image_url':'http://picz.in/data/media/7/study-in-canada-students.jpg'
}
r = requests.post(url='https://api-
us.faceplusplus.com/facepp/v3/detect',data=params)
print(r)
400 error code basically means it's a bad request. So it can be that you provided the wrong params for the api, or the api_key or api_secret is not correct.
Check the documentation of the API whether you are sending all the required params are there or not. If so check your secret keys and make sure it's the correct one.
Remeber to never post you API Keys on public forums like SO...
400 means its a bad request: the request you made is not what the server wanted.
(invalid arguments, wrong payload data, etc)
More info on 400 errors here:
https://airbrake.io/blog/http-errors/400-bad-request
The 400 Bad Request Error is an HTTP response status code that indicates that the server was unable to process the request sent by the client due to invalid syntax
Sometimes the response object will include information on why the request failed, but that's not always the case.

how to redirect to a external 404 page python flask

I am trying to redirect my 404 to a external URL like this:
#app.route('404')
def http_error_handler(error):
return flask.redirect("http://www.exemple.com/404"), 404
but it does not work. I keep getting:
Not Found
The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
You should try something like this:
from flask import render_template
#app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
Source http://flask.pocoo.org/docs/1.0/patterns/errorpages/
You cannot do this - the user-agent (in most cases, a browser) looks at the status code that is returned to determine what to do. When you return a 404 status code what you are saying to the user-agent is, "I don't know what this thing is you are requesting" and the user-agent can then:
Display what you return in the body of the response
Display its own error message to the end user
Do some combination of the above two options
redirect actually creates a little HTML response (via werkzeug.exceptions), which normally the end user doesn't see because the user-agent follows the Location header when it sees the 302 response. However, you override the status code when you provide your own status code (404).
The fix is to either:
Remove the status code (at the cost of sending the wrong signal to the end user, potentially)
or Send a 404 with a meta:refresh and / or JavaScript redirect (slightly better, still confusing):
return redirect("/where-ever"), 404, {"Refresh": "1; url=/where-ever"}
Try this instead of a route
from flask import request
#app.errorhandler(404)
def own_404_page(error):
pageName = request.args.get('url')
print(pageName)
print(error)
f = open('erreur404.tpl')
return f.read()

Categories

Resources