I'm building an API using Flask-Restful following the steps from the book "Python API Fundamentals". The problem I'm having is that I'm unable to get a token. My code which is an edited replica of that which is in the book is throwing the following error whenever I test it on Postman
127.0.0.1 - - [01/Aug/2022 13:26:49] "POST /token HTTP/1.1" 500 -
Traceback (most recent call last):
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask\app.py", line 2091, in __call__
return self.wsgi_app(environ, start_response)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask\app.py", line 2076, in wsgi_app
response = self.handle_exception(e)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask_restful\__init__.py", line 271, in error_router
return original_handler(e)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask\app.py", line 2073, in wsgi_app
response = self.full_dispatch_request()
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask\app.py", line 1519, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask_restful\__init__.py", line 271, in error_router
return original_handler(e)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask\app.py", line 1517, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask\app.py", line 1503, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask_restful\__init__.py", line 467, in wrapper
resp = resource(*args, **kwargs)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask\views.py", line 84, in view
return current_app.ensure_sync(self.dispatch_request)(*args, **kwargs)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask_restful\__init__.py", line 582, in dispatch_request
resp = meth(*args, **kwargs)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\smilecook\resources\token.py", line 24, in post
access_token = create_access_token(identity=user.id)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask_jwt_extended\utils.py", line 157, in create_access_token
return jwt_manager._create_access_token(identity, fresh, expires_delta, user_claims)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask_jwt_extended\jwt_manager.py", line 469, in _create_access_token
access_token = encode_access_token(
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask_jwt_extended\tokens.py", line 76, in encode_access_token
return _encode_jwt(token_data, expires_delta, secret, algorithm,
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask_jwt_extended\tokens.py", line 31, in _encode_jwt
json_encoder=json_encoder).decode('utf-8')
AttributeError: 'str' object has no attribute 'decode'
Here's my requirements.txt
Flask==2.1.3
Flask-RESTful==0.3.9
httpie==3.2.1
Flask-SQLAlchemy==2.5.1
Flask-Migrate==3.1.0
psycopg2-binary==2.9.3
passlib==1.7.4
Flask-JWT-Extended==3.20.0
Werkzeug==2.0
and the codes that I think is causing the issue
from http import HTTPStatus
from flask import request
from flask_restful import Resource
from flask_jwt_extended import create_access_token
from utils import check_password
from models.user import User
class TokenResource(Resource):
def post(self):
json_data = request.get_json()
email = json_data.get('email')
password = json_data.get('password')
user = User.get_by_email(email=email)
if not user or not check_password(password, user.password):
return {'message': 'username or password is incorrect'}, HTTPStatus.UNAUTHORIZED
access_token = create_access_token(identity=user.id)
return {'access_token': access_token}, HTTPStatus.OK
from flask import request
from flask_restful import Resource
from flask_jwt_extended import jwt_optional, get_jwt_identity
from http import HTTPStatus
from utils import hash_password
from models.user import User
class UserListResource(Resource):
def post(self):
json_data = request.get_json()
username = json_data.get('username')
email = json_data.get('email')
non_hash_password = json_data.get('password')
if User.get_by_username(username):
return {'message': 'username already used'}, HTTPStatus.BAD_REQUEST
if User.get_by_email(email):
return {'message': 'email already used'}, HTTPStatus.BAD_REQUEST
password = hash_password(non_hash_password)
user = User(
username=username,
email=email,
password=password
)
user.save()
data = {
'id': user.id,
'username': user.username,
'email': user.email
}
return data, HTTPStatus.CREATED
class UserResource(Resource):
@jwt_optional
def get(self, username):
user = User.get_by_username(username=username)
if user is None:
return {'message': 'user not found'}, HTTPStatus.NOT_FOUND
current_user = get_jwt_identity()
if current_user == user.id:
data = {
'id': user.id,
'username': user.username,
'email': user.email,
}
else:
data = {
'id': user.id,
'username': user.username,
}
return data, HTTPStatus.OK
from flask import Flask
from flask_migrate import Migrate
from flask_restful import Api
from config import Config
from extensions import db, jwt
from resources.user import UserListResource, UserResource
from resources.token import TokenResource
from resources.recipe import RecipeListResource, RecipeResource, RecipePublishResource
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
register_extensions(app)
register_resources(app)
return app
def register_extensions(app):
db.init_app(app)
migrate = Migrate(app, db)
jwt.init_app(app)
def register_resources(app):
api = Api(app)
api.add_resource(UserListResource, '/users')
api.add_resource(UserResource, '/users/<string:username>')
api.add_resource(TokenResource, '/token')
api.add_resource(RecipeListResource, '/recipes')
api.add_resource(RecipeResource, '/recipes/<int:recipe_id>')
api.add_resource(RecipePublishResource, '/recipes/<int:recipe_id>/publish')
if __name__ == '__main__':
app = create_app()
app.run()
the results of pip freeze
alembic==1.8.1
aniso8601==9.0.1
certifi==2022.6.15
charset-normalizer==2.1.0
click==8.1.3
colorama==0.4.5
commonmark==0.9.1
defusedxml==0.7.1
Flask==2.1.3
Flask-JWT-Extended==3.20.0
Flask-Migrate==3.1.0
Flask-RESTful==0.3.9
Flask-SQLAlchemy==2.5.1
greenlet==1.1.2
httpie==3.2.1
idna==3.3
itsdangerous==2.1.2
Jinja2==3.1.2
Mako==1.2.1
MarkupSafe==2.1.1
multidict==6.0.2
passlib==1.7.4
psycopg2-binary==2.9.3
Pygments==2.12.0
PyJWT==2.4.0
PySocks==1.7.1
pytz==2022.1
requests==2.28.1
requests-toolbelt==0.9.1
rich==12.5.1
six==1.16.0
SQLAlchemy==1.4.39
urllib3==1.26.11
Werkzeug==2.0.0
This is happening because you have a newer version of PyJWT with an older version of flask jwt extended that are incompatible. Upgrading flask jwt extended to at least 3.25.0 should fix the issue. If you upgrade to the 4.x.x version of flask jwt extended, make sure to read over the breaking changes here: https://flask-jwt-extended.readthedocs.io/en/stable/v4_upgrade_guide/