Skip to content

控制器

控制器负责处理传入请求并向客户端返回响应。

控制器的目的是接收应用程序的特定请求。路由机制控制哪个控制器接收哪些请求。通常,每个控制器都有多个路由,并且不同的路由可以执行不同的操作。

装饰器纯平台特定的对象
@Request(), @Req()req
@Response(), @Res()*res
@Next()next
@Session()req.session
@Param(key?: string)req.params / req.params[key]
@Body(key?: string)req.body / req.body[key]
@Query(key?: string)req.query / req.query[key]
@Headers(name?: string)req.headers / req.headers[name]
@Ip()req.ip
@HostParam()req.hosts
ts
import {
  Controller,
  Get,
  Post,
  Query,
  Body,
  Param,
  Headers,
  HttpCode,
} from '@nestjs/common';
import { UserService } from './user.service';
// import { CreateUserDto } from './dto/create-user.dto';
// import { UpdateUserDto } from './dto/update-user.dto';

/**
 * 可以通过以下方式来指定此控制器下所有路由的版本号:
 * @Controller({
 *  path: 'user',
 *  version: '1',
 * })
 *
 * @export
 * @class UserController
 */
@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get()
  // get读取信息用query
  findAll(@Query() query) {
    console.log(query);
    return {
      code: 200,
      message: query.name,
    };
  }

  @Post()
  // post读取信息用body
  create(@Body() body) {
    console.log(body);
    return {
      code: 200,
      message: '名字' + body.name + '年龄' + body.age,
    };
  }

  // get 动态路由测试
  @Get(':id')
  // @HttpCode(500) 控制状态码
  findId(@Param('id') param, @Headers() headers) {
    console.log(param, headers.cookie);
    return {
      code: 200,
    };
  }
}

Get请求

get动态路由

Post请求

生成验证码和session以及验证

后端nest框架

先要安装svg-captcha,用于生成验证码图片:

shell
pnpm install svg-captcha

然后再main.ts中去配置一下session:

ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { VersioningType } from '@nestjs/common';
import * as session from 'express-session';
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableVersioning({
    type: VersioningType.URI,
  });
  app.use(
    session({
      secret: 'Rarrot',
      rolling: true,
      name: 'Rarrot.sid',
      cookie: { maxAge: 666666 }, //过期时间
    }),
  );
  await app.listen(3000);
}
bootstrap();

而后在session-test.service.ts中写一个service,配置生成的验证码图片的样式:

ts
import { Injectable, Req } from '@nestjs/common';
import * as svgCaptcha from 'svg-captcha';//引入svg-captcha

@Injectable()
export class SessionTestService {
  createCode() {
    const Captcha=svgCaptcha.create({
      size: 4,
      noise: 2,
      color: true,
      fontSize: 50,
      width:100,
      height:34,
      background: '#e0e8f6',
    });
    return Captcha;
  }
}

再在session-test.controller.ts中定义get和post接口:

ts
import { Controller, Get, Post, Body, Patch, Param, Delete, Req, Res, Session } from '@nestjs/common';
import { SessionTestService } from './session-test.service';

@Controller('session-test')
export class SessionTestController {
  constructor(private readonly sessionTestService: SessionTestService) { }

  @Get('code')
  createCode(@Req() req: any, @Res() res: any, @Session() session: any) {
    // 拿到svg-captcha的返回值,text是验证码的值,data是svg的图片
    const capture = this.sessionTestService.createCode();
    // 将验证码的值存入session
    session.code = capture.text;
    // 设置响应头
    res.type('image/svg+xml');
    // 传入验证码的svg图片
    res.send(capture.data);
  }

  @Post('create')
  createUser(@Body() body, @Session() session: any) {
    // 判断验证码是否正确
    console.log(body,session.code) 
    if (session.code.toLocaleLowerCase() !== body?.code?.toLocaleLowerCase()) {
      return {
        code: 400,
        message: '验证码错误',
      };
    } else {
      return {
        code: 200,
        message: '登录成功',
        session: session
      };
    }
  }
}

前端vue框架

vue
<template>
  <div class="wraps">
    <el-form :label-position="labelPosition" label-width="100px" :model="formLabelAlign" style="max-width: 460px">
      <el-form-item label="账号">
        <el-input v-model="formLabelAlign.name" />
      </el-form-item>
      <el-form-item label="密码">
        <el-input type="password" v-model="formLabelAlign.password" />
      </el-form-item>
      <el-form-item label="验证码">
        <div style="display:flex">
          <el-input v-model="formLabelAlign.code" />
          <img @click="resetCode" :src="codeUrl" alt="">
        </div>
      </el-form-item>
      <el-form-item>
        <el-button @click="submit">登录</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script setup lang='ts'>
import { onMounted, reactive, ref } from 'vue';
// 如果使用的是axios,默认是不打开的,需要使用axios.defaults.withcredentials = true才能携带cookie
// 但是这样会导致跨域,所以需要后端设置允许跨域,也可以使用代理,但是代理需要配置
// 也可以使用jsonp,但是jsonp需要后端设置;
// 也可以使用nginx反向代理,但是需要配置;
// 也可以使用cors,但是需要后端设置;
// 也可以使用fetch 但是fetch需要设置mode: 'cors'。

// 以下代码已经在vite.config.ts设置了跨域,所以直接连接url,后端返回svg图片
const codeUrl = ref<string>('/api/session-test/code')

// 重新获取验证码
const resetCode = () => {
  codeUrl.value = '/api/session-test/code?' + Math.random()
}

const labelPosition = ref<string>('right')

const formLabelAlign = reactive({
  name: "",
  password: "",
  code: ""
})


// 接口请求
const submit = () => {
  fetch('/api/session-test/create', {
    method: "POST",
    body: JSON.stringify(formLabelAlign),
    // 这里需要设置content-type,否则后端无法解析就收不到数据
    headers: {
      'content-type': 'application/json'
    }
  }).then(res => res.json()).then(res => {
    console.log(res)
  })
}

</script>

<style>
* {
  padding: 0;
  margin: 0;
}

.wraps {
  display: flex;
  justify-content: center;
  align-items: center;
  height: inherit;
}

html,
body,
#app {
  height: 100%;
}
</style>

前端本地环境下跨域:

ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
})

Released under the MIT License.