質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Flask

FlaskはPython用のマイクロフレームワークであり、Werkzeug・Jinja 2・good intentionsをベースにしています。

OAuth

OAuth(Open Authorization)は、APIを通して保護されたリソース(サードパーティのアプリケーション)へアクセスする為のオープンプロトコルです。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Twitter

Twitterは、140文字以内の「ツイート」と呼ばれる短文を投稿できるサービスです。Twitter上のほぼ全ての機能に対応するAPIが存在し、その関連サービスが多く公開されています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

1回答

2280閲覧

FlaskでOAuth認証時に発生するUnicodeDecodeErrorを解決したい

masty0000

総合スコア10

Flask

FlaskはPython用のマイクロフレームワークであり、Werkzeug・Jinja 2・good intentionsをベースにしています。

OAuth

OAuth(Open Authorization)は、APIを通して保護されたリソース(サードパーティのアプリケーション)へアクセスする為のオープンプロトコルです。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Twitter

Twitterは、140文字以内の「ツイート」と呼ばれる短文を投稿できるサービスです。Twitter上のほぼ全ての機能に対応するAPIが存在し、その関連サービスが多く公開されています。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2018/12/30 07:53

編集2022/01/12 10:55

前提・実現したいこと

PythonのWEBフレームワークFlaskを用いて、とある書籍を参考に、質問箱アプリを開発しています。
ログイン機能として、Twitterのアカウントを用いたOAuth認証を実装しようとしています。

リクエスト・トークンを取得し、その後リクエスト・トークンを元にアクセス・トークンを取得する段階で下記のようなエラーが発生しました。
(WEB画面上で言うと、ユーザーに対し、アプリがユーザー情報を利用することを許可する画面から、許可後、コールバックURLへリダイレクトするところでエラーが発生)

発生している問題・エラーメッセージ

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 1490: ordinal not in range(128)

表示されたメッセージの全文としては、以下の通りです。

(flaskApp3) mas-mac-pc% flask run * Serving Flask app "app.py" (lazy loading) * Environment: development * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 916-800-986 127.0.0.1 - - [30/Dec/2018 16:11:21] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [30/Dec/2018 16:11:25] "GET /oauth/twitter HTTP/1.1" 302 - 127.0.0.1 - - [30/Dec/2018 16:11:28] "GET /oauth/twitter/callback?provider=twitter&oauth_token={非公開}&oauth_verifier={非公開} HTTP/1.1" 500 - Traceback (most recent call last): File "/Users/Mas/.local/share/virtualenvs/flaskApp3-s8OM8UNK/lib/python3.7/site-packages/flask/app.py", line 2309, in __call__ return self.wsgi_app(environ, start_response) File "/Users/Mas/.local/share/virtualenvs/flaskApp3-s8OM8UNK/lib/python3.7/site-packages/flask/app.py", line 2295, in wsgi_app response = self.handle_exception(e) File "/Users/Mas/.local/share/virtualenvs/flaskApp3-s8OM8UNK/lib/python3.7/site-packages/flask/app.py", line 1741, in handle_exception reraise(exc_type, exc_value, tb) File "/Users/Mas/.local/share/virtualenvs/flaskApp3-s8OM8UNK/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise raise value File "/Users/Mas/.local/share/virtualenvs/flaskApp3-s8OM8UNK/lib/python3.7/site-packages/flask/app.py", line 2292, in wsgi_app response = self.full_dispatch_request() File "/Users/Mas/.local/share/virtualenvs/flaskApp3-s8OM8UNK/lib/python3.7/site-packages/flask/app.py", line 1815, in full_dispatch_request rv = self.handle_user_exception(e) File "/Users/Mas/.local/share/virtualenvs/flaskApp3-s8OM8UNK/lib/python3.7/site-packages/flask/app.py", line 1718, in handle_user_exception reraise(exc_type, exc_value, tb) File "/Users/Mas/.local/share/virtualenvs/flaskApp3-s8OM8UNK/lib/python3.7/site-packages/flask/_compat.py", line 35, in reraise raise value File "/Users/Mas/.local/share/virtualenvs/flaskApp3-s8OM8UNK/lib/python3.7/site-packages/flask/app.py", line 1813, in full_dispatch_request rv = self.dispatch_request() File "/Users/Mas/.local/share/virtualenvs/flaskApp3-s8OM8UNK/lib/python3.7/site-packages/flask/app.py", line 1799, in dispatch_request return self.view_functions[rule.endpoint](**req.view_args) File "/Users/Mas/Python/flask/flaskApp3/app.py", line 123, in oauth_callback data={'oauth_verifier': request.args['oauth_verifier']} # ここがエラーの原因 File "/Users/Mas/.local/share/virtualenvs/flaskApp3-s8OM8UNK/lib/python3.7/site-packages/rauth/service.py", line 359, in get_auth_session **kwargs) File "/Users/Mas/.local/share/virtualenvs/flaskApp3-s8OM8UNK/lib/python3.7/site-packages/rauth/service.py", line 332, in get_access_token process_token_request(r, decoder, key_token, key_token_secret) File "/Users/Mas/.local/share/virtualenvs/flaskApp3-s8OM8UNK/lib/python3.7/site-packages/rauth/service.py", line 20, in process_token_request data = decoder(r.content) File "/Users/Mas/.local/share/virtualenvs/flaskApp3-s8OM8UNK/lib/python3.7/site-packages/rauth/utils.py", line 26, in parse_utf8_qsl for k, v in dict(parse_qsl(s)).items(): # pragma: no cover File "/anaconda3/lib/python3.7/urllib/parse.py", line 683, in parse_qsl qs, _coerce_result = _coerce_args(qs) File "/anaconda3/lib/python3.7/urllib/parse.py", line 123, in _coerce_args return _decode_args(args) + (_encode_result,) File "/anaconda3/lib/python3.7/urllib/parse.py", line 107, in _decode_args return tuple(x.decode(encoding, errors) if x else '' for x in args) File "/anaconda3/lib/python3.7/urllib/parse.py", line 107, in <genexpr> return tuple(x.decode(encoding, errors) if x else '' for x in args) UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 1490: ordinal not in range(128)

該当のソースコード

app.py

1from flask import Flask, redirect, url_for, session, request, render_template 2from flask_sqlalchemy import SQLAlchemy 3from flask_migrate import Migrate 4 5from flask_login import UserMixin, LoginManager, login_user, logout_user, current_user, login_required 6 7from datetime import datetime 8from rauth import OAuth1Service 9 10app = Flask(__name__) 11 12app.secret_key = 'xxxxxxxxxx' 13app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False 14app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql://localhost/testdb" 15db = SQLAlchemy(app) 16login_manager = LoginManager(app) 17login_manager.login_view = 'index' 18migrate = Migrate(app, db) 19 20service = OAuth1Service( 21 name='twitter', 22 consumer_key='XXXXXXXXXX', 23 consumer_secret='XXXXXXXXXX', 24 request_token_url='https://api.twitter.com/oauth/request_token', 25 authorize_url='https://api.twitter.com/oauth/authorize', 26 access_token_url='https:/api.twitter.com/oauth/access_token', 27 base_url='https://api.twitter.com/1.1' 28) 29 30# Userモデル 31class User(UserMixin, db.Model): 32 # テーブル定義 33 id = db.Column(db.Integer, primary_key=True) 34 username = db.Column(db.String(64), index=True, unique=True) 35 description = db.Column(db.String(1204), index=True, unique=True) 36 user_image_url = db.Column(db.String(1204), index=True, unique=True) 37 date_published = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) 38 twitter_id = db.Column(db.String(64), nullable=False, unique=True) 39 40 def __repr__(self): 41 42 return '<User %r>' % self.username 43 44@app.route('/') 45def index(): 46 return render_template('index.html') 47 48@app.route('/logout') 49def logout(): 50 logout_user() 51 return redirect(url_for('index')) 52 53@app.route('/oauth/twitter') 54def oauth_authorize(): 55 if not current_user.is_anonymous: 56 return redirect(url_for('index')) 57 else: 58 request_token = service.get_request_token( 59 params={'oauth_callback': url_for('oauth_callback', provider='twitter', _external=True)} 60 ) 61 62 session['request_token'] = request_token 63 64 return redirect(service.get_authorize_url(request_token[0])) 65 66@app.route('/oauth/twitter/callback') 67def oauth_callback(): 68 request_token = session.pop('request_token') 69 70 oauth_session = service.get_auth_session( 71 request_token[0], 72 request_token[1], 73 method='GET', # 自分で追加 74 data={'oauth_verifier': request.args['oauth_verifier']} # ここがエラー発生 75 ) 76 77 profile = oauth_session.get('account/verify_credentials.json').json() 78 79 twitter_id = str(profile.get('id')) 80 username = str(profile.get('name')) 81 description = str(profile.get('description')) 82 profile_image_url = str(profile.get('profile_image_url')) 83 user = db.session.query(User).filter(User.twitter_id==twitter_id).first() 84 85 if user: 86 87 user.twitter_id = twitter_id 88 user.username = username 89 else: 90 91 user = User(twitter_id=twitter_id, 92 username=username, 93 description=description, 94 user_image_url=profile_image_url) 95 db.session.add(user) 96 db.session.commit() 97 98 login_user(user, True) 99 return redirect(url_for('index')) 100 101@login_manager.user_loader 102def load_user(id): 103 return User.query.get(int(id))

試したこと

エラーメッセージからasciiでデコードしているようなので、
以下を実行してみましたが、環境のエンコーディングはutf-8でした。

>>> import locale >>> print(locale.getpreferredencoding()) UTF-8 >>> import sys >>> print(sys.getdefaultencoding()) utf-8

また、エラーメッセージからparse.pyで例外が発生しているようだったので、
parse.pyの中身を見てみたりしてみましたが、いまいちわからず・・・

補足情報(FW/ツールのバージョンなど)

・OS:OS X EI Captain(Version 10.11.6)
・Python:3.7.0
・Flask:1.0.2
・pipenv:version 2018.11.14

追記

上記app.py中の下記部分request.args['oauth_verifier']の型はstrです。

data={'oauth_verifier': request.args['oauth_verifier']} # ここがエラー発生

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

can110

2018/12/30 08:47

「request.args['oauth_verifier']」の型は何(strまたはbytes?)でしょうか
masty0000

2018/12/30 09:50

確認したところ、strでした。
hayataka2049

2018/12/30 09:52

「該当のソースコード」先頭に import sys print(sys.version) と書いて実行した場合の結果は何になりますか
hayataka2049

2018/12/30 09:56

すみません、質問文の3.7という記載を見落としていました。スルーして構いません
hayataka2049

2018/12/30 09:56

すみません、質問文の3.7という記載を見落としていました。スルーして構いません
masty0000

2018/12/30 10:00

ありがとうございます。 念の為再度確認してみましたが、以下の通りでした。 3.7.0 (default, Jun 28 2018, 07:39:16) [Clang 4.0.1 (tags/RELEASE_401/final)]
guest

回答1

0

FlaskもOAuth認証もよく分からないのですが、エラー内容で検索するとrequest.args uncompatible with quote, which lead decode error #2016が引っかかってきました。
これによるとrequest.url_charsetasciiが入っていると内部でこのエンコーディングが利用され、エラー発生しているのかもしれません。
そうだとするとrequest.url_charset='utf-8'した後に関数を呼び出すと正常に動作するかもしれません。

あるいは以下のようにしてbyteのまま取得できれば、自力でデコードすることで処理できるかもしれません。

Python

1ov = request.args.get('oauth_verifier',type=byte) # byteのまま取得できないか? 2ov = ov.decode('utf-8') 3print(ov) # 念のため意図したものか確認 4# ovはURLデコードも必要? 5 6oauth_session = service.get_auth_session( 7 # 略 8 data={'oauth_verifier': ov}

参考:Flaskのrequest.argsでパラメータの処理について

投稿2018/12/30 10:22

can110

総合スコア38233

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問