I have an API with 12 endpoints which was working fine, then the endpoints started failing and now all return 404 status code. Here are some of my files
My run.py
import os
from app import create_app
config = os.getenv('APP_SETTINGS')
app = create_app(config)
if __name__ == "__main__":
app.run(debug=True)
I register my endpoints in app.py like so
from flask import Flask
from .api.v2.views.userview import auth
def create_app(config):
'''Creates all Flask configurations and returns app.
Expects config name'''
app = Flask(__name__, instance_relative_config=True)
app.config['JSON_SORT_KEYS'] = False
app.config.from_object(app_config[config])
app.config.from_pyfile('config.py', silent=True)
app.url_map.strict_slashes = False
app.register_blueprint(auth)
return app
And finally my endpoint user.py
from flask import request, jsonify
from flask import Blueprint
from ..models.usermodel import UserModel
usr_obj = UserModel()
auth = Blueprint('auth', __name__, '/api/v2')
#auth.route('/auth/signup', methods=['POST'])
def user_signup():
fullname = request.json['fullname']
username = request.json['username']
email = request.json['email']
password = request.json['password']
data = usr_obj.inituser(fullname, username, email, password)
return jsonify(data), 201
When I try to run this endpoint or any other in Version 1 (/api/v1) I get a Not Found error. I have also tried THIS SOLUTION with no success.
I made a silly mistake the Blueprint declaration ought to be
auth = Blueprint('auth', __name__, url_prefix='/api/v2')
Related
I have a file that registers a blueprint and runs the app api/v1/app.py:
#!/usr/bin/python3
"""returns api status code"""
from api.v1.views import app_views
from flask import Flask
from models import storage
from os import getenv
app = Flask(__name__)
app.register_blueprint(app_views, url_prefix="/api/v1")
#app.teardown_appcontext
def close_session(session):
"""close a session"""
storage.close()
if __name__ == '__main__':
host = getenv('HBNB_API_HOST') if getenv('HBNB_API_HOST') else '0.0.0.0'
port = getenv('HBNB_API_PORT') if getenv('HBNB_API_PORT') else 5000
app.run(port=port, host=host, threaded=True)
a file that creates a blueprint api/v1/views/__init__.py:
#!/usr/bin/python3
"""create a blueprint"""
from flask import Blueprint
app_views = Blueprint("app_views", __name__)
and a file that creates a blueprint route api/v1/views/index.py:
#!/usr/bin/python3
"""returns api status code"""
from flask import jsonify
from api.v1.views import app_views
#app_views.route('/status')
def status():
"""return ok response"""
return jsonify({"status": "OK"})
i registered the blueprint with the url_prefix /api/v1, but when i try to access http://0.0.0.0:5000/api/v1/status, I get 404.
So what am I missing here ?
You defined the prefix correctly. The problem here is because the file api/v1/views/index.py was never interpreted. If you add import api/v1/views/index.py in the file api/v1/app.py it will probably work
I am using flask_jwt_extended for jwt authentication in my flask web application. After the user enters email and password, I make a token using create_access_token and then redirect to another link which can only be accessed with #jwt_required.
app.py file. Notice if the way of importing jwt from user.py file like this is correct.
from flask import Flask
from flask_restful import Api
from resources.organization import OrganizationResourceList, OrganizationResource
from resources.user import LoginResource, user_list, User, jwt
app = Flask(__name__)
app.config['SECRET_KEY'] = '_rV;He_7Bz8TVvA'
app.config['JWT_TOKEN_LOCATION'] = ['headers']
jwt.init_app(app)
api = Api(app)
user_list.append(User(name="Admin User", email="admin#test.com", password="12345", photo="", user_type="host"))
# Authorization
api.add_resource(LoginResource, '/login')
# Organization
api.add_resource(OrganizationResourceList, '/organizations')
if __name__ == '__main__':
app.run(port=5000, debug=True)
user.py file containing LoginResource This is where I am creating token.
from flask import request, Response, render_template, redirect
from flask_restful import Resource
from models.user import User, user_list
from passlib.hash import sha256_crypt
from flask_jwt_extended import create_access_token, create_refresh_token, JWTManager
jwt = JWTManager()
class LoginResource(Resource):
def post(self):
req = request.form
email = req.get("email")
user = [x for x in user_list if x.email == email]
if user:
user = user[0]
password = sha256_crypt.verify(req.get("password"), user.password)
if user and password:
access_token = create_access_token(identity=user.id)
refresh_token = create_refresh_token(user.id)
redir = redirect('/organizations')
redir.headers['Authorization'] = "Bearer %s" % access_token
return redir
return redirect("/login")
Interestingly, when I debug the app, I see the headers of redirect as shown.
organization.py file containing OrganizationResourceList class
from flask import request, Response, render_template
from flask_restful import Resource
from models.organization import Organization, organization_list
from flask_jwt_extended import jwt_required, get_jwt_identity
class OrganizationResourceList(Resource):
#jwt_required()
def get(self):
current_user = get_jwt_identity()
sample_org = Organization(
name='Bin Yousef',
description='My main working company in Qatar',
photo=''
)
data = []
for organization in organization_list:
data.append(organization.data)
return Response(response=render_template('organization/index.html', organizations=data))
After hours of searching, I am still not able to get rid of the error :( Please help
As I'm testing the URL endpoint for a resource, I'm getting a 404 Not Found error. I don't understand why it cannot be found as the resource is added to the api instance and the blueprint is added to the flask app.
tests.py
def test_todo_collection_resource(self):
with app.test_client() as client:
http_response = client.get("/todos/")
json_data = http_response.get_json()
self.assertEqual(http_response.status_code, 200) <<<---FAILS
self.assertTrue(http_response.is_json)
self.assertTrue(all(
(instance['name'] in self.todo_resources.values()
for instance in json_data)
))
todos.py
from flask import Blueprint, jsonify
from flask_restful import Api, Resource, fields, marshal
from models import Todo
todo_api = Blueprint("resources.todos", __name__)
api = Api(todo_api)
todos_fields = {
'name': fields.String
}
class TodoList(Resource):
pass
api.add_resource(
TodoList,
''
'todos'
)
app.py
from flask import Flask, g, jsonify, render_template
from config import HOST, PORT, DEBUG
from peewee import *
import models
from resources.todos import todo_api
app = Flask(__name__)
app.register_blueprint(todo_api, url_prefix="/todos/")
models.DATABASE.init('todo_api.db')
models.initialize(models.User, models.Todo)
#app.route('/')
def my_todos():
return render_template('index.html')
if __name__ == '__main__':
app.run(host=HOST, port=PORT, debug=DEBUG)
You have not defined any methods in your TodoList class that is why there is a 404 error as it can't find any HTTP methods defined on that endpoint. The Flask-RESTful documentation specifies defining HTTP methods in your Resource class e.g.
class TodoList(Resource):
def get(self):
return TODOS
I have following code for flask restful basic authentication
from flask import Flask
from flask_restful import Resource, Api
from flask_httpauth import HTTPBasicAuth
app = Flask(__name__)
api = Api(app, prefix="/api/v1")
auth = HTTPBasicAuth()
USER_DATA = {
"admin": "SuperSecretPwd"
}
#auth.verify_password
def verify(username, password):
if not (username and password):
return False
return USER_DATA.get(username) == password
class PrivateResource(Resource):
#auth.login_required
def get(self):
return {"meaning_of_life": 42}
api.add_resource(PrivateResource, '/private')
if __name__ == '__main__':
app.run(debug=True)
But if My resource class PrivateResource is in separate file, how can I use #auth.login_required. I don't want to import app.py in every resource file.
You can structure your project like this:
In app.py
from flask import Flask
from flask_restful import Api
from my_resource import PrivateResource
app = Flask(__name__)
api = Api(app, prefix="/api/v1")
# add all resources here
api.add_resource(PrivateResource, '/private')
if __name__ == '__main__':
app.run(debug=True)
Handle authentication in authentication.py
from flask_httpauth import HTTPBasicAuth
auth = HTTPBasicAuth()
USER_DATA = {
"admin": "SuperSecretPwd"
}
#auth.verify_password
def verify(username, password):
if not (username and password):
return False
return USER_DATA.get(username) == password
and your resources in separate files like my_resource.py
from flask_restful import Resource
from authentication import auth
class PrivateResource(Resource):
#auth.login_required
def get(self):
return {"meaning_of_life": 42}
If every resource requires authentication, you can add the decorator automatically to every resource, see the decorators parameters in the third line
from flask_httpauth import HTTPBasicAuth
from werkzeug.security import check_password_hash
from flask import Flask
from flask_restful import Api
application: Flask = Flask(__name__)
auth = HTTPBasicAuth()
api: Api = Api(app=application, serve_challenge_on_401=True, decorators=[auth.login_required])
PW_HASH: str = ..
#auth.verify_password
def verify_password(username: str, password: str):
if username == 'foo' and check_password_hash(PW_HASH, password):
return username
You always need to import the auth instance from app.py, because you are creating a new instance of Auth by calling auth = HTTPBasicAuth(). And you are registering your validation method on that specific instance, see #auth.verify_password. So only this instance is able to handle your authentication. So you need to import it every time you want to use it. But you don't need to import the whole module. Its enough to import only that instance: from app.py import auth
I'm calling db dev to insert data into its collection. But it's creating a new collection and inserting data into admin db.
from app import app
from flask import Flask
from flask import jsonify
from flask import request
from flask_pymongo import PyMongo
#app.route('/')
#app.route('/index')
def index():
return "Hello, World!"
app.config['MONGO_DBNAME'] = 'dev'
app.config['MONGO_AUTH_SOURCE'] = 'admin'
app.config['MONGO_URI'] = 'mongodb://<user>:<password>#<url>:27017/admin'
mongo = PyMongo(app)
#app.route('/mongo', methods=['GET'])
def get_all_docs():
doc = mongo.db.abcd.insert({'abcd':'abcd'})
return "Inserted"
if __name__ == '__main__':
app.run(debug=True)
Am I missing something here?
PS: I tried replacing admin with dev. It gave pymongo.errors.OperationFailure: Authentication failed. I guess thats because the authentication data is in admin db.
app.config['MONGO_URI'] = 'mongodb://<user>:<password>#<url>:27017'
This, also, didn't work.
Replacing admin with dev in MONGO_URI causes Authentication Error.
Adding authSource will authenticate with admin db.
To do this, replace admin with dev?authSource=admin
from app import app
from flask import Flask
from flask import jsonify
from flask import request
from flask_pymongo import PyMongo
#app.route('/')
#app.route('/index')
def index():
return "Hello, World!"
app.config['MONGO_URI'] = 'mongodb://<user>:<password>#<url>:27017/dev?authSource=admin'
mongo = PyMongo(app)
#app.route('/mongo', methods=['GET'])
def get_all_docs():
doc = mongo.db.abcd.insert({'abcd':'abcd'})
return "Inserted"
if __name__ == '__main__':
app.run(debug=True)