For you who are new to using nestj as a project (three articles: writing API & uploading pictures)

Time:2021-10-18

For you who are new to using nestj as a project (three articles: writing API & uploading pictures)

Module 1.

Like we wrote beforeuserController for(controller)And services(service)In fact, they should be regarded as a whole. For example, they should be introduced somewhereuserRelated operations, direct introductionUser's moduleOK, let’s start from scratchmoduleTry it.

nest g module modules/users
nest g controller modules/users
nest g service modules/users

For you who are new to using nestj as a project (three articles: writing API & uploading pictures)

For you who are new to using nestj as a project (three articles: writing API & uploading pictures)

2、 Various request modes

To get the parameters of different request methods, you need to usenestDifferent decorators provided, such asgetTo request this explicit parameter, you need to use@QueryDecorator treatment.

get
import { Controller, Get, Query} from '@nestjs/common';
// ...
@Get()
getList(@Query() query) {
    return query;
}

For you who are new to using nestj as a project (three articles: writing API & uploading pictures)

post
import { Controller, Post, Body } from '@nestjs/common';
// ...
@Post('create')
create(@Body() body) {
    return body
}

For you who are new to using nestj as a project (three articles: writing API & uploading pictures)

Interline ID
import { Controller, Get, Param } from '@nestjs/common';
// ...
@Get(':id')
getUser(@Param('id') id): string {
    return id;
}

For you who are new to using nestj as a project (three articles: writing API & uploading pictures)

3、 Public path

It is necessary to set the prefix of the API path. We will set it as/api/v1, in/share/src/main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.setGlobalPrefix('api/v1'); //  Here, here
  await app.listen(3000);
}
bootstrap();

For you who are new to using nestj as a project (three articles: writing API & uploading pictures)

4、 Decorator

In some cases, we may need to preprocess the parameters. For example, the API parameters of the request list must containpageAndpageSizeAnd the minimum is 1.

We aresrcFile plus createdecoratorInside the folder ispaging.decorator.tsFile.

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const Paging = createParamDecorator(
    (data: string, ctx: ExecutionContext) => {
        const request = ctx.switchToHttp().getRequest();
        const query = request.query;
        
        if (!query.page || query.page < 1) {
            query.page = 1
        }

        if (!query.pageSize || query.pageSize < 1) {
            query.pageSize = 1
        }

        return query
    },
);

We transform the originalgetListMethods, replacing@Query() queryby@Paging() queryAnd view the results;

    @Get()
    getList(@Paging() query) {
        return query;
    }

For you who are new to using nestj as a project (three articles: writing API & uploading pictures)

5、 Pipeline

The pipeline converts the input data into the required data output and verifies the input data. If the verification succeeds and continues to pass, an exception will be thrown if the verification fails. The pipeline can handle more things than the decorator. For example, the decorator mentioned above is more for parameters, and the pipeline is the whole request.

Let’s use the pipeline to verify the page and PageSize. SRC is created belowpipeInside the folder ispaging.pipe.tsThe contents of the document are as follows.

import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';

@Injectable()
export class PagingPipe implements PipeTransform {
    transform(query: any, metadata: ArgumentMetadata) {
        if (metadata.type === 'query') {
            if (!query.page || query.page < 1) {
                query.page = 1
            }

            if (!query.pageSize || query.pageSize < 1) {
                query.pageSize = 1
            }
        }
        return query;
    }
}

Here, you need to use metadata to determine which requester will process it. Its usage is as follows:

import { Controller, Get, Query, UsePipes } from '@nestjs/common';
@Controller('users')
export class UsersController {

    @Get()
    @UsePipes(new PagingPipe())
    getList(@Query() query) {
        return query;
    }
}
  1. Need to rely onUsePipesDecorator.
  2. It acts on the overall request and does not target a parameter.
  3. Pipes can have multiple settings, such as @UsePipes(new PagingPipe(), new PagingPipe2())Execute from left to right.

6、 Middleware

This is an old friend. I won’t introduce it much. Let’s directly introduce the configuration method and create it in the SRC directorymiddlewareFolder, here isglobal.middleware.tsThe contents are as follows:

export default function (req, res, next) {
    Console.log ('global function: Enter ');
    next();
    Console.log ('global function: exit ';
};
Global use

staymain.tsIt can be used directly inside

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import globalMiddleware from './middleware/global.middleware';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.setGlobalPrefix('api/v1');
  app.use(globalMiddleware)
  await app.listen(3000);
}
bootstrap();
Local use, only route is/usersTo take effect

Createusers.middleware.tsDocument, which reads as follows:

import { Injectable, NestMiddleware } from '@nestjs/common';

@Injectable()
export class UsersMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    Console.log ('enter users middleware ')
    next();
    Console.log ('Get out of the users middleware ')
  }
}

stay/share/src/app.module.tsThe following modifications are made in the document:

import { Module, MiddlewareConsumer } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { GitlabController } from './modules/gitlab/gitlab.controller';
import { GitlabService } from './modules/gitlab/gitlab.service';
import { UsersModule } from './modules/users/users.module';
import { UsersMiddleware } from './middleware/users.middleware';

@Module({
  imports: [UsersModule],
  controllers: [AppController, GitlabController],
  providers: [AppService, GitlabService],
})
export class AppModule {
  //Middleware is defined here
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(UsersMiddleware)
      .forRoutes('/users*');
  }
}
  1. If inmain.tsIt defines global middleware, which can only be functional middleware.
  2. consumerMultiple can be spliced at the back.apply
  3. If.forRoutes('/users*');finish writing sth..forRoutes('users*');Will report an error.
  4. stayforRoutesDirect configuration insideforRoutes('*')It is the global intermediate key, and there is no need for functional middleware.

7、 Guard

The guard is similar to the intermediate key. It can be executed before processing the request. The difference between the guard and the intermediate key is that the middleware calls next but does not know what to execute next, but the guard can know what to execute next. The guard is generally used for permission verification.

Next, we will use JWD to do a simple and easy to understand verification, so the practice is on the official websiteSimplified version

Installation dependency

yarn add @nestjs/jwt

Generation module

nest g module modules/auth
nest g controller modules/auth
nest g service modules/auth

stayshare/src/modules/auth/auth.controller.ts, configure the interface for generating token.

import { Controller, Get, Response } from '@nestjs/common';
import { AuthService } from './auth.service'

@Controller('auth')
export class AuthController {
    constructor(
        private readonly authService: AuthService
    ) { }

    @Get()
    getToken(@Response() res) {
        return this.authService.getToken(res);
    }
}

stay/share/src/modules/auth/auth.module.tsdefinitionjwtStrategies, such as expiration time, etc.

import { Module } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { JwtModule } from '@nestjs/jwt';

@Module({
  imports: [
    JwtModule.register({
      secret: 'secretKey',
      signOptions: { expiresIn: `${60 * 60 * 24 * 10}s` },
    }),
  ],
  controllers: [AuthController],
  providers: [AuthService],
  Exports: [authservice], // note here that it will be used globally later, so you need to export it
})
export class AuthModule { }

stay/share/src/modules/auth/auth.service.tsDefine the method of generating token.

import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
    constructor(
        private readonly jwtService: JwtService
    ) { }
    getToken(res) {
        res.setHeader("token", this.jwtService.sign({
            id: 'dadscxciweneiu122',
            Name: "golden hair"
        }))
        res.send()
    }
}
  1. Use abovejwtServiceThe sig method of generates a token, and it is better not to return it directly in the parameter, but in the header.
  2. The object in sig is the data to be encrypted. We can put some user IDs in it.
  3. exports: [AuthService]It’s important because it’s going to be introduced into the guard.
Guard configuration

/share/src/guard/token.guard.ts

import { CanActivate, ExecutionContext, Injectable, HttpException } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Reflector } from '@nestjs/core';
import { Inject } from '@nestjs/common';

@Injectable()
export class TokenGuard implements CanActivate {
  constructor(
    private readonly reflector: Reflector,
    @Inject('AuthService') private readonly authService,
  ) { }
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    try {
      const user = this.authService.testToken(request)
      request.user = user;
      return true
    } catch (error) {
      throw new HttpException({
        status: 401,
        Error: 'authentication failed',
      }, 401);
    }
  }
}
  1. The example here is that after parsing the token, we will find the user information according to the userid in the token, and then give the user information on the request, so that each operation can use the user information for authentication and other operations.
  2. HttpExceptionAn error type. If the verification fails, we directly report 401.

Use guards globally,/share/src/app.module.tsAdd the following changes:

import { APP_GUARD } from '@nestjs/core';
import { TokenGuard } from './guard/token.guard';
// ...
@Module({
  imports: [UsersModule, AuthModule],
  controllers: [AppController, GitlabController],
  providers: [AppService, GitlabService,
    {
      provide: APP_GUARD,
      useClass: TokenGuard,
    }]
})

8、 Detect token

Of course, the generated token needs to be verified, that is, the abovetestTokenImplementation of method/share/src/modules/auth/auth.service.ts:

testToken(req) {
     const token = req.headers.token;
     return this.jwtService.verify(token)
     //The user information will be found and returned after the database is linked
}

9、 Set token without verification

Many APIs do not restrict users to login status, so we need to set a decorator that makes requests do not need verification. For example, the operation of obtaining token does not need authentication. The usage is shown in the following figure.

For you who are new to using nestj as a project (three articles: writing API & uploading pictures)

/share/src/guard/noauth.ts

import { SetMetadata } from '@nestjs/common';

export const NoAuth = () => SetMetadata('no-auth', true);
  1. SetMetadataset upmetadata, metadata is the data used to describe the data, which can be understood as explaining what the requested data is for.
  2. ‘no auth’ is set to true.

/share/src/guard/token.guard.tsofcanActivateAdd judgment in the method

import { CanActivate, ExecutionContext, Injectable, HttpException } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Reflector } from '@nestjs/core';
import { Inject } from '@nestjs/common';

@Injectable()
export class TokenGuard implements CanActivate {
  constructor(
    private readonly reflector: Reflector,
    @Inject('AuthService') private readonly authService,
  ) { }
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    // const headers = request.headers;
    const noAuth =
      this.reflector.get<boolean>('no-auth', context.getHandler());
    if (noAuth) {
      return true;
    }
    //Unauthorized
    try {
      const user = this.authService.testToken(request)
      request.user = user;
      return true
    } catch (error) {
      throw new HttpException({
        status: 401,
        Error: 'authentication failed',
      }, 401);
    }
  }
}
  1. Reflex extractionno-auth
  2. context.getHandler()Returned is[Function: testToken]That is, the methods in the services we perform.
  3. You can also usecontext.getClass()The return value is [class AuthController]The controller where our current request resides.
  4. For insurance purposes, it can be written as follows:

     const noAuth =
       this.reflector.get<boolean>('no-auth', context.getClass()) ||
       this.reflector.get<boolean>('no-auth', context.getHandler());

10、 Picture upload & display

Browse pictures

Create folder/share/publicPut a picture inside.
Inmain.tsThe following code is added to the file:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NestExpressApplication } from '@nestjs/platform-express'
import globalMiddleware from './middleware/global.middleware';

async function bootstrap() {
 // const app = await NestFactory.create(AppModule); //  This change
 const app = await NestFactory.create<NestExpressApplication>(AppModule);
 app.setGlobalPrefix('api/v1');
 app.use(globalMiddleware);
 app.useStaticAssets('public', {
   Prefix: '/ static' // must not omit '/'
 });
 await app.listen(3000);
}
bootstrap();

Let’s look at the effect:
For you who are new to using nestj as a project (three articles: writing API & uploading pictures)

For you who are new to using nestj as a project (three articles: writing API & uploading pictures)

Upload pictures

Get the file directly, and then store it in the form of stream. Don’t mention it here. Directly demonstrate the function of receiving and uploading pictures with decorator.

import { Controller, BadRequestException, Post UploadedFile, UseInterceptors } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { join } from 'path';
import multer = require('multer');

@Controller('user')
export class UserController {
    @Post('img:import')
    @UseInterceptors(
        FileInterceptor('file', {
            storage: multer.diskStorage({
                destination: function (req, file, cb) {
                    // cb(null, join(process.cwd(), 'upload'));
                    cb(null, join(process.cwd(), 'public'));
                },
                filename: function (req, file, cb) {
                    const unique = `${Date.now()}${Math.round(Math.random() * 1e9)}`;
                    const imgPath = `${unique}.${file.mimetype.split('/')[1]}`;
                    cb(null, imgPath);
                },
            }),
            limits: {
                fileSize: 1024 * 1024,
            },
            fileFilter(req, file, cb) {
                if (file.mimetype !== 'image/jpeg' && file.mimetype !== 'image/png') {
                    Throw new badrequestexception ('Only JPG and PNG formats are supported ');
                }
                cb(null, true);
            },
        }),
    )
    async coverImport(@UploadedFile() file) {
        return { url: `/static/${file.filename}` };
    }
}
  1. destinationSet the storage path inside.
  2. filenameName the file here. Don’t forget to add a suffix.
  3. limitsThat’s the size.
  4. fileFilterDo some filtering operations and throw errors.
  5. Finally, the relative path is returned to the client.
  6. This way of writing is not beautiful, so it is not necessary to use this way.

For you who are new to using nestj as a project (three articles: writing API & uploading pictures)

end.

The next article is to usetypeormIn the actual battle of operating the database, I will share many real cases that were not displayed on the official website. Some practical problems caused me a lot of trouble at that time. I hope to make progress with you.

Recommended Today

Playing network video on Android

This example shares the specific code of playing network video on Android for your reference. The specific contents are as follows PlayVideoActivity.java package cn.edu.zufe.app002; import android.Manifest; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.Toast; import android.widget.VideoView; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; public class PlayVideoActivity extends AppCompatActivity implements View.OnClickListener{ […]