当您登录管理员帐户时,会重定向到react-admin页面,然后重定向到/users(http://localhost:3000/admin#/users)。此页面上没有任何内容。我不明白什么是冲突以及为什么会发生这种情况,没有错误。请帮帮我。 (ip改为...)
后端部分
main.js:
const express = require('express');
const mongoose = require('mongoose');
const swaggerUi = require('swagger-ui-express');
const swaggerDocs = require('./Other/SwaggerDoc/swaggerDoc');
const bookRoutes = require('./Books/booksRoutes');
const userRoutes = require('./User/userRoutes');
const authRoutes = require('./User/authRoutes')
const passport = require('./User/passportConfig');
const cors = require('cors');
const corsConfig = require('./Other/corsConfig');
const session = require('express-session');
const app = express();
app.use(express.json());
app.use(session({
secret: '',
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(cors(corsConfig));
async function connectToDatabase() {
const uris = [
'mongodb://.../bookapi',
'mongodb://.../bookapi'
];
for (let uri of uris) {
try {
await mongoose.connect(uri, {useNewUrlParser: true, useUnifiedTopology: true});
console.log(`Connected to ${uri}`);
break;
} catch (err) {
console.log(`Failed to connect to ${uri}`);
}
}
}
connectToDatabase();
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs));
app.use('/api/books', bookRoutes);
app.use('/api/users', userRoutes);
app.use('/auth', authRoutes);
app.listen(3001, () => console.log('Listening on port 3001...'));
用户.js:
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
let userId = 0;
const userSchema = new mongoose.Schema({
id: Number,
email: {
type: String,
required: true,
maxlength: 100,
validate: {
validator: function(v) {
return /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/.test(v);
},
message: props => `${props.value} invalid email`
}
},
password: {
type: String,
required: true,
validate: {
validator: function(v) {
return v.length >= 8 && v.length <= 32;
},
message: 'password length should be 8 - 32 symbols'
}
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user'
}
});
userSchema.pre('save', async function(next) {
let user = this;
if (user.isNew) {
const maxUser = await User.find().sort('-id').limit(1);
const maxId = maxUser.length > 0 ? maxUser[0].id : 0;
user.id = maxId + 1;
}
if (user.isModified('password')) {
user.password = await bcrypt.hash(user.password, 8);
}
next();
});
const User = mongoose.model('User', userSchema);
module.exports = User;
userRoutes.js:
const express = require('express');
const User = require('./user');
const router = express.Router();
router.post('/', async (req, res) => {
try {
const user = new User(req.body);
await user.save();
const userResponse = await User.findOne({id: user.id}).select('-_id -__v');
res.status(201).send(userResponse);
} catch (error) {
res.status(400).send(error);
}
})
router.get('/:id', async (req, res) => {
try {
const user = await User.findOne({id: req.params.id}).select('-_id -__v');
if (!user) {
return res.status(404).send();
}
res.send(user);
} catch (error) {
res.status(500).send(error);
}
});
router.put('/', async (req, res) => {
try {
const user = await User.findOneAndUpdate({id: req.body.id}, req.body, { new: true, runValidators: true }).select('-_id -__v');
if (!user) {
return res.status(404).send();
}
res.send(user);
} catch (error) {
res.status(400).send(error);
}
});
router.delete('/:id', async (req, res) => {
try {
const user = await User.findOneAndDelete({id: req.params.id}).select('-_id -__v');
if (!user) {
return res.status(404).send();
}
res.send(user);
} catch (error) {
res.status(500).send(error);
}
});
router.get('/users', async (req, res) => {
const users = await User.find().select('-_id -__v');
res.send(users);
});
module.exports = router;
passportConfig.js:
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const User = require('./user');
const bcrypt = require('bcrypt');
passport.use(new LocalStrategy({ usernameField: 'email' },
async (email, password, done) => {
try {
const user = await User.findOne({ email });
if (!user) {
return done(null, false, { message: 'Incorrect email.' });
}
if (!await bcrypt.compare(password, user.password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
} catch (error) {
return done(error);
}
}
));
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
});
});
module.exports = passport;
```
authRoutes.js:
```
const express = require('express');
const passport = require('./passportConfig');
const User = require('./user');
const router = express.Router();
router.post('/login', passport.authenticate('local'), async (req, res) => {
const user = await User.findOne({ email: req.user.email }).select('-_id -__v');
res.send(user);
});
router.get('/logout', (req, res) => {
req.logout();
res.send('Logged out');
});
module.exports = router;
corsConfig.js:
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const User = require('./user');
const bcrypt = require('bcrypt');
passport.use(new LocalStrategy({ usernameField: 'email' },
async (email, password, done) => {
try {
const user = await User.findOne({ email });
if (!user) {
return done(null, false, { message: 'Incorrect email.' });
}
if (!await bcrypt.compare(password, user.password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
} catch (error) {
return done(error);
}
}
));
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
});
});
module.exports = passport;
```
authRoutes.js:
```
const express = require('express');
const passport = require('./passportConfig');
const User = require('./user');
const router = express.Router();
router.post('/login', passport.authenticate('local'), async (req, res) => {
const user = await User.findOne({ email: req.user.email }).select('-_id -__v');
res.send(user);
});
router.get('/logout', (req, res) => {
req.logout();
res.send('Logged out');
});
module.exports = router;
前端部分
App.js:
const corsConfig = {
origin: 'http://localhost:3000',
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
credentials: true,
allowedHeaders: ['Content-Type', 'Authorization'],
exposedHeaders: ['X-Total-Count']
};
module.exports = corsConfig;
AppRouter.jsx:
import AppRouter from './components/AppRouter';
import './index.css';
import {HashRouter} from "react-router-dom";
function App() {
return (
<HashRouter>
<div className="App">
<AppRouter/>
</div>
</HashRouter>
);
}
export default App;
路由器/index.js:
import React, { useEffect } from 'react'
import { Routes, Route, useNavigate, Outlet} from "react-router-dom";
import { Links } from '../router'
function AppRouter() {
const navigate = useNavigate();
useEffect(() => {
navigate('/login')
}, []);
return (
<Routes>
<Route path="/*" element={<Outlet />} />
{Links.map(route => {
return (
<Route
element={React.createElement(route.component)}
path={route.path}
exact={route.exact}
/>
);
})}
</Routes>
)
}
export default AppRouter
登录.jsx:
import AdminInt from "../pages/Admin/AdminInt";
import Login from "../pages/Login/Login";
export const Links = [
{path: '/login', component: Login, exact: true},
{path: '/admin/*', component: AdminInt , exact: true}
]
AdminInt.jsx:
import React, { useState } from 'react';
import { useLogin, useNotify } from 'react-admin';
import { useNavigate } from 'react-router-dom';
const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const login = useLogin();
const notify = useNotify();
const navigate = useNavigate();
const handleSubmit = async (event) => {
event.preventDefault();
login({ email, password })
.then(() => {
notify('Successfully logged in', 'info');
navigate('/admin');
})
.catch((error) => {
notify('Invalid email or password', 'warning');
});
};
return (
<form onSubmit={handleSubmit}>
<label>
Email:
<input type="email" value={email} onChange={e => setEmail(e.target.value)} required />
</label>
<label>
Password:
<input type="password" value={password} onChange={e => setPassword(e.target.value)} required />
</label>
<button type="submit">Log in</button>
</form>
);
};
export default Login;
工具.jsx:
import React from "react";
import { Admin, Resource } from 'react-admin';
import dataProvider from "../../components/Admin/dataProvider";
import { UserList, UserEdit, UserCreate } from '../../components/Admin/Tools';
const AdminInt = () => (
<Admin dataProvider={dataProvider}>
<Resource name="users" list={UserList} edit={UserEdit} create={UserCreate} />
</Admin>
);
export default AdminInt;
dataProvider.js:
import React from "react";
import { List, Datagrid, TextField, EmailField, Edit, SimpleForm, TextInput, SelectInput, Create } from 'react-admin';
export const UserList = props => (
<List {...props}>
<Datagrid rowClick="edit">
<TextField source="id" />
<TextField source="role" />
<EmailField source="email" />
</Datagrid>
</List>
);
export const UserEdit = props => (
<Edit {...props}>
<SimpleForm>
<TextInput disabled source="id" />
<TextInput source="email" />
<TextInput source="password" type="password" />
<SelectInput source="role" choices={[
{ id: 'user', name: 'user' },
{ id: 'admin', name: 'admin' }
]} />
</SimpleForm>
</Edit>
);
export const UserCreate = props => (
<Create {...props}>
<SimpleForm>
<TextInput source="email" />
<TextInput source="password" type="password" />
<SelectInput source="role" choices={[
{ id: 'user', name: 'user' },
{ id: 'admin', name: 'admin' }
]} />
</SimpleForm>
</Create>
);
在您的 userRoutes.js 文件中,您有以下设置:
import { fetchUtils } from 'react-admin';
import { stringify } from 'query-string';
const apiUrl = 'http://.../api';
const httpClient = (url, options = {}) => {
if (!options.headers) {
options.headers = new Headers({ Accept: 'application/json' });
}
const token = localStorage.getItem('token');
options.headers.set('Authorization', `Bearer ${token}`);
return fetchUtils.fetchJson(url, options);
}
export default {
getList: (resource, params) => {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const query = {
sort: JSON.stringify([field, order]),
range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]),
filter: JSON.stringify(params.filter),
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
return httpClient(url).then(({ headers, json }) => {
headers.set('X-Total-Count', json.length);
return {
data: json,
total: parseInt(headers.get('x-total-count').split('/').pop(), 10),
};
});
},
getOne: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`).then(({ json }) => ({
data: json,
})),
getMany: (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids }),
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
return httpClient(url).then(({ json }) => ({ data: json }));
},
getManyReference: (resource, params) => {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const query = {
sort: JSON.stringify([field, order]),
range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]),
filter: JSON.stringify({ ...params.filter, [params.target]: params.id }),
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
return httpClient(url).then(({ headers, json }) => ({
data: json,
total: parseInt(headers.get('x-total-count').split('/').pop(), 10),
}));
},
update: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`, {
method: 'PUT',
body: JSON.stringify(params.data),
}).then(({ json }) => ({ data: json })),
updateMany: (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids }),
};
return httpClient(`${apiUrl}/${resource}?${stringify(query)}`, {
method: 'PUT',
body: JSON.stringify(params.data),
}).then(({ json }) => ({ data: json }));
},
create: (resource, params) =>
httpClient(`${apiUrl}/${resource}`, {
method: 'POST',
body: JSON.stringify(params.data),
}).then(({ json }) => ({
data: { ...params.data, id: json.id },
})),
delete: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`, {
method: 'DELETE',
}).then(({ json }) => ({ data: json })),
deleteMany: (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids }),
};
return httpClient(`${apiUrl}/${resource}?${stringify(query)}`, {
method: 'DELETE',
}).then(({ json }) => ({ data: json }));
},
};
在你的 main.js 文件中你有这样的设置:
router.get('/users', async (req, res) => {
const users = await User.find().select('-_id -__v');
res.send(users);
});
在这种安排中,终点实际上是 app.use('/api/users', userRoutes);
。 在您的
/api/users/users
文件中,您正在导入
AdminInt.jsx
,它可能会在您进行设置的
<UserList/>
中调用某些 api
dataProvider.js
因此您可能会点击 const url = `${apiUrl}/${resource}?${stringify(query)}`;
而不是
/api/users/users
,这可能会导致问题。还要检查 chrome 中的网络选项卡,看看您的 api 请求是否成功。