Built-in Basic Objects of the Framework
12003In this chapter, we will get a preliminary understanding of some basic objects built into the framework. These objects include four objects (Application, Context, Request, Response) inherited from [Koa], as well as other objects extended by the framework (Controller, Service, Helper, Config, Logger). We will frequently encounter them in the subsequent documentation.
Application
The Application is the global application object. In an application, each process will instantiate only one Application instance. It inherits from Koa.Application, on which we can mount some global methods and objects. We can easily extend the Application object in plugins or applications.
Events
During the operation of the framework, some events will be triggered on the Application instance, which application developers or plugin developers can listen to for certain operations. As application developers, we generally listen in the custom startup script:
server: This event will be triggered only once in a worker process. After the HTTP service has completed startup, it will expose the HTTP server to developers through this event.error: Whenever an exception is captured at runtime, theerrorevent will be triggered, exposing the error object and associated context (if any), allowing developers to perform custom logging, reporting, etc.requestandresponse: When the application receives a request and responds to a request, therequestandresponseevents will be triggered respectively, exposing the context of the current request, allowing developers to listen to these two events for logging.
// app.js
module.exports = (app) => {
app.once('server', (server) => {
// websocket related operations
});
app.on('error', (err, ctx) => {
// report error
});
app.on('request', (ctx) => {
// log received request
});
app.on('response', (ctx) => {
// ctx.starttime is set by the framework
const used = Date.now() - ctx.starttime;
// log total request time
});
};Access Methods
The Application object can be accessed in almost any scenario when writing applications. Here are a few common access methods:
Almost all files loaded by the framework's Loader (Controller, Service, Schedule, etc.) can obtain the Application instance by exporting a function that will be called by Loader, passing app as a parameter:
-
// app.js module.exports = (app) => { app.cache = new Cache(); }; -
// app/controller/user.js class UserController extends Controller { async fetch() { this.ctx.body = this.app.cache.get(this.ctx.query.id); } }
Similar to Koa, the Application object can be accessed via ctx.app on the Context object. For example, in a Controller file:
// app/controller/user.js
class UserController extends Controller {
async fetch() {
this.ctx.body = this.ctx.app.cache.get(this.ctx.query.id);
}
}In instances that inherit from the Controller and Service base classes, the Application object can be accessed via this.app.
// app/controller/user.js
class UserController extends Controller {
async fetch() {
this.ctx.body = this.app.cache.get(this.ctx.query.id);
}
}Context
The Context is a request-level object that inherits from [Koa.Context]. Each time a user request is received, the framework will instantiate a Context object. This object encapsulates the information of the user request and provides many convenient methods to obtain request parameters or set response information. The framework will mount all [Service] instances onto the Context instance. Some plugins will also mount other methods and objects onto it (e.g., [egg-sequelize] will mount all models onto the Context).
Access Methods
The most common way to access the Context instance is within [Middleware], [Controller], and [Service]. We have already seen the corresponding access method in the Controller example. The method of obtaining the Context in Service is the same as in Controller, while the method of obtaining the Context instance in Middleware is consistent with the usage in [Koa] framework middleware.
The framework's [Middleware] supports both Koa v1 and Koa v2 middleware syntax. Depending on the syntax used, the way to obtain the Context instance varies slightly:
// Koa v1
function* middleware(next) {
// this is the instance of Context
console.log(this.query);
yield next;
}
// Koa v2
async function middleware(ctx, next) {
// ctx is the instance of Context
console.log(ctx.query);
}In addition to obtaining the Context instance when handling requests, there are also scenarios outside of user requests where we need to access objects on the Context instance, such as service/model. In this case, we can create an anonymous Context instance using the Application.createAnonymousContext() method:
// app.js
module.exports = (app) => {
app.beforeStart(async () => {
const ctx = app.createAnonymousContext();
// Preload before application starts
await ctx.service.posts.load();
});
};In scheduled tasks, each task receives a Context instance as a parameter, allowing us to conveniently execute some scheduled business logic:
// app/schedule/refresh.js
exports.task = async (ctx) => {
await ctx.service.posts.refresh();
};Request & Response
Request is a request-level object that inherits from [Koa.Request]. It encapsulates the native HTTP Request object of Node.js and provides a series of helper methods to obtain commonly used HTTP request parameters.
Response is a request-level object that inherits from [Koa.Response]. It encapsulates the native HTTP Response object of Node.js and provides a series of helper methods to set HTTP responses.
Access Methods
You can access the current request's Request (ctx.request) and Response (ctx.response) instances on the Context instance.
// app/controller/user.js
class UserController extends Controller {
async fetch() {
const { app, ctx } = this;
// Get the `id` parameter from the request
const id = ctx.request.query.id;
// Set the response body
ctx.response.body = app.cache.get(id);
}
}[Koa]will proxy some methods and properties from Request and Response on the Context, see[Koa.Context].- As in the example above,
ctx.request.query.idandctx.query.idare equivalent, andctx.response.body =andctx.body =are also equivalent. - It is important to note that to obtain the POST body, you should use
ctx.request.body, notctx.body.
Controller
The framework provides a Controller base class and recommends that all Controllers inherit from this base class. This Controller base class has the following properties:
ctx- The current request'sContextinstance.app- The application'sApplicationinstance.config- The application's configuration.service- All services of the application.logger- Theloggerobject encapsulated for the currentController.
In the Controller file, you can reference the Controller base class in two ways:
// app/controller/user.js
// Get from egg (recommended)
const Controller = require('egg').Controller;
class UserController extends Controller {
// implementation
}
module.exports = UserController;
// Get from app instance
module.exports = (app) => {
return class UserController extends app.Controller {
// implementation
};
};Service
The framework provides a Service base class and recommends that all Services inherit from this base class.
The properties of the Service base class are consistent with those of the Controller base class, and the access method is similar:
// app/service/user.js
// Get from egg (recommended)
const Service = require('egg').Service;
class UserService extends Service {
// implement
}
module.exports = UserService;
// Get from app instance
module.exports = (app) => {
return class UserService extends app.Service {
// implement
};
};Helper
Helper is used to provide some utility functions. Its purpose is to allow us to extract some common actions into an independent function in helper.js. This way, we can write complex logic using JavaScript, avoiding logic being scattered in various places, and making it easier to write test cases.
Helper itself is a class with the same properties as the Controller base class, and it will also be instantiated on each request. Therefore, all functions on Helper can also access the context information related to the current request.
Access Methods
You can access the current request's Helper (ctx.helper) instance on the Context instance.
// app/controller/user.js
class UserController extends Controller {
async fetch() {
const { app, ctx } = this;
const id = ctx.query.id;
const user = app.cache.get(id);
ctx.body = ctx.helper.formatUser(user);
}
}In addition, the Helper instance can also be accessed in templates. For example, you can use the shtml method provided by the security plugin in templates.
<!-- app/view/home.nj -->
{{ helper.shtml(value) }}Custom Helper Methods
In application development, we may often need to customize some Helper methods. For example, in the above example, formatUser can be customized through framework extension.
// app/extend/helper.js
module.exports = {
formatUser(user) {
return only(user, ['name', 'phone']);
},
};Config
We recommend that application development follow the principle of separating configuration and code, placing some business configurations that need hard coding into configuration files. At the same time, configuration files support different configurations for various runtime environments, making them very convenient to use. All framework, plugin, and application-level configurations can be accessed through the Config object. For details on framework configuration, you can read the Config Configuration section.
Access Methods
We can access the config object from the Application instance via app.config, and we can also access the config object via this.config on the instances of Controller, Service, and Helper.
Logger
The framework has a powerful built-in logging feature, which makes it very convenient to print various levels of logs to the corresponding log files. Each logger object provides methods for four levels:
logger.debug()logger.info()logger.warn()logger.error()
The framework provides multiple Logger objects, and below we briefly introduce the access methods and usage scenarios for each Logger object.
App Logger
We can access it via app.logger. If we want to do some application-level logging, such as recording some data information during the startup phase or logging some business information unrelated to requests, we can accomplish this through the App Logger.
App CoreLogger
We can access it via app.coreLogger. Generally, when developing applications, we should not print logs through CoreLogger. However, the framework and plugins need to use it to print application-level logs, which helps to clearly distinguish between application and framework logs. Logs printed through CoreLogger will be placed in a different file than those from Logger.
Context Logger
We can access it from the Context instance via ctx.logger. From the access method, we can see that the Context Logger is certainly related to requests. The logs it prints will be prefixed with some information related to the current request (e.g., [$userId/$ip/$traceId/${cost}ms $method $url]). With this information, we can quickly locate requests from the logs and link all logs within a single request.
Context CoreLogger
We can access it via ctx.coreLogger. The difference from Context Logger is that generally only plugins and the framework will use it to log.
Controller Logger and Service Logger
We can access them via this.logger on Controller and Service instances. They are essentially a Context Logger, but when printing logs, they will additionally include the file path to facilitate locating the log printing position.
Subscription Model
The subscription model is a relatively common development pattern, such as consumers of message middleware or scheduled tasks. Therefore, we provide a Subscription base class to standardize this pattern.
You can reference the Subscription base class as follows:
const Subscription = require('egg').Subscription;
class Schedule extends Subscription {
// This method needs to be implemented
// subscribe can be an async function or a generator function
async subscribe() {}
}Plugin developers can customize subscription specifications based on their needs, such as scheduled tasks implemented using this specification.
Related Links:
- koa
- koa.application
- koa.context
- koa.request
- koa.response
- egg-sequelize
- Middleware
- Controller
- Service