Firebase 身份验证:如何解决“TypeError:无法读取 null 的属性‘getIdToken’”错误
我是实施身份验证的新手,对如何解决我遇到的问题有些困惑。
我正在构建一个 React 应用,并希望登录的用户能够通过
GigRegister
组件将数据提交到 Firebase 数据库。但是,当我尝试提交信息时,出现以下错误:
TypeError: Cannot read property 'getIdToken' of null
关于如何解决这个问题,您有什么想法吗?
这是登录页面的代码(登录后重定向到 Gig 注册页面):
import React from 'react'
import Header from './Header'
import Button from '@material-ui/core/Button'
import axios from 'axios'
import {Link} from 'react-router-dom'
class Login extends React.Component {
constructor() {
super();
this.state = {
email: "",
password: "",
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
this.setState({
[e.target.name]: e.target.value,
});
}
handleSubmit(e) {
console.log("submit reached");
e.preventDefault();
const loginData = {
email: this.state.email,
password: this.state.password,
};
axios("http://localhost:5000/gig-fort/us-central1/api/login", {
method: "POST",
headers: {
"content-type": "application/json",
},
data: loginData,
})
.then((res) => {
console.log(res.data);
this.props.history.push("/gigregister");
})
.catch((err) => {
console.error(err);
});
}
render() {
return (
<>
<div>
<Header />
</div>
<Link to="/Homepage" style={{ textDecoration: "none" }}>
<h1 className="login-header">Gigs this week</h1>
</Link>
<div className="login-main">
<div className="login">
<h2>Venue login</h2>
<form onSubmit={this.handleSubmit}>
<input
type="text"
name="email"
placeholder="email"
onChange={this.handleChange}
/>
<br></br>
<input
type="password"
name="password"
placeholder="password"
onChange={this.handleChange}
/>
<div className="button">
<Button type="submit">Submit</Button>
</div>
</form>
</div>
<Link to="/venueregister" style={{ textDecoration: "none" }}>
<h2 style={{ color: "#b49650" }}>Register a venue</h2>
</Link>
</div>
</>
);
}
}
export default Login;
..这是 Gig 注册页面:
import React from "react";
import Header from "./Header";
import TextField from "@material-ui/core/TextField";
import Button from "@material-ui/core/Button";
import axios from "axios";
import * as firebase from 'firebase'
firebase.initializeApp({
apiKey: "",
authDomain: "",
databaseURL: "",
projectId: "gig-fort",
storageBucket: "gig-fort.appspot.com",
messagingSenderId: "",
appId: ""
})
class GigRegister extends React.Component {
constructor() {
super();
this.state = {
name: "",
venue: "",
time: "",
date: "",
genre: "",
tickets: "",
price: "",
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({
[e.target.name]: e.target.value,
});
}
handleSubmit(e) {
console.log("submit function reached");
e.preventDefault();
const gigData = {
name: this.state.name,
venue: this.state.venue,
time: this.state.time,
date: this.state.date,
genre: this.state.genre,
tickets: this.state.tickets,
price: this.state.price,
};
firebase.auth().currentUser.getIdToken().then(function(token) {
axios("http://localhost:5000/gig-fort/us-central1/api/createGigListing", {
method: "POST",
headers: {
"content-type": "application/json",
"Authorization": "Bearer "+token,
},
data: gigData,
})
})
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.error(err);
});
}
render() {
return (
<div className="gig-register">
<Header />
<h1 className="header-gigReg">Register a gig</h1>
<form onSubmit={this.handleSubmit}>
<TextField
placeholder="Event name"
defaultValue="Event name"
id="name"
name="name"
onChange={this.handleChange}
/>
<TextField
placeholder="Venue"
defaultValue="Venue"
id="venue"
name="venue"
onChange={this.handleChange}
/>
<TextField
placeholder="Time"
defaultValue="Time"
type="time"
label="Enter start time"
id="time"
name="time"
InputLabelProps={{
shrink: true,
}}
inputProps={{
step: 300, // 5 min
}}
onChange={this.handleChange}
/>
<TextField
id="date"
label="Select date"
type="date"
defaultValue="2017-05-24"
InputLabelProps={{
shrink: true,
}}
onChange={(e) => {
this.setState({ date: e.target.value });
}}
/>
<TextField
placeholder="Genre"
defaultValue="Genre"
id="genre"
name="genre"
onChange={this.handleChange}
/>
<TextField
placeholder="Tickets"
defaultValue="Tickets"
id="tickets"
name="tickets"
onChange={this.handleChange}
/>
<TextField
placeholder="Price"
defaultValue="Price"
id="price"
name="price"
onChange={this.handleChange}
/>
<Button type="submit">Submit</Button>
</form>
</div>
);
}
}
export default GigRegister
后端函数:
app.post('/login', (req,res)=> {
const user = {
email: req.body.email,
password: req.body.password
}
let errors = {}
if(isEmpty(user.email)){
errors.email = 'Must not be empty'
}
if(isEmpty(user.password)){
errors.password = 'Must not be empty'
}
if(Object.keys(errors).length >0){
return res.status(400).json(errors)
}
firebase.auth().signInWithEmailAndPassword(user.email, user.password)
.then((data) => {
return data.user.getIdToken()
})
.then(token => {
return res.json({token})
})
.catch(err => {
if(err.code === 'auth/wrong-password'){
return res.status(403).json({general: 'Wrong credentials, please try again'})
}
return res.status(500).json({error: err.code})
})
})
提示
TypeError:无法读取 null 的属性“getIdToken”
问题
您所写的内容:
firebase.auth().currentUser.getIdToken()
编译器接收到的内容:
null.getIdToken()
null
没有
getIdToken
方法,因此当您尝试调用该函数时,您会收到错误
TypeError:无法读取 null 的属性“getIdToken”
。
解决方案
您需要在代码中的某个地方调用
firebase.auth().signInWithEmailAndPassword(email, password)
,然后
currentUser
的详细信息将保存在浏览器中
firebaseLocalStorageDB
。
然后,您可以正确调用
firebase.auth().currentUser.getIdToken()
。
此外,我不明白您为什么在这里使用
axios
。您应该使用 firebase 读/写 API,它
可在此处获得
。从上到下阅读文档,您不会后悔的!
您出于什么原因从后端调用
signInWithEmailAndPassword
?据我所知,
app.post('/login', ...)
仅验证正文,调用
signInWithEmailAndPassword
并发送令牌作为响应。
我建议将此逻辑移至客户端。在这种情况下,firebase
auth
对象将具有
currentUser
。
如果您不想这样做,则必须在客户端的 POST 登录请求后调用
auth.signInWithCustomToken
。文档:
https://firebase.google.com/docs/auth/web/custom-auth#authenticate-with-firebase
。
更新:
1. 创建新文件
firebase.js
import * as firebase from 'firebase/app'
import 'firebase/auth'
/* if you use any other firebase modules import they here.
* for example:
* import "firebase/database"
*/
export default function initFirebase() {
firebase.initializeApp({
apiKey: '',
authDomain: '',
databaseURL: '',
projectId: 'gig-fort',
storageBucket: 'gig-fort.appspot.com',
messagingSenderId: '',
appId: ''
})
}
2. 从 index.js(项目的主文件)初始化应用程序
import initFirebase from 'path/to/firebase.js';
// ...imports
initFirebase();
3. 将 auth 导入到
Login
组件;
import React from 'react'
import Header from './Header'
import Button from '@material-ui/core/Button'
import axios from 'axios'
import { Link } from 'react-router-dom'
import { auth } from 'firebase/app'
4.替换
Login
组件中的提交方法
handleSubmit(e) {
e.preventDefault()
const { email, password } = this.state
const { history } = this.props
auth().signInWithEmailAndPassword(email, password)
.then((res) => {
console.log(res)
history.push('/gigregister')
})
.catch(console.error)
}
5. 替换
GigRegister
组件中的身份验证导入
import { auth } from 'firebase/app'
6. 替换
GigRegister
组件中的身份验证调用
/* handleSubmit method */
auth().currentUser.getIdToken().then(console.log)
firebase.auth().currentUser
为
null
。您必须在登录页面登录 Firebase。