Python passing a list of URL instead of one URL then output into excel file - python-3.x

I am trying to modify the code below to check the header URLs from a list
Usage
$ python securityheaders.py --max-redirects 5 https://secfault.fi
Output
Header 'x-xss-protection' is missing ... [ WARN ]
Header 'x-content-type-options' is missing ... [ WARN ]
Header 'content-security-policy' is missing ... [ WARN ]
Header 'x-powered-by' is missing ... [ OK ]
Header 'x-frame-options' contains value 'DENY' ... [ OK ]
Header 'strict-transport-security' contains value 'max-age=63072000' ... [ OK ]
Header 'access-control-allow-origin' is missing ... [ OK ]
Header 'server' contains value 'nginx/1.10.1' ... [ WARN ]
HTTPS supported ... [ OK ]
HTTPS valid certificate ... [ OK ]
HTTP -> HTTPS redirect ... [ OK ]
My question is the following:
Instead of passing one URL as argument like the usage above, is it possible to pass a list of URL to check the header and then save the result into the excel file like below.
Which part of the code should I modify?
Any help would be much appreciated
Thank you for reading
Desire ouput
Code
Source code
import http.client
import argparse
import socket
import ssl
import sys
import re
from urllib.parse import urlparse
class SecurityHeaders():
def __init__(self):
pass
def evaluate_warn(self, header, contents):
""" Risk evaluation function.
Set header warning flag (1/0) according to its contents.
Args:
header (str): HTTP header name in lower-case
contents (str): Header contents (value)
"""
warn = 1
if header == 'x-frame-options':
if contents.lower() in ['deny', 'sameorigin']:
warn = 0
else:
warn = 1
if header == 'strict-transport-security':
warn = 0
""" Evaluating the warn of CSP contents may be a bit more tricky.
For now, just disable the warn if the header is defined
"""
if header == 'content-security-policy':
warn = 0
""" Raise the warn flag, if cross domain requests are allowed from any
origin """
if header == 'access-control-allow-origin':
if contents == '*':
warn = 1
else:
warn = 0
if header.lower() == 'x-xss-protection':
if contents.lower() in ['1', '1; mode=block']:
warn = 0
else:
warn = 1
if header == 'x-content-type-options':
if contents.lower() == 'nosniff':
warn = 0
else:
warn =1
""" Enable warning if backend version information is disclosed """
if header == 'x-powered-by' or header == 'server':
if len(contents) > 1:
warn = 1
else:
warn = 0
return {'defined': True, 'warn': warn, 'contents': contents}
def test_https(self, url):
parsed = urlparse(url)
protocol = parsed[0]
hostname = parsed[1]
path = parsed[2]
sslerror = False
conn = http.client.HTTPSConnection(hostname, context = ssl.create_default_context() )
try:
conn.request('GET', '/')
res = conn.getresponse()
except socket.gaierror:
return {'supported': False, 'certvalid': False}
except ssl.CertificateError:
return {'supported': True, 'certvalid': False}
except:
sslerror = True
# if tls connection fails for unexcepted error, retry without verifying cert
if sslerror:
conn = http.client.HTTPSConnection(hostname, timeout=5, context = ssl._create_stdlib_context() )
try:
conn.request('GET', '/')
res = conn.getresponse()
return {'supported': True, 'certvalid': False}
except:
return {'supported': False, 'certvalid': False}
return {'supported': True, 'certvalid': True}
def test_http_to_https(self, url, follow_redirects = 5):
parsed = urlparse(url)
protocol = parsed[0]
hostname = parsed[1]
path = parsed[2]
if not protocol:
protocol = 'http' # default to http if protocl scheme not specified
if protocol == 'https' and follow_redirects != 5:
return True
elif protocol == 'https' and follow_redirects == 5:
protocol = 'http'
if (protocol == 'http'):
conn = http.client.HTTPConnection(hostname)
try:
conn.request('HEAD', path)
res = conn.getresponse()
headers = res.getheaders()
except socket.gaierror:
print('HTTP request failed')
return False
""" Follow redirect """
if (res.status >= 300 and res.status < 400 and follow_redirects > 0):
for header in headers:
if (header[0].lower() == 'location'):
return self.test_http_to_https(header[1], follow_redirects - 1)
return False
def check_headers(self, url, follow_redirects = 0):
""" Make the HTTP request and check if any of the pre-defined
headers exists.
Args:
url (str): Target URL in format: scheme://hostname/path/to/file
follow_redirects (Optional[str]): How deep we follow the redirects,
value 0 disables redirects.
"""
""" Default return array """
retval = {
'x-frame-options': {'defined': False, 'warn': 1, 'contents': '' },
'strict-transport-security': {'defined': False, 'warn': 1, 'contents': ''},
'access-control-allow-origin': {'defined': False, 'warn': 0, 'contents': ''},
'content-security-policy': {'defined': False, 'warn': 1, 'contents': ''},
'x-xss-protection': {'defined': False, 'warn': 1, 'contents': ''},
'x-content-type-options': {'defined': False, 'warn': 1, 'contents': ''},
'x-powered-by': {'defined': False, 'warn': 0, 'contents': ''},
'server': {'defined': False, 'warn': 0, 'contents': ''}
}
parsed = urlparse(url)
protocol = parsed[0]
hostname = parsed[1]
path = parsed[2]
if (protocol == 'http'):
conn = http.client.HTTPConnection(hostname)
elif (protocol == 'https'):
# on error, retry without verifying cert
# in this context, we're not really interested in cert validity
ctx = ssl._create_stdlib_context()
conn = http.client.HTTPSConnection(hostname, context = ctx )
else:
""" Unknown protocol scheme """
return {}
try:
conn.request('HEAD', path)
res = conn.getresponse()
headers = res.getheaders()
except socket.gaierror:
print('HTTP request failed')
return False
""" Follow redirect """
if (res.status >= 300 and res.status < 400 and follow_redirects > 0):
for header in headers:
if (header[0].lower() == 'location'):
redirect_url = header[1]
if not re.match('^https?://', redirect_url):
redirect_url = protocol + '://' + hostname + redirect_url
return self.check_headers(redirect_url, follow_redirects - 1)
""" Loop through headers and evaluate the risk """
for header in headers:
#set to lowercase before the check
headerAct = header[0].lower()
if (headerAct in retval):
retval[headerAct] = self.evaluate_warn(headerAct, header[1])
return retval
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Check HTTP security headers', \
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('url', metavar='URL', type=str, help='Target URL')
parser.add_argument('--max-redirects', dest='max_redirects', metavar='N', default=2, type=int, help='Max redirects, set 0 to disable')
args = parser.parse_args()
url = args.url
redirects = args.max_redirects
foo = SecurityHeaders()
parsed = urlparse(url)
if not parsed.scheme:
url = 'http://' + url # default to http if scheme not provided
headers = foo.check_headers(url, redirects)
if not headers:
print ("Failed to fetch headers, exiting...")
sys.exit(1)
okColor = '\033[92m'
warnColor = '\033[93m'
endColor = '\033[0m'
for header, value in headers.items():
if value['warn'] == 1:
if value['defined'] == False:
print('Header \'' + header + '\' is missing ... [ ' + warnColor + 'WARN' + endColor + ' ]')
else:
print('Header \'' + header + '\' contains value \'' + value['contents'] + '\'' + \
' ... [ ' + warnColor + 'WARN' + endColor + ' ]')
elif value['warn'] == 0:
if value['defined'] == False:
print('Header \'' + header + '\' is missing ... [ ' + okColor + 'OK' + endColor +' ]')
else:
print('Header \'' + header + '\' contains value \'' + value['contents'] + '\'' + \
' ... [ ' + okColor + 'OK' + endColor + ' ]')
https = foo.test_https(url)
if https['supported']:
print('HTTPS supported ... [ ' + okColor + 'OK' + endColor + ' ]')
else:
print('HTTPS supported ... [ ' + warnColor + 'FAIL' + endColor + ' ]')
if https['certvalid']:
print('HTTPS valid certificate ... [ ' + okColor + 'OK' + endColor + ' ]')
else:
print('HTTPS valid certificate ... [ ' + warnColor + 'FAIL' + endColor + ' ]')
if foo.test_http_to_https(url, 5):
print('HTTP -> HTTPS redirect ... [ ' + okColor + 'OK' + endColor + ' ]')
else:
print('HTTP -> HTTPS redirect ... [ ' + warnColor + 'FAIL' + endColor + ' ]')

Related

connecting to mikrotik using routeros api

I am automating WiFi payments, so my code is able to ask for payments and a callback is sent. I get the information and save it, generate a user, send an sms to the client and create the generated user in mikrotik all at the same time.
Below is my code:
'''
def confirmation(request):
print("Start Mpesa")
global profile
mpesa_body = request.body.decode('utf-8')
mpesa_body = json.loads(mpesa_body)
print(mpesa_body)
print("Mpesa Body")
merchant_requestID = mpesa_body["Body"]["stkCallback"]["MerchantRequestID"]
print('merchant_requestID: ', merchant_requestID)
checkout_requestID = mpesa_body["Body"]["stkCallback"]["CheckoutRequestID"]
print('checkout_requestID: ', checkout_requestID)
result_code = mpesa_body["Body"]["stkCallback"]["ResultCode"]
print('result_code: ', result_code)
result_desc = mpesa_body["Body"]["stkCallback"]["ResultDesc"]
print('result_desc: ', result_desc)
metadata = mpesa_body["Body"]["stkCallback"]["CallbackMetadata"]["Item"]
for item in metadata:
title = item["Name"]
if title == "Amount":
amount = item["Value"]
print('Amount: ', amount)
elif title == "MpesaReceiptNumber":
mpesa_receipt_number = item["Value"]
print('Mpesa Reference No: ', mpesa_receipt_number)
elif title == "TransactionDate":
transaction_date = item["Value"]
print('Transaction date: ', transaction_date)
elif title == "PhoneNumber":
phone_number = item["Value"]
print('Phone Number: ', phone_number)
str_transaction_date = str(transaction_date)
trans_datetime = datetime.strptime(str_transaction_date, "%Y%m%d%H%M%S")
tz_trans_datetime = pytz.utc.localize(trans_datetime)
payment = MpesaPayment.objects.create(
MerchantRequestID=merchant_requestID,
CheckoutRequestID=checkout_requestID,
ResultCode=result_code,
ResultDesc=result_desc,
Amount=amount,
MpesaReceiptNumber=mpesa_receipt_number,
TransactionDate=tz_trans_datetime,
PhoneNumber=phone_number,
)
if result_code == 0:
payment.save()
print("Successfully saved")
password_length = 5
password = secrets.token_urlsafe(password_length)
print(password)
headers = {
'Content-Type': 'application/json',
'h_api_key': os.getenv("SMS_Token"),
'Accept': 'application/json'
}
payload = {
"mobile": phone_number,
"response_type": "json",
"sender_name": "23107",
"service_id": 0,
"message": "Your WiFi Voucher is " + password + ".\nAirRumi."
}
response = requests.request("POST", 'https://api.mobitechtechnologies.com/sms/sendsms', headers=headers,
json=payload)
r = response.json()
print(r)
print("SMS Sent")
print(amount)
if amount == 1:
profile = 'one_hour'
elif amount == 50:
profile = 'one_day'
elif amount == 300:
profile = 'one_week'
elif amount == 1000:
profile = 'one_month'
else:
print('Incorrect Amount')
print(profile)
connection = routeros_api.RouterOsApiPool(
host=os.getenv("ip"),
username=os.getenv("usernamee"),
password=os.getenv("password"),
port=8728,
use_ssl=False,
ssl_verify=False,
ssl_verify_hostname=True,
ssl_context=None,
plaintext_login=True
)
api = connection.get_api()
user = api.get_resource('/ip/hotspot/user')
user.add(name=password, server="hotspot1", password='1234', profile=profile)
print("User " + password + " created.")
else:
print("Payment was not successful")
context = {
"resultcode": result_code,
"resultdesc": result_desc
}
if context['resultcode'] == 0:
return JsonResponse(dict(context))
'''
I keep getting this error:
2023-01-03T13:37:23.849348+00:00 app[web.1]: Internal Server Error: /confirm
2023-01-03T13:37:23.849349+00:00 app[web.1]: Traceback (most recent call last):
2023-01-03T13:37:23.849350+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/core/handlers/exception.py", line 55, in inner
2023-01-03T13:37:23.849350+00:00 app[web.1]: response = get_response(request)
2023-01-03T13:37:23.849353+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/core/handlers/base.py", line 197, in _get_response
2023-01-03T13:37:23.849353+00:00 app[web.1]: response = wrapped_callback(request, *callback_args, **callback_kwargs)
2023-01-03T13:37:23.849354+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
2023-01-03T13:37:23.849354+00:00 app[web.1]: return view_func(*args, **kwargs)
2023-01-03T13:37:23.849354+00:00 app[web.1]: File "/app/Splash/views.py", line 184, in confirmation
2023-01-03T13:37:23.849355+00:00 app[web.1]: api = connection.get_api()
2023-01-03T13:37:23.849355+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/routeros_api/api.py", line 45, in get_api
2023-01-03T13:37:23.849355+00:00 app[web.1]: self.socket = api_socket.get_socket(self.host, self.port,
2023-01-03T13:37:23.849355+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.10/site-packages/routeros_api/api_socket.py", line 16, in get_socket
2023-01-03T13:37:23.849356+00:00 app[web.1]: api_socket.connect((hostname, port))
2023-01-03T13:37:23.849356+00:00 app[web.1]: TypeError: str, bytes or bytearray expected, not NoneType
2023-01-03T13:37:23.849807+00:00 app[web.1]: 10.1.14.110 - - [03/Jan/2023:13:37:23 +0000] "POST /confirm HTTP/1.1" 500 75776 "-" "Apache-HttpClient/4.5.5 (Java/1.8.0_251)"
2023-01-03T13:37:23.849768+00:00 heroku[router]: at=info method=POST path="/confirm" host=airrumi.herokuapp.com request_id=2cb1aad4-7a83-43ce-a942-005d2cf64be9 fwd="10.184.18.139,196.201.214.200" dyno=web.1 connect=0ms service=454ms status=500 bytes=76092 protocol=https
I have no idea where am messing up, because my test page is connecting perfectly and creating the user in mikrotik. Below is my test.py
'''
# generate usernames
import secrets
import os
import routeros_api
from dotenv import load_dotenv
load_dotenv()
password_length = 5
password = secrets.token_urlsafe(password_length)
print(password)
amount = 1
if amount == 1:
profile = 'one_hour'
elif amount == 50:
profile = 'one_day'
elif amount == 300:
profile = 'one_week'
elif amount == 1000:
profile = 'one_month'
else:
print('Incorrect Amount')
print(profile)
connection = routeros_api.RouterOsApiPool(
host=os.getenv("ip"),
username=os.getenv("usernamee"),
password=os.getenv("password"),
port=8728,
use_ssl=False,
ssl_verify=False,
ssl_verify_hostname=True,
ssl_context=None,
plaintext_login=True
)
api = connection.get_api()
user = api.get_resource('/ip/hotspot/user')
user.add(name=password, server="hotspot1", password='1234', profile=profile)
print("User " + password + " Created.")
'''

I am trying to get the output in below nested dictionary format for the given input

I am trying to get the output in the form of nested dictionary. I am practicing this for python automation for Network simulator. I tried but not able to achieve. Kindly someone help me on this.
Interface IP-Address OK? Method Status Protocol
Vlan1 unassigned YES NVRAM up up
Vlan30 30.1.1.2 YES NVRAM up up
Vlan306 192.168.25.3 YES NVRAM up down
GigabitEthernet0/0 11.19.17.19 YES NVRAM up up
Te1/0/3 unassigned YES unset up up
Te1/0/21 unassigned YES unset up up
Te1/0/35 unassigned YES unset up up
output should be in below format.
{'interface': {
'vlan1': {
'name': 'vlan1',
'ip': 'unassigned',
'ok_status': 'YES',
'method': 'NVRAM',
'status': 'up',
'protocol': 'up',
},
'vlan30': {
'name': 'vlan30',
'ip': '30.1.1.2',
'ok_status': 'YES',
'method': 'NVRAM',
'status': 'up',
'protocol': 'up',
}
}
}
If you place your input data in a text file (example D:/test.txt), following code will format it according to your specification:
comma_sep = ""
with open("D:/test.txt") as f:
for line in f:
parts = [part for part in line.split(' ') if part != '']
comma_sep += ','.join(parts)
lines = [item for item in comma_sep.split('\n')]
output_dict = {"interface": {}}
for index, line in enumerate(lines):
if index == 0:
continue
# Compose each interface
interface = {}
for item_index, item in enumerate(line.split(',')):
if item_index == 0:
item = item.lower()
interface = {item: {}}
interface[item].update({'name': item})
main_key = item
elif item_index == 1:
interface[main_key].update({'ip': item})
elif item_index == 2:
interface[main_key].update({'ok_status': item})
elif item_index == 3:
interface[main_key].update({'method': item})
elif item_index == 4:
interface[main_key].update({'status': item})
elif item_index == 5:
interface[main_key].update({'protocol': item})
output_dict["interface"].update(interface)
print(output_dict)

Flask - TypeError: Object of type cycle is not JSON serializable

I'm trying to return a json response from server to client after client makes a post via AJAX.
Like this, it works:
#app.route("/test", methods=["GET", "POST"])
#login_required
def test():
if request.is_xhr:
try:
json_response = {
"result": "success"
}
except Exception as e:
err = _except(line=sys.exc_info()[-1].tb_lineno, error=e, function_name=what_func(), script_name=__file__)
json_response = {"result": "failure", "msg": err}
return jsonify(json_response)
else:
return redirect("/not-found")
return ''
If I do like this, it doesn't works:
#app.route("/test", methods=["GET", "POST"])
#login_required
def test():
if request.is_xhr:
try:
_dict = {
"key1": "value1",
"key2": "value2"
}
if not "my_dict" in session:
session["my_dict"] = [_dict]
else:
session["my_dict"] = session["my_dict"] + [_dict]
session.modified = True
json_response = {
"result": "success"
}
except Exception as e:
err = _except(line=sys.exc_info()[-1].tb_lineno, error=e, function_name=what_func(), script_name=__file__)
json_response = {"result": "failure", "msg": err}
return jsonify(json_response)
else:
return redirect("/not-found")
return ''
I get the following error and client doesn't receives json response:
File "~/myprojectenv/lib/python3.8/site-packages/flask/json/__init__.py", line 100, in default
return _json.JSONEncoder.default(self, o)
File "/usr/lib/python3.8/json/encoder.py", line 179, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type cycle is not JSON serializable
[2020-09-26 19:01:14,091] ERROR in app: Request finalizing failed with an error while handling an error
If I remove that stretch:
if not "my_dict" in session:
session["my_dict"] = [_dict]
else:
session["my_dict"] = session["my_dict"] + [_dict]
session.modified = True
It works again. Client receives json response.
I figured out the trouble.
Check out the following function:
#app.route("/test", methods=["GET", "POST"])
def test():
if request.is_xhr:
try:
licycle = cycle(files)
nextelem = next(licycle)
_dict = {
"sid": session["_id"],
"licycle": licycle,
"nextelem": nextelem,
"licycle2": licycle2,
"nextelem2": nextelem2,
"mon_id": _id,
"sch_id": 0
}
if not "all_submons" in session:
session["all_submons"] = [_dict]
else:
session["all_submons"] = session["all_submons"] + [_dict]
session.modified = True
all_submons.setdefault("sub_mons", []).append(_dict)
json_response = {"result": "success"}
except Exception as e:
err = _except(line=sys.exc_info()[-1].tb_lineno, error=e, function_name=what_func(), script_name=__file__)
json_response = {"result": "failure", "err": err}
finally:
try:
print("exiting...")
except Exception as e:
pass
return jsonify(json_response)
else:
return redirect("/not-found")
return ""
The reason is that type of licycle variable is <class 'itertools.cycle'> and session "probably doesn't accepts" that type of variable like a dictionary accepts as you can see in my all_submons dict variable.
The point is:
The execution DOESN'T FALLS in exception. And _dict is stored on session as you can see to next.
print(f"Session before: {session}")
print(f"all_submons before: {all_submons}")
if not "all_submons" in session:
session["all_submons"] = [_dict]
else:
session["all_submons"] = session["all_submons"] + [_dict]
session.modified = True
all_submons.setdefault("sub_mons", []).append(_dict)
print(f"Session after: {session}")
print(f"all_submons after: {all_submons}\n")
You can check the output:
Session before: <SecureCookieSession {'_fresh': True, '_id': 'e253a950...', 'all_submons': [], 'user_id': '1'}>
all_submons before: {}
Session after: <SecureCookieSession {'_fresh': True, '_id': 'e253a...', 'all_submons': [{'sid': 'e253a95...', 'licycle': <itertools.cycle object at 0x7fc989237280>, 'nextelem': ('1a4add0f275c7275.jpg',), 'licycle2': None, 'nextelem2': None, 'mon_id': 1, 'sch_id': 0}], 'user_id': '1'}>
all_submons after: {'sub_mons': [{'sid': 'e253a...', 'licycle': <itertools.cycle object at 0x7fd6f1a17b80>, 'nextelem': ('1a4add0f275c7275.jpg',), 'licycle2': None, 'nextelem2': None, 'mon_id': 1, 'sch_id': 0}]}
I'm not sure about session "probably doesn't accepts" that type of variable - <class 'itertools.cycle'>
But I created other dictionary with others variables, without type of itertools.cycle and it worked.

Not able to upload file using slack api files.upload

This question may seem duplicate but I have tried a lot but did not get success.
I am trying to upload html file using https://slack.com/api/files.upload API but I am getting below error always.
response
{'ok': False, 'error': 'no_file_data'}
I went through documentation [a link]https://api.slack.com/methods/files.upload and tried with different options but still i am getting the same response {'ok': False, 'error': 'no_file_data'}
Also i have seen many similar questions in stack overflow but none of them resolved the problem.
[a link]no_file_data error when using Slack API upload
[a link]How to upload files to slack using file.upload and requests
Below is my code.
import requests
def post_reports_to_slack(html_report):
"""
"""
url = "https://slack.com/api/files.upload"
# my_file = {html_report, open(html_report, 'rb'), 'html'}
data = {
"token": bot_user_token,
"channels": channel_name,
"file": html_report,
"filetype": "html"
}
# data = "token=" + bot_user_token + \
# "&channels=" + channel_name +\
# "&file=" + html_report + "&filetype=" + "html"
response = requests.post(
url=url, data=data,
headers={"Content-Type": "application/x-www-form-urlencoded"})
print("response", response)
print(response.json())
if response.status_code == 200:
print("successfully completed post_reports_to_slack "
"and status code %s" % response.status_code)
else:
print("Failed to post report on slack channel "
"and status code %s" % response.status_code)
Please help to resolve the issue.
I was needed to add "content" argument and "filename" argument instead of "file" argument in files.upload API payload, Now file uploading to slack channel is working fine.
import requests
def post_reports_to_slack(html_report):
url = "https://slack.com/api/files.upload"
with open(html_report) as fh:
html_data = fh.read()
data = {
"token": bot_user_token,
"channels": "#channel_name",
"content": html_data,
"filename": "report.html",
"filetype": "html",
}
response = requests.post(
url=url, data=data,
headers={"Content-Type": "application/x-www-form-urlencoded"})
if response.status_code == 200:
print("successfully completed post_reports_to_slack "
"and status code %s" % response.status_code)
else:
print("Failed to post report on slack channel "
"and status code %s" % response.status_code)

Flask TypeError: argument of type 'NoneType' is not iterable

I am not sure why I am getting this TypeError:
File "C:/Users/PycharmProjects/REST/app.py", line 30, in
valid_book_object
if ("isbn" in book and "name" in book and "price" in book):
TypeError: argument of type 'NoneType' is not iterable
127.0.0.1 - - [12/Nov/2018 14:22:29] "POST /books HTTP/1.1" 500 -
Code:
from flask import Flask, jsonify, request
from test import *
app=Flask(__name__)
books=[
{'name': 'M',
'price': 6.75,
'isbn':123
},
{'name': 'G',
'price': 7.75,
'isbn':456
},
]
#GET /store
#app.route('/books') #first endpoint
def get_books():
return jsonify({'books': books})
# POST /books
#{'name': 'F',
#'price': 7.00,
#'isbn': 789
#},
def valid_book_object(book):
if ("isbn" in book and "name" in book and "price" in book):
return True
print("true")
else:
return False
print("false")
#app.route('/books', methods=['POST'])
def add_book():
#return jsonify(request.get_json())
request_data=request.get_json()
if(valid_book_object(request_data)):
books.insert(0, request_data)
return "True"
else:
return "False"
#GET /books/456
#app.route('/books/<int:isbn>') #second endpoint
def get_book_by_isbn(isbn):
return_value={}
for book in books:
if book["isbn"]==isbn:
return_value={
'name': book["name"],
'price':book["price"]
}
return jsonify(return_value)
app.run(port=5000)
You are not sending JSON data to /books route using POST method.
I tried your solution with postman and it worked. Also, if you want to use some route for GET and POST, don't split them. Use methods=['GET', 'POST']. Here is your code reformatted with PEP 8 standard:
from flask import Flask, jsonify, request
app = Flask(__name__)
books = [
{'name': 'M',
'price': 6.75,
'isbn': 123
},
{'name': 'G',
'price': 7.75,
'isbn': 456
}
]
# POST /books
# {
# "name": "F",
# "price": 7.00,
# "isbn": 789
# }
def valid_book_object(book):
if "isbn" in book and "name" in book and "price" in book:
return True
else:
return False
#app.route('/books', methods=['GET', 'POST'])
def add_book():
# If request is GET, just return JSON data of books.
if request.method == 'GET':
return jsonify({'books': books})
else:
# This is part if it is POST request
request_data = request.get_json()
if valid_book_object(request_data):
books.insert(0, request_data)
return "True"
else:
return "False"
# GET /books/456
#app.route('/books/<int:isbn>') # second endpoint
def get_book_by_isbn(isbn):
return_value = {}
for book in books:
if book["isbn"] == isbn:
return_value = {
'name': book["name"],
'price': book["price"]
}
return jsonify(return_value)
return 'No book with {} isbn value'.format(isbn)
if __name__ == '__main__':
app.run(port=5000)
And here is the output from postman (you can see True at the bottom, that is what you return if POST was successful):
If you use postman, be sure to select application/json content-type.
If you are using JQuery ajax method, please read this answer. But anyway, here is using JQuery (tested):
data = JSON.stringify({
name: "F",
price: 7.00,
isbn: 789
});
$.ajax({
url: '/books',
type: "POST",
data: data,
contentType: "application/json",
dataType: "json",
success: function(){
console.log('Post executed successfully');
}
})

Resources