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

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

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

Q&A

解決済

1回答

1984閲覧

Express Sessionに入れた値がundefinedになってしまう

kazu235

総合スコア21

0グッド

0クリップ

投稿2022/06/11 05:51

実現したいこと

React Nativeでスマホアプリを開発しています。
バックエンドでNode.jsを使用してWebAPIを作っていて、Express SessionでSession管理をしたいです。

発生している問題

postでreq.sessionに値を保存しても、getで呼び出したときにundefinedになってしまいます。
どうしたらいいでしょうか?

app.js(Node.js)

javascript

1const express = require('express'); 2const app = express(); 3const session = require('express-session'); 4// const bodyParser = require('body-parser'); 5 6app.use(express.json()); 7 8// app.use(express.urlencoded({ extended: false })); 9app.use(session({ 10 secret: 'my_secret_key', 11 resave: false, 12 saveUninitialized: false, 13 rolling: true, 14 cookie: { 15 httpOnly: true, 16 maxAge: 60 * 1000 17 } 18})); 19 20//routes 21app.use('/api/user', require('./routes/user')); 22app.use('/api/tmp_user', require('./routes/tmp_user')); 23app.use('/api/post', require('./routes/post')); 24app.use('/api/hand_history', require('./routes/hand_history')); 25 26app.use('/validation', require('./routes/validation')); 27app.use('/login', require('./routes/login')); 28 29 30 31app.listen(3000);

login.js(Node.js)

javascript

1const express = require('express'); 2const router = express.Router(); 3const connection = require('../connetction'); 4const bcrypt = require('bcrypt'); 5 6router.get('/', (req, res, next) => { 7 console.log(req.session); 8}) 9 10router.post('/', (req, res) => { 11 const email = req.body.email; 12 const plain = req.body.password; 13 14 connection.query( 15 'select * from users where email = ?', 16 [email], 17 (error, results) => { 18 if (results.length > 0) { 19 const hash = results[0].password; 20 bcrypt.compare(plain, hash, (error, isEqual) => { 21 if (isEqual) { 22 req.session.userId = results[0].id; 23 console.log(req.session) 24 console.log('ログイン成功') 25 } else { 26 console.log('ログイン失敗') 27 } 28 }) 29 } 30 } 31 ) 32}) 33 34module.exports = router;

Login.js(React Native)

javascript

1import { StatusBar } from 'expo-status-bar'; 2import { useState, Component } from 'react'; 3import { Platform, StyleSheet, Text, View, Button, TextInput, TouchableOpacity, Alert } from 'react-native'; 4import axios from 'axios'; 5 6const host = 'http://192.168.0.195:3000' 7 8const Login = () => { 9 10 const [email, setEmail] = useState(''); 11 const [password, setPassword] = useState(''); 12 const [message, setMessage] = useState([]); 13 14 const login = async () => { 15 16 await axios.post(host + '/login', 17 { 18 email: email, 19 password: password, 20 } 21 ) 22 .then(() => { 23 }) 24 .catch((error) => console.log(error)); 25 } 26 const test = async () => { 27 await axios.get(host + '/login'); 28 29 } 30 31 return ( 32 <View style={styles.container}> 33 <Text>メールアドレス</Text> 34 <TextInput 35 style={styles.input} 36 placeholder='mail' 37 onChangeText={(text) => setEmail(text)} 38 /> 39 <Text>パスワード</Text> 40 <TextInput 41 style={styles.input} 42 secureTextEntry={true} 43 placeholder='password' 44 onChangeText={(text) => setPassword(text)} 45 /> 46 47 <Button 48 title='ログイン' 49 onPress={() => { 50 login(); 51 }} 52 /> 53 <Button 54 title='test' 55 onPress={() => { 56 test(); 57 }} 58 /> 59 {message.map((index) => { 60 return ( 61 <Text>{index.message}</Text> 62 ) 63 })} 64 <StatusBar style="auto" /> 65 66 </View> 67 ); 68} 69const styles = StyleSheet.create({ 70 container: { 71 flex: 1, 72 backgroundColor: '#fff', 73 alignItems: 'center', 74 justifyContent: 'center', 75 }, 76 input: { 77 borderWidth: 1, 78 width: 150 79 }, 80}); 81 82 83export default Login;

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

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

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

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

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

guest

回答1

0

ベストアンサー

私はReactNativeには全く詳しくないので、
Webの仕様から分かる範囲で解決のアプローチを模索する手助けという形で記述していきます。


まず普通のWebサイトでセッションが使える理由に関して解説していきます。

ReactはJavaScriptで作られています。
Webブラウザ(Chrome等)はHTMLをダウンロードした後に
DOMツリーを展開して描画、それと共にJavaScriptのエンジンを可動させてDOMツリーの編集に備えます。
JavaScript越しにDOMツリーを変更したら、それが同期的に画面上に反映される仕組みとなっています。

axiosのようなHTTPリクエストを発射するライブラリを使った場合、
Webブラウザに搭載されている機能を一時的に借りて実現します。

Webサーバの「セッションに保存」という仕組みを使うと
Webサーバから「クッキーにIDxxxxで保管しておいて、次の認証でその文字列を使ってアクセスしてくださいね」というお願いが来ます。
Webブラウザ(Chrome等)はそのお願いを元にクッキーにIDxxxxを保管しておいて、次回以降のAjax通信等で役立てます。

さて、先程Webサイトから発行される「お願い」と記載しました。
今回の対象はNode.jsやReactNativeというWebブラウザとは微塵も関係ない世界の話です。
誰がこの依頼を元にクッキーを生成・保管・管理してくれるのでしょうか?

これが今回ReactNativeでセッションの保存が効果なかった原因でほぼ間違いないかと思います。
サーバーにはセッション情報は生成されていてIDxxxxでアクセスしてくるのを待っているのに、
二度目以降にそのIDxxxxの情報を持ってアクセスしに来ないのだから、データを開示出来るわけがないんです。

怪しいのはこのどちらかです。

  • Node.js上でaxiosモジュールを利用した時のCookieの扱いが適当、というか無い可能性あり
  • ReactNative上ではCookieを取り扱えない

結論としては上記のaxiosモジュールを使うのがダメで間違いなさそうです。


上記を前提にReactNative Cookieといったワードで検索してみます。

こういう情報があるので、とりあえずReactNative上でCookieを扱う事は少なくとも可能。
ただしめんどくさいという感じです。

拉致があかないのであかないので公式サイトのドキュメントを漁ってみます。
ありました。
参考記事: Guide -> Workflow -> Networking

これの#Known Issues with fetch and cookie based authenticationセクションにAuthやCookie周りの記載があります。

Known Issues with fetch and cookie based authentication

Cookie based authentication is currently unstable. You can view some of the issues raised here

意訳: FetchやCookieベースの認証周りの課題の話
Cookieベースの認証は現在不安定です。このIssueで報告されているので興味があればどうぞ

こんなセクションがある時点でReactNativeでは使えて当然という話です。
安定性等に関していくつか不安事があるにせよ、使えないというのはおかしい。
従ってCookieが効かないのはaxiosのせいです。

そもそもNode.jsはつい最近のバージョン18でようやくネイティブのFetch APIをサポートしました。
それまではC++で作ったhttpモジュールでそれっぽいものをでっち上げるしかなかったんですよね。
axiosモジュールはどのくらい前だろ、Node.jsの8系とかの頃の時代のモジュールですから、Fetchを使ってるわけがないんですよね。
その辺が挙動が違う問題だろうと思います(興味が出たら調べてみてください)

結論としてはReactNativeのドキュメントの通りFetchを使ってアクセスしよう、
そうすればReactNative上でCookie機能が自動でついてくるよってことですね。


では具体的なコードを見ていきましょう。
FetchでPOSTを飛ばすにはmethod指定が使えます。
参考記事: Fetch の使用 - MDN

js

1const Login = () => { 2 3 const [email, setEmail] = useState(''); 4 const [password, setPassword] = useState(''); 5 const [message, setMessage] = useState([]); 6 7 const login = async () => { 8 // async内ではよほどのケース以外はPromise.thenやPromise.catchを実行してはならない 9 // 何故ならこいつらを駆逐するための構文なのに、無意味に使ったら何のために宣言したのか……となる 10 11 // このようにtry-catchでくくると、await Promiseで見張っているものがrejectされたらcatchに入ってくる 12 try { 13 // fetchは何もインポートしなくても普通に使えるらしい 14 // await構文はPromise.thenの第一引数を値として引っ張り上げてくる機能があるのでこう書ける 15 const res = await fetch(host + '/login', { 16 method: "POST", 17 headers: { 18 'Content-Type': 'application/json', 19 }, 20 body: JSON.stringify({email, password}), 21 }); 22 const json = await res.json(); // res.json()の結果もPromiseインスタンスなのでもう一度awaitで引っ張り出す 23 console.log(json); 24 } catch(err) { 25 console.error(err); 26 } 27 } 28 const test = async () => { 29 const res = await fetch(host + '/login'); 30 const json = await res.json(); 31 console.log(json); 32 } 33 34 // return (...省略...);

コードの動作は未確認です。
ReactNativeのWebView周りの話はさっぱりわからないので不安ですが、
これでちょっとは進むんじゃないですかね?

試してみてください。

投稿2022/06/11 07:31

miyabi-sun

総合スコア21158

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

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

kazu235

2022/06/11 12:58 編集

ご回答ありがとうございます。 React Native側でCookieの値が保存されていないのが原因なのですね。 大変参考になります。 ただ、ご教授いただいたやり方を試してみたのですが、同様にreq.sessionのなかにuserIdは確認できませんでした。 req.sessionの中にuserIdがあるかどうかでセッションが正しく通っているかどうかを確認するというこのデバッグのやり方は合っているのでしょうか?
miyabi-sun

2022/06/13 04:20

まずホンマに保存できてるのか?の確認からですね。 この辺はMySQLと併用される事が多い一時情報の保管に適したサーバのRedisを使うと良いでしょう。 RedisサーバさえDocker等で起動させてしまえば Express-sessionもストアの項目に登録するだけで読み書き出来るようになります。 https://symfoware.blog.fc2.com/blog-entry-2542.html docker-composeでRedisサーバを立ち上げたら中に入ってコマンド叩けば確認出来ます https://qiita.com/uggds/items/5e4f8fee180d77c06ee1
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問