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/web | Required ,Midway EggJS adaptation layer |
---|---|
@midwayjs/core | Required ,Midway core package |
egg | Required ,EggJS dependent package, and other capabilities such as definition. |
egg-scripts | Optional ,EggJS startup script |
@midwayjs/egg-ts-helper | Optional ,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
- starting from v3, midway provides more components, and most egg built-in plug-ins are disabled by default
- The baseDir is adjusted to
src
directory by default, and the server isdist
directory.
- The baseDir is adjusted to
- 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}.ts | For more information about how to write a configuration file, see Configuration. |
src/config/plugin.js | For more information, see Plug-ins. |
test/** | For more information, see Unit testing. |
src/app.js and src/agent.js | It 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.
appInfo | Description |
---|---|
pkg | package.json |
name | Application name, same as pkg.name |
baseDir | The src (locally developed) or Dist (after online) Directory of the application code |
appDir | Directory of application code |
HOME | The user directory, for example, the admin account is/home/admin. |
root | The root directory of the application is only baseDir in local and unittest environments, and the others are HOME. |
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);
}
}
@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);
};
}
}
Notice
If you want to continue to use the traditional functional writing method of EggJS, you must put the file under
src/app/middleware
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:
- middleware in the egg framework
- the order in which egg plug-ins are added through config.coreMiddleware
- The order in which the business code is configured in config.middleware
- 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:
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 requirements | Environment | errorPageUrl whether to configure | Return content |
---|---|---|---|
HTML & TEXT | local & unittest | - | Onerror comes with an error page that displays detailed error information |
HTML & TEXT | Other | Yes | Redirect to errorPageUrl |
HTML & TEXT | Other | No | onerror comes with a simple error page without error information (not recommended) |
JSON & JSONP | local & unittest | - | JSON object or corresponding JSONP format response with detailed error information |
JSON & JSONP | Other | - | 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 {
// ...
}
}
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=*********",
}
*********
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.
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 Item | Type | Description |
---|---|---|
port | number | Required, Started Port |
key | string | Buffer |
cert | string | Buffer |
ca | string | Buffer |
hostname | string | The hostname of the listener, the default 127.1 |
http2 | boolean | Optional, supported by http2, default false |
queryParseMode | simple|extended | The default is extended |
queryParseOptions | qs.IParseOptions | Parse options when 'simple' mode is used |
The above attributes are valid for applications deployed locally and using bootstrap.js
.
Modify port
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 ",
},
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. You can use any to bypass.
await (this.app as any).mysql.query(sql);
Or you can add extended definitions by yourself.
6、Get Http Server
The original HttpServer is sealed inside Eggjs and needs to be accessed through events.
// src/configuration.ts
import { Configuration, App } from '@midwayjs/core';
import { Application } from '@midwayjs/web';
@Configuration(/***/)
export class MainConfiguration {
@App('egg')
app: Application;
// ...
async onServerReady() {
this.app.once('server', (server) => {
// ...
})
}
}