继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

React+Nest全栈开发入门教程

吃鸡游戏
关注TA
已关注
手记 485
粉丝 55
获赞 339
概述

React+Nest全栈开发结合了React的高效前端组件化和Nest的强大后端模块化,提供了一种无缝集成的解决方案,提高开发效率和代码可维护性。通过详细的技术说明,本文介绍了从环境搭建到项目集成的全过程,帮助开发者快速上手React+Nest全栈开发。

React+Nest全栈开发简介

什么是React和Nest

React 是一个由 Facebook 开发并维护的开源 JavaScript 库,用于构建用户界面。它主要用于构建单页应用,提供了一种高效且灵活的方式来创建可重用的UI组件。React 通过虚拟DOM和高效的渲染机制,提升了应用的性能,使得开发变得更为简单。

Nest 是一个用于构建高效、可扩展的服务器端 Node.js 应用程序的框架。它基于 Express,提供了一组强大的工具来构建复杂的服务器端逻辑,包括中间件、装饰器、依赖注入等。Nest 的设计目标是利用 TypeScript 的强类型系统来保证代码的可维护性,同时提供了简洁的 API 使得开发更加高效。

为什么选择React+Nest进行全栈开发

React 和 Nest 的组合为开发者提供了一种无缝集成前后端的解决方案。前端使用 React 可以构建快速、响应式的用户界面,而后端使用 Nest 能够构建高性能、可扩展的服务器端逻辑。这种组合不仅提高了开发效率,还简化了前后端的通信,使得整个开发过程更加顺畅。

React 的组件化和 Nest 的模块化设计使得开发和维护都变得更加容易。React 组件可以被轻松地重用和组合,而 Nest 的服务和控制器则可以按照业务逻辑进行拆分,使得代码结构清晰,易于维护。此外,由于 React 和 Nest 都支持 TypeScript,开发者可以利用类型检查和静态分析功能来减少潜在的错误。

开发环境搭建

开发环境的搭建包括安装 Node.js、NPM(或 Yarn)、创建 React 项目和 Nest 项目。

安装Node.js和NPM

Node.js 是一个开源、跨平台的 JavaScript 运行时环境,NPM 是 Node.js 的包管理和分发工具。首先,访问 Node.js 官方网站 (https://nodejs.org/),下载最新的 LTS(长期支持)版本并安装。安装完成后,可以通过命令行验证安装是否成功:

node -v
npm -v

安装Yarn

Yarn 是一个由 Facebook 开发的包管理器,提供了一个更安全、更快速的依赖管理方案。可以通过以下命令安装 Yarn:

npm install -g yarn

创建React项目

使用 Create React App 工具可以快速搭建一个基本的 React 项目。以下是创建项目的步骤:

  1. 安装 Create React App:

    npx create-react-app my-app
  2. 进入项目目录并启动开发服务器:

    cd my-app
    npm start

创建Nest项目

使用 Nest CLI 工具可以快速搭建一个基本的 Nest 项目。以下是创建项目的步骤:

  1. 安装 Nest CLI:

    npm i -g @nestjs/cli
  2. 创建一个新的 Nest 项目:

    nest new my-nest-app
  3. 进入项目目录并启动开发服务器:

    cd my-nest-app
    npm run start:dev

完成以上步骤后,你将拥有一个基本的 React 前端和 Nest 后端项目,并可以通过浏览器访问它们。

React前端开发基础

本节将详细介绍 React 的基本概念和开发技巧,包括创建项目、组件与状态管理、路由与导航以及样式与 CSS 引入。

创建React项目

你已经通过 create-react-app 创建了一个基本的 React 项目。为了进一步了解 React,我们来创建一个新的 React 组件:

  1. src 目录下创建一个新的文件夹 components
  2. components 文件夹中创建一个新文件 HelloWorld.tsx

    import React from 'react';
    
    const HelloWorld: React.FC = () => {
     return <div>Hello World!</div>;
    };
    
    export default HelloWorld;
  3. App.tsx 文件中引入并使用 HelloWorld 组件:

    import React from 'react';
    import HelloWorld from './components/HelloWorld';
    
    function App() {
     return (
       <div className="App">
         <HelloWorld />
       </div>
     );
    }
    
    export default App;

组件与状态管理

React 中的组件分为两种类型,一种是无状态组件(Stateless Component),另一种是有状态组件(Stateful Component)。无状态组件只接收输入,不保存自身的状态;而有状态组件可以通过定义状态(state)来存储和管理数据。

以下是如何定义一个有状态组件:

import React, { Component } from 'react';

class Counter extends Component {
  state = {
    count: 0,
  };

  increment = () => {
    this.setState((prevState) => {
      return { count: prevState.count + 1 };
    });
  };

  render() {
    return (
      <div>
        <h1>Count: {this.state.count}</h1>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

export default Counter;

在这个示例中,Counter 组件维护了一个状态 count,并提供了一个 increment 方法来增加这个状态。

路由与导航

React 中通常使用 react-router-dom 来实现路由。首先需要安装 react-router-dom

npm install react-router-dom

接下来,创建一些路由:

import React from 'react';
import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';
import Counter from './Counter';
import HelloWorld from './HelloWorld';

const App: React.FC = () => {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/counter">Counter</Link>
            </li>
            <li>
              <Link to="/hello">HelloWorld</Link>
            </li>
          </ul>
        </nav>

        <Switch>
          <Route path="/counter">
            <Counter />
          </Route>
          <Route path="/hello">
            <HelloWorld />
          </Route>
          <Route path="/">
            <Home />
          </Route>
        </Switch>
      </div>
    </Router>
  );
};

export default App;

在这个示例中,我们定义了三个路由:/counter/hello/'(根目录),分别对应 Counter 组件、HelloWorld 组件和 Home 组件。

样式与CSS引入

React 中可以使用多种方式引入 CSS,包括内联样式、CSS 文件和 CSS-in-JS 库。这里我们将使用普通的 CSS 文件来引入样式:

  1. src 目录下创建一个 App.css 文件,并添加一些样式:

    .App {
     text-align: center;
     font-family: Arial, sans-serif;
    }
    
    nav {
     background-color: #333;
     padding: 10px;
    }
    
    nav ul {
     list-style: none;
     padding: 0;
    }
    
    nav ul li {
     display: inline;
     margin-right: 10px;
    }
    
    nav a {
     color: white;
     text-decoration: none;
    }
  2. App.tsx 文件中引入这个 CSS 文件:

    import React from 'react';
    import './App.css';
    import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';
    import Counter from './Counter';
    import HelloWorld from './HelloWorld';
    
    const App: React.FC = () => {
     return (
       <Router>
         <div className="App">
           <nav>
             <ul>
               <li>
                 <Link to="/">Home</Link>
               </li>
               <li>
                 <Link to="/counter">Counter</Link>
               </li>
               <li>
                 <Link to="/hello">HelloWorld</Link>
               </li>
             </ul>
           </nav>
    
           <Switch>
             <Route path="/counter">
               <Counter />
             </Route>
             <Route path="/hello">
               <HelloWorld />
             </Route>
             <Route path="/">
               <Home />
             </Route>
           </Switch>
         </div>
       </Router>
     );
    };
    
    const Home: React.FC = () => <h2>Welcome to the Home Page</h2>;
    
    export default App;

通过这种方式,你可以轻松地为你的 React 应用添加样式。

Nest后端开发基础

本节将详细介绍 Nest 的基本概念和开发技巧,包括创建项目、控制器与服务、数据库集成与操作、RESTful API设计与实现。

创建Nest项目

你已经通过 nest new 命令创建了一个基本的 Nest 项目。为了进一步了解 Nest,我们将创建一个新的控制器和服务。

  1. 创建一个新的控制器 app.controller.ts

    import { Controller, Get, Post } from '@nestjs/common';
    import { AppService } from './app.service';
    
    @Controller('app')
    export class AppController {
     constructor(private readonly appService: AppService) {}
    
     @Get()
     getHello(): string {
       return this.appService.getHello();
     }
    }
  2. 创建一个新的服务 app.service.ts

    import { Injectable } from '@nestjs/common';
    
    @Injectable()
    export class AppService {
     getHello(): string {
       return 'Hello World!';
     }
    }
  3. main.ts 文件中引入并使用创建的控制器和服务:

    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    import { AppController } from './app.controller';
    
    async function bootstrap() {
     const app = await NestFactory.create(AppModule);
     app.getHttpAdapter().useStaticResponses({
       '404': 'Not Found',
     });
     await app.listen(3000);
    }
    
    bootstrap();

控制器与服务

在 Nest 中,控制器(Controller)负责处理 HTTP 请求,而服务(Service)则用于实现业务逻辑。控制器可以依赖注入服务来执行业务操作。

例如,我们可以通过以下方式实现一个简单的登录功能:

  1. 创建一个新的服务 auth.service.ts

    import { Injectable } from '@nestjs/common';
    
    @Injectable()
    export class AuthService {
     async login(username: string, password: string): Promise<string> {
       // 模拟登录逻辑
       if (username === 'admin' && password === 'admin') {
         return 'Login successful';
       } else {
         throw new Error('Invalid credentials');
       }
     }
    }
  2. app.module.ts 文件中导入并提供服务:

    import { Module } from '@nestjs/common';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    import { AuthService } from './auth.service';
    
    @Module({
     imports: [],
     controllers: [AppController],
     providers: [AppService, AuthService],
    })
    export class AppModule {}
  3. 创建一个新的控制器 auth.controller.ts

    import { Controller, Post } from '@nestjs/common';
    import { AuthService } from './auth.service';
    
    @Controller('auth')
    export class AuthController {
     constructor(private readonly authService: AuthService) {}
    
     @Post('login')
     async login(username: string, password: string): Promise<string> {
       return this.authService.login(username, password);
     }
    }

通过这种方式,你可以在 Nest 中实现复杂的业务逻辑,并通过 HTTP 请求进行调用。

数据库集成与操作

Nest 允许集成各种数据库,包括 MySQL、PostgreSQL、MongoDB 等。这里我们以 MongoDB 为例,展示如何进行数据库集成。

  1. 安装 MongoDB 相关依赖:

    npm install @nestjs/mongoose mongoose
  2. 创建一个新的模块 app.module.ts

    import { Module } from '@nestjs/common';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    import { AuthService } from './auth.service';
    import { AuthController } from './auth.controller';
    import { MongooseModule } from '@nestjs/mongoose';
    import { UserSchema } from './user.schema';
    import { UserController } from './user.controller';
    import { UserService } from './user.service';
    
    @Module({
     imports: [
       MongooseModule.forRoot('mongodb://localhost:27017/mydb'),
       MongooseModule.forFeature([{ name: 'User', schema: UserSchema }]),
     ],
     controllers: [AppController, AuthController, UserController],
     providers: [AppService, AuthService, UserService],
    })
    export class AppModule {}
  3. 创建一个新的模式 user.schema.ts

    import { Schema, Document } from 'mongoose';
    import { User } from './user.interface';
    
    export const UserSchema: Schema = new Schema<User>({
     username: { type: String, required: true },
     password: { type: String, required: true },
    });
  4. 创建一个新的服务 user.service.ts

    import { Injectable } from '@nestjs/common';
    import { InjectModel } from '@nestjs/mongoose';
    import { Model } from 'mongoose';
    import { User } from './user.interface';
    import { UserSchema } from './user.schema';
    
    @Injectable()
    export class UserService {
     constructor(@InjectModel(UserSchema.name) private userModel: Model<User>) {}
    
     async createUser(user: User): Promise<User> {
       const createdUser = new this.userModel(user);
       return createdUser.save();
     }
    
     async findUser(username: string): Promise<User | null> {
       return this.userModel.findOne({ username });
     }
    }
  5. 创建一个新的控制器 user.controller.ts

    import { Controller, Post, Req, Body } from '@nestjs/common';
    import { UserService } from './user.service';
    import { User } from './user.interface';
    
    @Controller('users')
    export class UserController {
     constructor(private readonly userService: UserService) {}
    
     @Post('register')
     async registerUser(@Body() user: User): Promise<User> {
       return this.userService.createUser(user);
     }
    
     @Post('login')
     async loginUser(@Body() { username, password }): Promise<User | null> {
       const user = await this.userService.findUser(username);
       if (user && user.password === password) {
         return user;
       }
       return null;
     }
    }

通过这种方式,你可以在 Nest 中实现数据库操作,并通过 HTTP 请求进行调用。

RESTful API设计与实现

RESTful API 使用 HTTP 协议定义了一系列资源的操作,包括 GET、POST、PUT、DELETE 等。为了实现这些操作,我们可以通过 Nest 中的控制器来定义具体的路由和处理函数。

例如,我们可以通过以下方式实现 CRUD 操作:

  1. 创建一个新的服务 user.service.ts

    import { Injectable } from '@nestjs/common';
    import { InjectModel } from '@nestjs/mongoose';
    import { Model } from 'mongoose';
    import { User } from './user.interface';
    import { UserSchema } from './user.schema';
    
    @Injectable()
    export class UserService {
     constructor(@InjectModel(UserSchema.name) private userModel: Model<User>) {}
    
     async createUser(user: User): Promise<User> {
       const createdUser = new this.userModel(user);
       return createdUser.save();
     }
    
     async findUser(username: string): Promise<User | null> {
       return this.userModel.findOne({ username });
     }
    
     async findAllUsers(): Promise<User[]> {
       return this.userModel.find().exec();
     }
    
     async updateUser(username: string, user: User): Promise<User> {
       return this.userModel.findOneAndUpdate({ username }, user, { new: true }).exec();
     }
    
     async deleteUser(username: string): Promise<User | null> {
       return this.userModel.findOneAndRemove({ username }).exec();
     }
    }
  2. 创建一个新的控制器 user.controller.ts

    import { Controller, Post, Req, Body, Get, Param } from '@nestjs/common';
    import { UserService } from './user.service';
    import { User } from './user.interface';
    
    @Controller('users')
    export class UserController {
     constructor(private readonly userService: UserService) {}
    
     @Post('register')
     async registerUser(@Body() user: User): Promise<User> {
       return this.userService.createUser(user);
     }
    
     @Post('login')
     async loginUser(@Body() { username, password }): Promise<User | null> {
       const user = await this.userService.findUser(username);
       if (user && user.password === password) {
         return user;
       }
       return null;
     }
    
     @Get()
     async findAllUsers(): Promise<User[]> {
       return this.userService.findAllUsers();
     }
    
     @Get(':username')
     async findUser(@Param('username') username: string): Promise<User | null> {
       return this.userService.findUser(username);
     }
    
     @Post(':username/edit')
     async updateUser(@Param('username') username: string, @Body() user: User): Promise<User> {
       return this.userService.updateUser(username, user);
     }
    
     @Post(':username/delete')
     async deleteUser(@Param('username') username: string): Promise<User | null> {
       return this.userService.deleteUser(username);
     }
    }

通过这种方式,你可以在 Nest 中实现 RESTful API 的设计与实现,通过 HTTP 请求进行 CRUD 操作。

React与Nest项目集成

本节将详细介绍如何将 React 前端项目与 Nest 后端项目进行集成,包括跨域资源共享配置、数据交互与 API 调用、用户认证与授权实现。

跨域资源共享配置

为了使 React 前端项目能够与 Nest 后端项目进行通信,我们需要在 Nest 中配置跨域资源共享(CORS)。

  1. 创建一个新的中间件 cors.middleware.ts

    import { Injectable, NestMiddleware } from '@nestjs/common';
    import * as cors from 'cors';
    
    @Injectable()
    export class CorsMiddleware implements NestMiddleware {
     use(req: any, res: any, next: () => void) {
       res.header('Access-Control-Allow-Origin', '*');
       res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
       res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
       next();
     }
    }
  2. main.ts 文件中使用中间件:

    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    import { CorsMiddleware } from './cors.middleware';
    
    async function bootstrap() {
     const app = await NestFactory.create(AppModule);
     app.use(new CorsMiddleware().use);
     await app.listen(3000);
    }
    
    bootstrap();

数据交互与API调用

为了在 React 前端项目中调用 Nest 后端项目中的 API,我们需要使用 HTTP 客户端库,例如 axios

  1. 安装 axios:

    npm install axios
  2. 创建一个新的服务 api.service.ts

    import { Injectable } from '@nestjs/common';
    import axios from 'axios';
    
    @Injectable({
     providedIn: 'root',
    })
    export class ApiService {
     private readonly baseUrl = 'http://localhost:3000';
    
     async getHello(): Promise<string> {
       const { data } = await axios.get(`${this.baseUrl}/app`);
       return data;
     }
    
     async registerUser(user: User): Promise<User> {
       const { data } = await axios.post(`${this.baseUrl}/users/register`, user);
       return data;
     }
    
     async loginUser(username: string, password: string): Promise<User | null> {
       const { data } = await axios.post(`${this.baseUrl}/users/login`, { username, password });
       return data;
     }
    
     async findAllUsers(): Promise<User[]> {
       const { data } = await axios.get(`${this.baseUrl}/users`);
       return data;
     }
    
     async findUser(username: string): Promise<User | null> {
       const { data } = await axios.get(`${this.baseUrl}/users/${username}`);
       return data;
     }
    
     async updateUser(username: string, user: User): Promise<User> {
       const { data } = await axios.post(`${this.baseUrl}/users/${username}/edit`, user);
       return data;
     }
    
     async deleteUser(username: string): Promise<User | null> {
       const { data } = await axios.post(`${this.baseUrl}/users/${username}/delete`);
       return data;
     }
    }

通过这种方式,你可以在 React 中调用 Nest 后端项目的 API,并实现数据交互。

用户认证与授权实现

为了实现用户认证与授权,我们需要在 Nest 后端项目中实现认证逻辑,并在 React 前端项目中进行相应的处理。

  1. auth.service.ts 中实现认证逻辑:

    import { Injectable } from '@nestjs/common';
    import { Request } from 'express';
    import * as jwt from 'jsonwebtoken';
    
    const secret = 'secret';
    
    @Injectable()
    export class AuthService {
     generateToken(user: any) {
       return jwt.sign(user, secret, { expiresIn: '1h' });
     }
    
     validateToken(token: string) {
       try {
         return jwt.verify(token, secret);
       } catch (error) {
         return null;
       }
     }
    }
  2. auth.controller.ts 中实现登录功能:

    import { Controller, Post, Req, Body, HttpException, HttpStatus } from '@nestjs/common';
    import { AuthService } from './auth.service';
    import { UserService } from './user.service';
    import { User } from './user.interface';
    
    @Controller('auth')
    export class AuthController {
     constructor(private readonly authService: AuthService, private readonly userService: UserService) {}
    
     @Post('login')
     async login(@Req() req, @Body() { username, password }): Promise<{ token: string }> {
       const user = await this.userService.findUser(username);
       if (!user || user.password !== password) {
         throw new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
       }
       const token = this.authService.generateToken(user);
       return { token };
     }
    
     @Post('profile')
     async getProfile(@Req() req) {
       return req.user;
     }
    }
  3. user.service.ts 中实现用户服务:

    import { Injectable } from '@nestjs/common';
    import { InjectModel } from '@nestjs/mongoose';
    import { Model } from 'mongoose';
    import { User } from './user.interface';
    import { UserSchema } from './user.schema';
    
    @Injectable()
    export class UserService {
     constructor(@InjectModel(UserSchema.name) private userModel: Model<User>) {}
    
     async createUser(user: User): Promise<User> {
       const createdUser = new this.userModel(user);
       return createdUser.save();
     }
    
     async findUser(username: string): Promise<User | null> {
       return this.userModel.findOne({ username });
     }
    }
  4. user.controller.ts 中实现用户管理功能:

    import { Controller, Post, Req, Body, Get, Param } from '@nestjs/common';
    import { UserService } from './user.service';
    import { User } from './user.interface';
    import { AuthService } from './auth.service';
    
    @Controller('users')
    export class UserController {
     constructor(private readonly userService: UserService, private readonly authService: AuthService) {}
    
     @Post('register')
     async registerUser(@Body() user: User): Promise<User> {
       return this.userService.createUser(user);
     }
    
     @Get()
     async findAllUsers(@Req() req): Promise<User[]> {
       return this.userService.findAllUsers();
     }
    
     @Get(':username')
     async findUser(@Param('username') username: string, @Req() req): Promise<User | null> {
       return this.userService.findUser(username);
     }
    }
  5. app.module.ts 中提供 AuthServiceUserService

    import { Module } from '@nestjs/common';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    import { AuthService } from './auth.service';
    import { AuthController } from './auth.controller';
    import { UserSchema } from './user.schema';
    import { UserController } from './user.controller';
    import { UserService } from './user.service';
    import { MongooseModule } from '@nestjs/mongoose';
    import { CorsMiddleware } from './cors.middleware';
    
    @Module({
     imports: [
       MongooseModule.forRoot('mongodb://localhost:27017/mydb'),
       MongooseModule.forFeature([{ name: 'User', schema: UserSchema }]),
     ],
     controllers: [AppController, AuthController, UserController],
     providers: [AppService, AuthService, UserService, CorsMiddleware],
    })
    export class AppModule {}
  6. auth.service.ts 中使用 jsonwebtoken 进行认证:

    import { Injectable } from '@nestjs/common';
    import { Request } from 'express';
    import * as jwt from 'jsonwebtoken';
    
    const secret = 'secret';
    
    @Injectable()
    export class AuthService {
     generateToken(user: any) {
       return jwt.sign(user, secret, { expiresIn: '1h' });
     }
    
     validateToken(token: string) {
       try {
         return jwt.verify(token, secret);
       } catch (error) {
         return null;
       }
     }
    }
  7. auth.controller.ts 中实现登录功能:

    import { Controller, Post, Req, Body, HttpException, HttpStatus } from '@nestjs/common';
    import { AuthService } from './auth.service';
    import { UserService } from './user.service';
    import { User } from './user.interface';
    
    @Controller('auth')
    export class AuthController {
     constructor(private readonly authService: AuthService, private readonly userService: UserService) {}
    
     @Post('login')
     async login(@Req() req, @Body() { username, password }): Promise<{ token: string }> {
       const user = await this.userService.findUser(username);
       if (!user || user.password !== password) {
         throw new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
       }
       const token = this.authService.generateToken(user);
       return { token };
     }
    
     @Post('profile')
     async getProfile(@Req() req) {
       return req.user;
     }
    }
  8. user.controller.ts 中实现用户管理功能:

    import { Controller, Post, Req, Body, Get, Param } from '@nestjs/common';
    import { UserService } from './user.service';
    import { User } from './user.interface';
    import { AuthService } from './auth.service';
    
    @Controller('users')
    export class UserController {
     constructor(private readonly userService: UserService, private readonly authService: AuthService) {}
    
     @Post('register')
     async registerUser(@Body() user: User): Promise<User> {
       return this.userService.createUser(user);
     }
    
     @Get()
     async findAllUsers(@Req() req): Promise<User[]> {
       return this.userService.findAllUsers();
     }
    
     @Get(':username')
     async findUser(@Param('username') username: string, @Req() req): Promise<User | null> {
       return this.userService.findUser(username);
     }
    }

通过这种方式,你可以在 Nest 中实现用户认证与授权,并在 React 中进行相应的处理。

项目实战:构建一个简单的CRUD应用

本节将通过一个简单的 CRUD 应用来展示 React 和 Nest 的集成,并介绍前端设计、后端 API 设计、数据持久化与数据库操作、用户界面与后端交互优化。

前端设计与后端API设计

前端设计:

  1. 创建一个新的组件 Login.tsx

    import React, { useState } from 'react';
    import { useLogin } from '../hooks/useLogin';
    import { useNavigate } from 'react-router-dom';
    import { ApiService } from '../services/api.service';
    
    const Login: React.FC = () => {
     const [username, setUsername] = useState('');
     const [password, setPassword] = useState('');
     const navigate = useNavigate();
     const { login, loading, error } = useLogin();
    
     const handleSubmit = async (e: React.FormEvent) => {
       e.preventDefault();
       try {
         await login(username, password);
         navigate('/dashboard');
       } catch (err) {
         console.error(err);
       }
     };
    
     return (
       <div>
         <h2>Login</h2>
         <form onSubmit={handleSubmit}>
           <div>
             <label htmlFor="username">Username:</label>
             <input
               type="text"
               id="username"
               name="username"
               value={username}
               onChange={(e) => setUsername(e.target.value)}
             />
           </div>
           <div>
             <label htmlFor="password">Password:</label>
             <input
               type="password"
               id="password"
               name="password"
               value={password}
               onChange={(e) => setPassword(e.target.value)}
             />
           </div>
           <button type="submit" disabled={loading}>
             Login
           </button>
           {error && <p>{error}</p>}
         </form>
       </div>
     );
    };
    
    export default Login;
  2. 创建一个新的组件 Dashboard.tsx

    import React, { useEffect, useState } from 'react';
    import { useGetUsers } from '../hooks/useGetUsers';
    import { useNavigate } from 'react-router-dom';
    import { ApiService } from '../services/api.service';
    
    const Dashboard: React.FC = () => {
     const [users, setUsers] = useState<User[]>([]);
     const navigate = useNavigate();
     const { loading, error } = useGetUsers();
    
     useEffect(() => {
       if (error) {
         navigate('/login');
       }
     }, [error, navigate]);
    
     const fetchUsers = async () => {
       try {
         const fetchedUsers = await ApiService.findAllUsers();
         setUsers(fetchedUsers);
       } catch (err) {
         console.error(err);
       }
     };
    
     useEffect(() => {
       fetchUsers();
     }, []);
    
     return (
       <div>
         <h2>Dashboard</h2>
         <table>
           <thead>
             <tr>
               <th>Username</th>
               <th>Password</th>
             </tr>
           </thead>
           <tbody>
             {users.map((user) => (
               <tr key={user._id}>
                 <td>{user.username}</td>
                 <td>{user.password}</td>
               </tr>
             ))}
           </tbody>
         </table>
       </div>
     );
    };
    
    export default Dashboard;

后端 API 设计:

  1. 创建一个新的服务 auth.service.ts

    import { Injectable } from '@nestjs/common';
    import { Request } from 'express';
    import * as jwt from 'jsonwebtoken';
    
    const secret = 'secret';
    
    @Injectable()
    export class AuthService {
     generateToken(user: any) {
       return jwt.sign(user, secret, { expiresIn: '1h' });
     }
    
     validateToken(token: string) {
       try {
         return jwt.verify(token, secret);
       } catch (error) {
         return null;
       }
     }
    }
  2. 创建一个新的控制器 auth.controller.ts

    import { Controller, Post, Req, Body, HttpException, HttpStatus } from '@nestjs/common';
    import { AuthService } from './auth.service';
    import { UserService } from './user.service';
    import { User } from './user.interface';
    
    @Controller('auth')
    export class AuthController {
     constructor(private readonly authService: AuthService, private readonly userService: UserService) {}
    
     @Post('login')
     async login(@Req() req, @Body() { username, password }): Promise<{ token: string }> {
       const user = await this.userService.findUser(username);
       if (!user || user.password !== password) {
         throw new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
       }
       const token = this.authService.generateToken(user);
       return { token };
     }
    
     @Post('profile')
     async getProfile(@Req() req) {
       return req.user;
     }
    }
  3. 创建一个新的控制器 user.controller.ts

    import { Controller, Post, Req, Body, Get, Param } from '@nestjs/common';
    import { UserService } from './user.service';
    import { User } from './user.interface';
    import { AuthService } from './auth.service';
    
    @Controller('users')
    export class UserController {
     constructor(private readonly userService: UserService, private readonly authService: AuthService) {}
    
     @Post('register')
     async registerUser(@Body() user: User): Promise<User> {
       return this.userService.createUser(user);
     }
    
     @Get()
     async findAllUsers(@Req() req): Promise<User[]> {
       return this.userService.findAllUsers();
     }
    
     @Get(':username')
     async findUser(@Param('username') username: string, @Req() req): Promise<User | null> {
       return this.userService.findUser(username);
     }
    }

数据持久化与数据库操作

数据持久化:

  1. 创建一个新的服务 user.service.ts

    import { Injectable } from '@nestjs/common';
    import { InjectModel } from '@nestjs/mongoose';
    import { Model } from 'mongoose';
    import { User } from './user.interface';
    import { UserSchema } from './user.schema';
    
    @Injectable()
    export class UserService {
     constructor(@InjectModel(UserSchema.name) private userModel: Model<User>) {}
    
     async createUser(user: User): Promise<User> {
       const createdUser = new this.userModel(user);
       return createdUser.save();
     }
    
     async findUser(username: string): Promise<User | null> {
       return this.userModel.findOne({ username });
     }
    
     async findAllUsers(): Promise<User[]> {
       return this.userModel.find().exec();
     }
    }
  2. 创建一个新的模式 user.schema.ts

    import { Schema, Document } from 'mongoose';
    import { User } from './user.interface';
    
    export const UserSchema: Schema = new Schema<User>({
     username: { type: String, required: true },
     password: { type: String, required: true },
    });
  3. main.ts 文件中配置数据库连接:

    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    import { CorsMiddleware } from './cors.middleware';
    import { MongooseModule } from '@nestjs/mongoose';
    
    async function bootstrap() {
     const app = await NestFactory.create(AppModule);
     app.use(new CorsMiddleware().use);
     await app.listen(3000);
    }
    
    bootstrap();
  4. app.module.ts 文件中导入并提供服务:

    import { Module } from '@nestjs/common';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    import { AuthService } from './auth.service';
    import { AuthController } from './auth.controller';
    import { UserService } from './user.service';
    import { UserController } from './user.controller';
    import { MongooseModule } from '@nestjs/mongoose';
    import { UserSchema } from './user.schema';
    import { CorsMiddleware } from './cors.middleware';
    
    @Module({
     imports: [
       MongooseModule.forRoot('mongodb://localhost:27017/mydb'),
       MongooseModule.forFeature([{ name: 'User', schema: UserSchema }]),
     ],
     controllers: [AppController, AuthController, UserController],
     providers: [AppService, AuthService, UserService, CorsMiddleware],
    })
    export class AppModule {}

用户界面与后端交互优化

用户界面优化:

  1. 创建一个新的组件 UserForm.tsx

    import React, { useState } from 'react';
    import { useRegisterUser } from '../hooks/useRegisterUser';
    import { useNavigate } from 'react-router-dom';
    import { ApiService } from '../services/api.service';
    
    const UserForm: React.FC = () => {
     const [username, setUsername] = useState('');
     const [password, setPassword] = useState('');
     const [loading, setLoading] = useState(false);
     const [error, setError] = useState('');
     const navigate = useNavigate();
     const { registerUser } = useRegisterUser();
    
     const handleSubmit = async (e: React.FormEvent) => {
       e.preventDefault();
       setLoading(true);
       try {
         await registerUser(username, password);
         setError('');
         navigate('/dashboard');
       } catch (err) {
         setError(err.message);
       } finally {
         setLoading(false);
       }
     };
    
     return (
       <div>
         <h2>Register User</h2>
         <form onSubmit={handleSubmit}>
           <div>
             <label htmlFor="username">Username:</label>
             <input
               type="text"
               id="username"
               name="username"
               value={username}
               onChange={(e) => setUsername(e.target.value)}
             />
           </div>
           <div>
             <label htmlFor="password">Password:</label>
             <input
               type="password"
               id="password"
               name="password"
               value={password}
               onChange={(e) => setPassword(e.target.value)}
             />
           </div>
           <button type="submit" disabled={loading}>
             Register
           </button>
           {error && <p>{error}</p>}
         </form>
       </div>
     );
    };
    
    export default UserForm;
  2. 创建一个新的组件 UserList.tsx

    
    import React, { useEffect, useState } from 'react';
    import { useGetUsers } from '../hooks/useGetUsers';
    import { useNavigate } from 'react-router-dom';
    import { ApiService } from '../services/api.service';
    
    const UserList: React.FC = () => {
     const [users, setUsers] = useState<User[]>([]);
     const navigate = useNavigate();
     const { loading, error } = useGetUsers();
    
     useEffect(() => {
       if (error) {
         navigate('/login');
       }
     }, [error, navigate]);
    
     const fetchUsers = async () => {
       try {
         const fetchedUsers = await ApiService.findAllUsers();
         setUsers(fetchedUsers);
       } catch (err) {
         console.error(err);
       }
     };
    
     useEffect(() => {
       fetchUsers();
     }, []);
    
     return (
       <div>
         <h2>User List</h诋毁
打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP