Aller au contenu principal
Version: 3.0.0

EggJS

Midway can use EggJS as the upper-level Web framework. EggJS provides many commonly used plug-ins and API to help users quickly build enterprise-level Web applications. This chapter mainly introduces how EggJS uses its own capabilities in Midway.

Description
Contains independent main framework
Contains independent logs

Installation dependency

$ npm i @midwayjs/web@3 egg --save
$ npm i @midwayjs/egg-ts-helper --save-dev

For the EggJS scenario, these packages are listed below.

  "dependencies": {
"@midwayjs/web": "^3.0.0",
"@midwayjs/core": "^3.0.0",
"egg": "^2.0.0 ",
"egg-scripts": "^2.10.0"
},
"devDependencies": {
"@midwayjs/egg-ts-helper": "^1.0.1 ",
},
@midwayjs/webRequired ,Midway EggJS adaptation layer
@midwayjs/coreRequired ,Midway core package
eggRequired ,EggJS dependent package, and other capabilities such as definition.
egg-scriptsOptional ,EggJS startup script
@midwayjs/egg-ts-helperOptional ,EggJS defines the generation tool.

Examples can also be created directly using scaffolding.

# npm v6
$ npm init midway --type=egg-v3 my_project

# npm v7
$ npm init midway -- --type=egg-v3 my_project

Open the component

import { Configuration, App } from '@midwayjs/core';
import * as web from '@midwayjs/web';
import { join } from 'path';

@Configuration({
imports: [web]
importConfigs: [join(__dirname, './config')]
})
export class MainConfiguration {
@App()
app: web.Application;

async onReady() {
// ...
}
}

The difference from the default EggJS

    1. starting from v3, midway provides more components, and most egg built-in plug-ins are disabled by default
    1. The baseDir is adjusted to src directory by default, and the server is dist directory.
    1. disable egg-logger, replace all with @midwayjs/logger, and cannot switch

The entire architecture is as follows:

Directory structure

In addition to the directory structure provided by Midway, EggJS also has some special directory structures (immutable). The entire structure is as follows.

➜  my_midway_app tree
.
├── src
| ├── app.ts ## EggJS Extended Worker Lifecycle File (optional)
| ├── agent.ts ## EggJS Extended Agent Lifecycle File (Optional)
| ├── app ## EggJS fixed root directory (optional)
| │ ├── public ## The default directory for EggJS static hosting plug-ins (available)
| │ | └── reset.css
| │ ├── view (optional) ## ## The default directory for EggJS template rendering (available)
| │ | └── home.tpl
| │ └── extend (optional) ## EggJS 扩展目录(可配)
| │ ├── helper.ts (optional)
| │ ├── request.ts (optional)
| │ ├── response.ts (optional)
| │ ├── context.ts (optional)
| │ ├── application.ts (optional)
| │ └── agent.ts (optional)
| │
| ├── config
| | ├── plugin.ts
| | ├── config.default.ts
| │ ├── config.prod.ts
| | ├── config.test.ts (可选)
| | ├── config.local.ts (可选)
| | └── config.unittest.ts (可选)
│ ├── controller ## Midway controller directory (recommended)
│ ├── service ## Midway service directory (recommended)
│ └── schedule

├── typings ## EggJS defines the generation directory.
├── test
├── package.json
└── tsconfig.json

The above is a complete picture of the directory structure of EggJS, which contains many specific directories of EggJS, some of which have been replaced by corresponding capabilities in the Midway system and can be replaced directly. The entire structure is basically equivalent to moving the directory structure of EggJS to the src directory.

Since EggJS is a convention-based framework, the directory structure of the entire project is fixed. Here are some commonly used convention directories.

src/app/public/**Optional. For more information, see egg-static.
src/config/config.{env}.tsFor more information about how to write a configuration file, see Configuration.
src/config/plugin.jsFor more information, see Plug-ins.
test/**For more information, see Unit testing.
src/app.js and src/agent.jsIt is used to customize initialization during startup. Optional. For more information, see Start customization. For more information about the role of agent.js, see [Agent mechanism](https://eggjs.org/zh-cn/core/cluster-and-ipc.html#agent-%E6%9C%BA%E5%88% B6).

Configuration Definition

Midway provides the standard TS configuration writing of EggJS in the scaffold. The MidwayConfig includes the definition and attribute tips of the configuration in egg. The structure is as follows.

// src/config/config.default.ts
import { MidwayConfig, MidwayAppInfo } from '@midwayjs/core';

export default (appInfo: MidwayAppInfo) => {
return {
// use for cookie sign key, should change to your own and keep security
keys: appInfo.name + '_xxxx',
egg: {
port: 7001
},
// security: {
// csrf: false
// },
} as MidwayConfig;
};

In the form of this return method, it will be automatically executed during the run-time and merged into the complete configuration object.

The parameter of this function is of MidwayAppConfig type and the value is the following.

appInfoDescription
pkgpackage.json
nameApplication name, same as pkg.name
baseDirThe src (locally developed) or Dist (after online) Directory of the application code
appDirDirectory of application code
HOMEThe user directory, for example, the admin account is/home/admin.
rootThe root directory of the application is only baseDir in local and unittest environments, and the others are HOME.
info

Note that the baseDir here is different from the appDir and EggJS applications.

Using Egg plugin

Plugin are one of EggJS's features. @midwayjs/web also supports EggJS's plug-in system, but in the case of Midway components, Midway components are used as much as possible.

Plug-ins are generally reused by npm modules.

$ npm i egg-mysql --save

Then, you must declare that it is enabled in the src/config/plugin.js of the application or framework.

If there is an export default, please write it in it.

import { EggPlugin } from 'egg';
export default {
static: false, // default is true
mysql: {
enable: true
package: 'egg-mysql'
}
} as EggPlugin;

If there is no export default, you can export it directly.

// src/config/plugin.ts
// Use mysql plug-in
export const mysql = {
enable: true
package: 'egg-mysql',
};

After opening the plug-in, we can use the functions provided by the plug-in in the business code. Generally, the plug-in mounts the object to the app and ctx of EggJS, and then uses it directly.

app.mysql.query(sql, values);           // Methods provided by egg

In Midway, you can use @App to obtain the app object, and in the request scope, you can use @Inject() ctx to obtain the ctx object, so you can obtain the plug-in object by injection.

import { Provide, Inject, Get } from '@midwayjs/core';
import { Application, Context } from '@midwayjs/web';

@Provide()
export class HomeController {

@App()
app: Application;

@Inject()
ctx: Context;

@Get('/')
async home() {
This. app.mysql.query (SQL, values); // Call methods on app (if any)
This. ctx.mysql.query (SQL, values); // Call the method mounted on ctx (if any)
}
}

In addition, you can directly inject plugins mounted by app through the @Plugin decorator. By default, if no parameters are passed, the property name will be used as the key.

import { Provide, Get, Plugin } from '@midwayjs/core';

@Provide()
export class HomeController {

@Plugin()
mysql: any;

@Get('/')
async home() {
this.mysql.query( SQL, values);
}
}
info

@Plugin() mysql is equivalent to app.mysql. The function of @Plugin is to take the plug-in corresponding to the attribute name from the app object. Therefore, @Plugin() xxx is equal to app['xxx'].

Web middleware

The middleware sample is as follows:

import { Middleware, IMiddleware } from '@midwayjs/core';
import { Context, NextFunction } from '@midwayjs/web';

@Middleware()
export class ReportMiddleware implements IMiddleware<Context, NextFunction> {

resolve() {
return async (ctx: Context, next: NextFunction) => {
const startTime = Date.now();
await next();
console.log(Date.now() - startTime);
};
}

}
attention

Notice

  1. If you want to continue to use the traditional functional writing method of EggJS, you must put the file under src/app/middleware

  2. The built-in middleware that comes with egg has been integrated

Application Middleware.

// src/configuration.ts
import { App, Configuration } from '@midwayjs/core';
import * as egg from '@midwayjs/web';
import { ReportMiddleware } from './middleware/user.middleware';

@Configuration({
imports: [egg]
// ...
})
export class MainConfiguration {

@App()
app: egg.Application;

async onReady() {
this.app.useMiddleware(ReportMiddleware);
}
}

For more information, see Web middleware.

Middleware sequence

Since egg also has its own middleware logic, in the new version, we have done a certain processing of the middleware loading sequence, and the execution sequence is as follows:

    1. middleware in the egg framework
    1. the order in which egg plug-ins are added through config.coreMiddleware
    1. The order in which the business code is configured in config.middleware
    1. the order in which App. useMiddleware is added

Because midway's middleware will be post-loaded, we can customize sorting in the onReady.

BodyParser

the bodyParser feature of the egg. by default, it parses post requests and automatically identifies the json and form types.

If you need text or xml, you can configure it yourself.

The default size is limited to 1mb. You can set the size of each item separately.

// src/config/config.default
export default {
// ...
bodyParser: {
formLimit: '1mb',
jsonLimit: '1mb',
textLimit: '1mb',
xmlLimit: '1mb',
},
}

Note that the type selection when using Postman for Post requests:

postman

Schedule

For more information about the start of v3, see bull components.

To be compatible with the previous egg scheduled tasks, follow the following steps.

First install midway-schedule dependencies.

$ npm i midway-schedule --save

Add to the plug-in.

// src/config/plugin.ts
export default {
schedule: true
schedulePlus: {
enable: true
package: 'midway-schedule',
}
}

Please refer to the previous version of the document.

Log

Since v3 cannot use egg-logger, see Logs.

Exception handling

EggJS framework provides a unified error handling mechanism through onerror plug-ins, which will be used as Midway's bottom error logic and will not conflict with error filters.

Any exception thrown in all processing methods (Middleware, Controller, Service) for a request will be captured by it and will automatically return different types of errors (based on Content Negotiation) according to the type the request wants to obtain.

Format of request requirementsEnvironmenterrorPageUrl whether to configureReturn content
HTML & TEXTlocal & unittest-Onerror comes with an error page that displays detailed error information
HTML & TEXTOtherYesRedirect to errorPageUrl
HTML & TEXTOtherNoonerror comes with a simple error page without error information (not recommended)
JSON & JSONPlocal & unittest-JSON object or corresponding JSONP format response with detailed error information
JSON & JSONPOther-JSON object or corresponding JSONP format response, without detailed error information

errorPageUrl attribute is supported in the configuration of the onerror plug-in. When the errorPageUrl is configured, once the user requests an exception to the HTML page applied online, it will be redirected to this address.

In src/config/config.default.ts

// src/config/config.default.ts
module.exports = {
onerror: {
// When an exception occurs on the online page, redirect to this page
errorPageUrl: '/50x.html',
},
};

Extended Application/Context/Request/Response

Add extension logic

Although MidwayJS do not want to mount the attribute directly to koa's Context and App (which will cause uncertainty in management and definition), this function of EggJS is still available.

The file location is as follows.

➜  my_midway_app tree
.
├── src
│ ├── app
│ │ └── extend
│ │ ├── application.ts
│ │ ├── context.ts
│ │ ├── request.ts
│ │ └── response.ts
│ ├── config
│ └── interface.ts
├── test
├── package.json
└── tsconfig.json

The content is the same as the original EggJS.

// src/app/extend/context.ts
export default {
get hello() {
return 'hello world';
},
};

Add extended definition

Context please use Midway to extend, please check the extended context definition.

For the rest, please expand in src/interface.ts.

// src/interface.ts
declare module 'egg' {
interface Request {
// ...
}
interface Response {
// ...
}
interface Application {
// ...
}
}
info

Do not place the definition of business custom extensions under the root directory to avoid overwriting by ts-helper tools.``

Use egg-scripts deployment

Since EggJS provides the default multi-process deployment tool egg-scripts, Midway also continues to support this method. If the upper layer is EggJS, this deployment method is recommended.

First, in the dependency, ensure that the egg-scripts package is installed.

$ npm i egg-scripts --save

Add npm scripts to package.json:

After the above code is built, use our start and stop commands to start and stop.

"scripts": {
"start": "egg-scripts start --daemon --title=********* --framework=@midwayjs/web",
"stop": "egg-scripts stop --title=*********",
}
info

********* is your project name.

Note: egg-scripts has limited support for Windows systems, see #22.

Start Parameters

$ egg-scripts start --port=7001 --daemon --title=egg-server-showcase

Copy

As shown in the above example, the following parameters are supported:

  • `--port=7001 Port number, the environment variable process.env.PORT will be read by default, if not passed, the framework's built-in port 7001 will be used.`
  • Whether --daemon is allowed in the background mode without nohup. If Docker is used, it is recommended to run directly at the foreground.
  • --env=prod running environment of the framework. By default, the environment variable process.env.EGG_SERVER_ENV will be read. If it is not passed, the built-in environment prod of the framework will be used.
  • --workers=2 Number of Worker threads in the framework. By default, the number of app workers equivalent to the number of CPU cores will be created, which can make full use of CPU resources.
  • --title=egg-server-showcase is used to facilitate grep in ps processes. the default value is egg-server-${appname}.
  • --framework=yadan If the application uses a custom framework, you can configure the egg.framework of the package.json or specify this parameter.
  • --ignore-stderr.
  • --https.key specifies the full path of the key file that is required for HTTPS.
  • --https.cert specifies the full path of the certificate file required for HTTPS.
  • All egg-cluster Options support transparent transmission, such as -- port, etc.

For more parameters, see the egg-scripts and egg-cluster documents.

info

Logs deployed using egg-scripts are stored in the user directory , such as /home/xxxx/logs.

Startup environment

The original egg uses EGG_SERVER_ENV as an environmental sign, please use MIDWAY_SERVER_ENV in Midway.

State type definition

There is a special State attribute in the Context of koa at the bottom of egg. The State definition can be extended in a similar way to Context.

// src/interface.ts

declare module '@midwayjs/web/dist/interface' {
interface Context {
abc: string;
}

interface State{
bbb: string;
ccc: number;
}
}

Configuration

Default configuration

// src/config/config.default
export default {
// ...
egg: {
port: 7001
},
}

All parameters of @midwayjs/web are as follows:

Configuration ItemTypeDescription
portnumberRequired, Started Port
keystringBuffer
certstringBuffer
castringBuffer
hostnamestringThe hostname of the listener, the default 127.1
http2booleanOptional, supported by http2, default false
queryParseModesimple|extendedThe default is extended
queryParseOptionsqs.IParseOptionsParse options when 'simple' mode is used

The above attributes are valid for applications deployed locally and using bootstrap.js.

Modify port

astuce

Note that this method will only take effect for projects developed locally and deployed using bootstrap.js files.

By default, we provide the 7001 default port parameter in config.default. by modifying it, we can modify the default port of egg http service.

For example, we changed it to 6001:

// src/config/config.default
export default {
// ...
egg: {
port: 6001
},
}

By default, our port configuration is null because the single-test environment requires supertest to start the port.

// src/config/config.unittest
export default {
// ...
egg: {
port: null
},
}

In addition, you can also temporarily modify the port by midway-bin dev-ts-port = 6001, which overwrites the configured port.

Global prefix

For more information about this feature, see [Global Prefixes](../controller# Global Routing Prefix).

Https configuration

In most cases, please use external agents as much as possible to complete the implementation of Https, such as Nginx.

In some special scenarios, you can directly turn on Https by configuring SSL certificates (TLS certificates).

First, you must prepare certificate files in advance, such as ssl.key and ssl.pem. The key is the private key of the server and the pem is the corresponding certificate.

Then configure it.

// src/config/config.default
import { readFileSync } from 'fs';
import { join } from 'path';

export default {
// ...
egg: {
key: join(__dirname, '../ssl/ssl.key')
cert: join(__dirname, '../ssl/ssl.pem')
},
}

favicon settings

By default, the browser will initiate a request to favicon.ico.

// src/config/config.default
import { readFileSync } from 'fs';
import { join } from 'path';

export default {
// ...
siteFile: {
'/favicon.ico': readFileSync(join(__dirname, 'favicon.png'))
},
}

If the @midwayjs/static-file component is turned on, static file hosting of the component will be preferred.

Modify context log

The context log of the egg framework can be modified individually.

export default {
egg: {
contextLoggerFormat: info => {
const ctx = info.ctx;
return '${info.timestamp} ${info.LEVEL} ${info.pid} [${ctx.userId} - ${Date.now() - ctx.startTime}ms ${ctx.method}] ${info.message}';
}
// ...
},
};

Query array parsing

By default, ctx.query parses to ignore arrays, while ctx.queries strictly turns all fields into arrays.

If you adjust the queryParseMode, you can make ctx.query a structure between the two (the result of the querystring).

// src/config/config.default
export default {
// ...
egg: {
// ...
queryParseMode: 'simple',
queryParseOptions: {
arrayLimit: 100,
},
},
}

Common problem

1. generate ts definition

Midway provides the @midwayjs/egg-ts-Hepler toolkit for quickly generating the definitions that EggJS depends on when developing.

$ npm install @midwayjs/egg-ts-helper --save-dev

Add the corresponding ets command to package.json. Generally speaking, we will add it before the dev command to ensure the correctness of the code.

  "scripts": {
"dev": "cross-env ets && cross-env NODE_ENV=local midway-bin dev --ts ",
},
info

Before writing code for the first time, you need to execute this command once to have ts definition generation.

EggJS-generated definitions are in the typings directory.

➜  my_midway_app tree
.
├── src ## midway project source code
├── typings ## EggJS defines the generation directory.
├── test
├── package.json
└── tsconfig.json

2. Special Situation of Configuration in EggJS

Under EggJS, the life cycle in configuration.ts will only be loaded and executed under worker. If you have similar requirements on the Agent, use the agent.ts of EggJS.

3. Asynchronous initialization configuration cannot override plug-in configuration

onConfigLoad the lifecycle is executed after the egg plug-in (if any) is initialized, it cannot be used to override the configuration used by the egg plug-in.

4. default csrf error

In the post request, especially the first time, the user will find a csrf error. the reason is that egg-security is built into the security plug-in in the framework by default, and csrf verification is enabled by default.

We can turn it off in the config, but better to go to Learn about it and then make a selection.

export const security = {
csrf: false
};

5. There is no definition problem

Some egg plug-ins do not provide ts definitions, resulting in undeclared methods, such as egg-mysql. image.png You can use any to bypass.

await (this.app as any).mysql.query(sql);

Or you can add extended definitions by yourself.