如何为admin-on-rest项目实现用户注册页面?求含REST API的示例项目
Hey there! Let's walk through how to build a user registration page for your admin-on-rest project, plus how to tie it to a LoopBack backend that handles email verification and password recovery—since you mentioned those specific features.
Admin-on-rest (now evolved into React Admin) is built for backend admin UIs, so it doesn’t include a registration page out of the box. But since it’s built on React Router, we can easily extend it with custom routes and components.
Step 1: Create a Registration Form Component
First, build a form component to collect user details (username, email, password). We’ll use redux-form (which integrates smoothly with admin-on-rest) and Material-UI components for styling:
// src/auth/Register.js import React from 'react'; import { reduxForm, Field } from 'redux-form'; import { TextField, Button, Card, CardContent, Typography } from '@material-ui/core'; import { connect } from 'react-redux'; import { push } from 'react-router-redux'; import { registerUser } from '../actions/auth'; const RegisterForm = ({ handleSubmit, submitting, error }) => ( <Card style={{ maxWidth: 400, margin: '2rem auto' }}> <CardContent> <Typography variant="h5" gutterBottom>Create Account</Typography> <form onSubmit={handleSubmit}> <div style={{ marginBottom: 16 }}> <Field name="username" component={TextField} label="Username" fullWidth required /> </div> <div style={{ marginBottom: 16 }}> <Field name="email" component={TextField} label="Email" type="email" fullWidth required /> </div> <div style={{ marginBottom: 16 }}> <Field name="password" component={TextField} label="Password" type="password" fullWidth required /> </div> {error && <Typography color="error" variant="body2">{error}</Typography>} <Button type="submit" variant="contained" color="primary" fullWidth disabled={submitting} > Register </Button> </form> </CardContent> </Card> ); const Register = reduxForm({ form: 'registration' })(RegisterForm); const mapDispatchToProps = dispatch => ({ onSubmit: (values) => { dispatch(registerUser(values)) .then(() => dispatch(push('/login'))) .catch(err => console.error('Registration failed:', err)); } }); export default connect(null, mapDispatchToProps)(Register);
Step 2: Extend Admin-on-Rest Routes
Wrap the Admin component with a React Router to add the registration route (and any other public routes like login):
// src/App.js import React from 'react'; import { Admin, Resource } from 'admin-on-rest'; import { Router, Route } from 'react-router-dom'; import createHistory from 'history/createBrowserHistory'; import Register from './auth/Register'; import Login from './auth/Login'; import { authClient } from './auth/authClient'; // Import your admin resources here const history = createHistory(); const App = () => ( <Router history={history}> <div> {/* Public routes */} <Route path="/register" component={Register} /> <Route path="/login" component={Login} /> {/* Admin dashboard */} <Admin history={history} authClient={authClient} loginPage={Login} > {/* Add your admin resources here, e.g.: <Resource name="posts" list={PostList} /> */} </Admin> </div> </Router> ); export default App;
Step 3: Implement the Registration API Call
Create an action to send the registration data to your backend. Use admin-on-rest’s fetchUtils for consistent API handling:
// src/actions/auth.js import { fetchUtils } from 'admin-on-rest'; export const registerUser = (userData) => { return (dispatch) => { const apiUrl = 'http://your-loopback-api/api/Users'; const options = { method: 'POST', body: JSON.stringify(userData), headers: new Headers({ 'Content-Type': 'application/json' }), }; return fetchUtils.fetchJson(apiUrl, options) .then(({ json }) => { // Optionally save user data to localStorage or Redux return json; }); }; };
LoopBack is perfect for this because it has a built-in User model that we can extend to add your required features.
Step 1: Enable User Registration
First, make sure the User model allows public registration. Update server/model-config.json to include create in the public methods:
{ "User": { "dataSource": "db", "public": ["create", "findById", "updateAttributes", "verifyEmail", "forgotPassword", "resetPassword"] } }
Step 2: Add Email Verification
Extend the User model to generate verification tokens and send confirmation emails. We’ll use LoopBack’s email component (install it first with npm install loopback-component-email):
// common/models/user.js module.exports = function(User) { // Generate a verification token (custom helper) User.prototype.generateVerificationToken = function() { return require('crypto').randomBytes(20).toString('hex'); }; // Send verification email after user registration User.afterRemote('create', function(ctx, user, next) { const verificationToken = user.generateVerificationToken(); user.updateAttribute('verificationToken', verificationToken, (err) => { if (err) return next(err); // Send email with verification link User.app.models.Email.send({ to: user.email, from: 'no-reply@yourapp.com', subject: 'Verify Your Account', html: `Click <a href="http://your-app.com/verify-email?token=${verificationToken}">here</a> to verify your email.` }, (err) => { if (err) console.error('Failed to send verification email:', err); next(); }); }); }); // Remote method to verify email User.verifyEmail = function(token, callback) { User.findOne({ where: { verificationToken: token } }, (err, user) => { if (err) return callback(err); if (!user) return callback(new Error('Invalid verification token')); user.updateAttributes({ verified: true, verificationToken: null }, (err) => { if (err) return callback(err); callback(null, 'Email verified successfully!'); }); }); }; User.remoteMethod('verifyEmail', { accepts: [{ arg: 'token', type: 'string', required: true }], returns: { arg: 'message', type: 'string' }, http: { verb: 'get', path: '/verify-email' } }); };
Step 3: Add Password Recovery
Extend the model further to handle password reset requests:
// common/models/user.js (continued) User.prototype.generateResetToken = function() { return require('crypto').randomBytes(20).toString('hex'); }; User.forgotPassword = function(email, callback) { User.findOne({ where: { email: email } }, (err, user) => { if (err) return callback(err); if (!user) return callback(new Error('User not found with that email')); const resetToken = user.generateResetToken(); user.updateAttribute('resetToken', resetToken, (err) => { if (err) return callback(err); User.app.models.Email.send({ to: email, from: 'no-reply@yourapp.com', subject: 'Reset Your Password', html: `Click <a href="http://your-app.com/reset-password?token=${resetToken}">here</a> to reset your password.` }, (err) => { if (err) console.error('Failed to send reset email:', err); callback(null, 'Password reset email sent!'); }); }); }); }; User.remoteMethod('forgotPassword', { accepts: [{ arg: 'email', type: 'string', required: true }], returns: { arg: 'message', type: 'string' }, http: { verb: 'post', path: '/forgot-password' } }); // Add a reset password method too User.resetPassword = function(token, newPassword, callback) { User.findOne({ where: { resetToken: token } }, (err, user) => { if (err) return callback(err); if (!user) return callback(new Error('Invalid reset token')); user.updateAttribute('password', newPassword, (err) => { if (err) return callback(err); user.updateAttribute('resetToken', null, (err) => { if (err) return callback(err); callback(null, 'Password reset successfully!'); }); }); }); }; User.remoteMethod('resetPassword', { accepts: [ { arg: 'token', type: 'string', required: true }, { arg: 'newPassword', type: 'string', required: true } ], returns: { arg: 'message', type: 'string' }, http: { verb: 'post', path: '/reset-password' } });
You can build a full example by:
- Adding
VerifyEmailandResetPasswordcomponents to your admin-on-rest frontend (similar to the Register component) - Wiring those components to call the LoopBack remote methods we created
- Securing your admin routes so only verified users can access them
This setup gives you a complete user management flow: registration → email verification → login → password recovery.
内容的提问来源于stack exchange,提问作者lingjin.w

