preface

By 2021, NodeJS is playing a pivotal role in the web development field. It is often used in the development of various business middleware and the development of building tools and plug-ins. Its Web frameworks, such as Express, KOA, Fastify, Egg, Midway, Nest, etc., also have certain application space. This article will focus on the application value of NodeJS in the field of Web MVC framework based on the common MVC framework features. Hope to be interested in their own packaging framework of children’s shoes help!

The body of the

The core functions of common MVC framework are as follows: 1. Unified processing mechanism of request/response; 2. 2. Static resource mapping. 3. HTTP request distribution. This article will elaborate on the above three problem points, respectively about the solution!

1 Unified request/response processing mechanism

This problem is implemented using the HTTP module interception mechanism. The interception prototype is as follows:

function createServer(requestListener? : RequestListener): Server; function createServer(options: ServerOptions, requestListener? : RequestListener): Server; type RequestListener = (req: IncomingMessage, res: ServerResponse) => void;Copy the code

The createServer function accepts a request listener that contains request and response objects. You can define the interceptor’s uniform processing of the request and response, as follows:

export default function interceptor(req, res) { console.log(`request ${req.url} is targeted! `.green.bold); Res.setheader (" access-control-allow-origin ", "*"); res.setHeader("Access-Control-Allow-Headers", "*"); res.setHeader("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS"); }Copy the code

2 Static resource mapping

Before mapping static resources, you need to distinguish between interface requests and resource requests. For example, interface requests are forwarded to business codes for processing, and resource requests are returned after reading local files. Therefore, the following issues need to be addressed:

(1) Distinguish between interface requests and resource requests

The simple distinction can be distinguished by the URL prefix. The request that starts with/API is an interface request; otherwise, it is a resource request.

Export default async function(req, res) {try {if (req.url.startsWith('/ API ')) { Res)} else {// Resource request... } } catch (error) { console.error(error); res.statusCode = 500; Res.end (' server exception '); }}Copy the code

Resource requests can be subdivided into back-end resource requests and front-end resource requests. Therefore, you need to add response configuration items for processing.

let path; const urlObj = new URL(req.url, globalThis.$HOST); If (req.url.startswith ('upload')) {// Path = join(CWD (), urlobj.pathName); } else {// Front end resource request let pathname = urlObj. Pathname === '/'? '/index.html' : urlObj.pathname; path = join(cwd(), config.views, pathname); }Copy the code
(2) Set the appropriate ContentType for the resource request

Content-type mapping table can obtain the common file suffix and the corresponding contentType, and the tool class can handle the response.

async function setContentType(path, res) { try { let contentType; if (path.includes('.')) { const suffix = '.' + path.split('.')[1]; metaData[suffix] ? (contentType = metaData[suffix]) : (contentType = metaData['.*']); } if (contentType === 'text/html' || contentType === 'text/plain') { contentType += '; charset=utf-8'; } if (contentType) { res.setHeader('Content-Type', contentType); } } catch (error) { console.error(error) } }Copy the code

3 Distributing HTTP requests

(1) Write the request distribution function
async function dispatchActions(req, res) {
    const urlObj = new URL(req.url, globalThis.$HOST);
    const api = urlObj.pathname.split('/api')[1];
    if (getControllerExport()[api]) {
        await getControllerExport()[api](req, res);
    } else  {
        res.statusCode = 404;
        res.end('not found');
    } 
}
export function setControllerExport(data) {
    globalThis.$CONTROLLER = data;
}

export function getControllerExport() {
    return globalThis.$CONTROLLER;
}
Copy the code
(2) Read the default export of other files under the same file as webpack require.context function
// controll/index.mjs async function autoImpotController(dir, exclude) { let result = {}; const files = await readdir(dir); if (files? .length) { for (let i = 0; i < files.length; i++) { if (exclude ! == files[i]) { const module = await import('./' + files[i]); result = {... result, ... module.default}; } } } setControllerExport(result); } const fileUrl = import.meta.url; const dir = dirname(fileURLToPath(fileUrl)); const exclude = basename(fileURLToPath(fileUrl)); autoImpotController(dir, exclude);Copy the code
(3) writing the controller
// controll/user.mjs const base="/user"; const controllers = { getUsername : async function (req, res) { try { res.setHeader('Content-Type', 'application/json; charset=utf-8'); res.end(getJsonResult(true, 'getUsername')); } catch (error) { throw new Error(error); } } } const ept = {}; Object.keys(controllers).forEach(controller => { ept[`${base}/${controller}`] = controllers[controller]; }) export default ept;Copy the code

4 Annotation-based Controller development

A review of the data shows that typescript-based annotation development is in the experimental stage, while the ECMAScript annotation proposal is in the second stage. See github.com/tc39/propos… , this article temporarily do not explore, interested in children’s shoes can study below! The final controller should be written like this:

@controller({baes: 'uesr'})
class UserController {
     @Request({path: '/user', method: 'GET', contentType: 'application/json;charset=utf-8'})
     async getUsername() {
        try {
            res.setHeader('Content-Type', 'application/json;charset=utf-8');
            res.end(getJsonResult(true, 'getUsername'));
        } catch (error) {
            throw new Error(error);
        }
     }
     
}
Copy the code

Welcome to the original link

Welcome to my blog – Simple encapsulation of HTTP modules using NodeJS impersonation Express