Midway.js opens up WebSocket front-end and back-end listening channels

Hello, If you like my article or want to join a big company, you can follow the public account “Quantum Frontend”, which will push good front-end articles and share employment information and tips from time to time. I also hope to have the opportunity. One-on-one help to realize your dreams

Foreword

The WebSocket protocol allows persistent connections between the client and the server. This persistent connection feature makes WebScoket particularly suitable for games or chat rooms, as well as for orders. Scenario of monitoring status changes and refreshing the page after submission.

Midway provides support and encapsulation for the ws module, which can quickly create WebSocket services.

Server-side implementation

We use Midway.js to implement server-side capabilities. First, create a project:

npm init midway@latest -y

Then install the dependency package of WebSocket in the project:

npm i @midwayjs/ws@3 --save
npm i @types/ws --save-dev

Enable the WebSocket component in /src/configuration.ts:

// src/configuration.ts
import {<!-- --> Configuration } from '@midwayjs/core';
import * as ws from '@midwayjs/ws';

@Configuration({<!-- -->
  imports: [ws],
  // ...
})
export class MainConfiguration {<!-- -->
  async onReady() {<!-- -->
        // ...
  }
}

Then we started to write the interface, first creating the socket directory in the project directory:

├── package.json
├── src
│ ├── configuration.ts ## Entry configuration file
│ ├── interface.ts
│ └── socket ## ws service file
│ └── hello.controller.ts
├── test
├── bootstrap.js ## Service startup entrance
└── tsconfig.json

Define the WebSocket service through the @WSController decorator:

import {<!-- --> WSController } from '@midwayjs/core';

@WSController()
export class HelloSocketController {<!-- -->
  // ...
}

When a client connects, the connection event will be triggered. We can use the @OnWSConnection() decorator in the code to decorate a method. When each client first This method will be called automatically when connecting to the service for the first time.

import {<!-- --> WSController, OnWSConnection, Inject } from '@midwayjs/core';
import {<!-- --> Context } from '@midwayjs/ws';
import * as http from 'http';

@WSController()
export class HelloSocketController {<!-- -->

  @Inject()
  ctx: Context;

  @OnWSConnection()
  async onConnectionMethod(socket: Context, request: http.IncomingMessage) {<!-- -->
    console.log(`namespace / got a connection ${<!-- -->this.ctx.readyState}`);
  }
}

WebSocket obtains data through event listening. Midway provides the @OnWSMessage() decorator to format received events. Every time the client sends an event, the decorated method will be executed.

import {<!-- --> WSController, OnWSMessage, Inject } from '@midwayjs/core';
import {<!-- --> Context } from '@midwayjs/ws';

@WSController()
export class HelloSocketController {<!-- -->

  @Inject()
  ctx: Context;

  @OnWSMessage('message')
  async gotMessage(data) {<!-- -->
    return {<!-- --> name: 'harry', result: parseInt(data) + 5 };
  }
}

We can send messages to all connected clients through the @WSBroadCast decorator.

import {<!-- --> WSController, OnWSConnection, Inject } from '@midwayjs/core';
import {<!-- --> Context } from '@midwayjs/ws';

@WSController()
export class HelloSocketController {<!-- -->

  @Inject()
  ctx: Context;

  @OnWSMessage('message')
  @WSBroadCast()
  async gotMyMessage(data) {<!-- -->
    return {<!-- --> name: 'harry', result: parseInt(data) + 5 };
  }

  @OnWSDisConnection()
  async disconnect(id: number) {<!-- -->
    console.log('disconnect ' + id);
  }
}

At this point, the basic WebSocket interface has been developed.

  • When the client connects, we print namespace / got a connection;
  • When the client sends a message, we will return to the client a result based on the number of the input parameters plus 5;
  • When the client disconnects, we print disconnect;

Finally, we open a WebSocket service on the server side:

/src/config/config.default.ts

// src/config/config.default
export default {<!-- -->
  // ...
  webSocket: {<!-- -->
    port: 3000,
  },
}

Next, let’s call the front end to test.

Front-end implementation

The front-end uses react. We create a new page and enable the ws service in useEffect:

 useEffect(async () => {<!-- -->
    const ws = new WebSocket(`ws://localhost:9999`);

    ws.onopen = () => {<!-- -->
      console.log('Connection successful');
      ws.send(1);
    };
    ws.onmessage = (e) => {<!-- -->
      console.log('Server response:', e);
    };
    ws.onclose = (e) => {<!-- -->
      console.log('Close the connection, server response:', e)
    }
  }, []);

In this way, we can print out the results returned by the server on the front end. At the same time, the server obtains the log information, and the WebSocket links of the front and back ends are opened.

front end:

image.png

The webSocket service is enabled:

image.png

rear end:

image.png

Order refresh

Next, we simulate a scenario: after the front-end submits the order, it reaches the order details page. It needs to obtain the order status in real time. If the order application is passed, the success message will be displayed. The database will not be connected here, and an object will be maintained directly on the server side to simulate :

import {<!-- -->
  WSController,
  Inject,
  OnWSConnection,
  OnWSMessage,
  OnWSDisConnection,
} from '@midwayjs/core';
import {<!-- --> Context } from '@midwayjs/ws';
import * as http from 'http';

const orderInfo = {<!-- -->
  status: 'pending',
  id: 1,
  // ...orderInfo
};

@WSController()
export class HelloSocketController {<!-- -->
  @Inject()
  ctx: Context;

  //The client connects for the first time
  @OnWSConnection()
  async onConnectionMethod(socket: Context, request: http.IncomingMessage) {<!-- -->
    this.ctx.logger.info(`namespace / got a connection ${<!-- -->this.ctx.readyState}`);
    setTimeout(() => {<!-- -->
      orderInfo['status'] = 'success';
    }, 3000);
  }

  //Receive client message
  @OnWSMessage('message')
  async gotMessage() {<!-- -->
    if (orderInfo.status === 'success') {<!-- -->
      return {<!-- --> result: true };
    }
    return {<!-- --> result: false };
  }

  //Client disconnects
  @OnWSDisConnection()
  async disconnect(id: number) {<!-- -->
    console.log('disconnect ' + id);
  }
}
  • Mock an orderInfo on the backend;
  • When WebSocket is connected, delay three seconds to change the status of orderInfo to simulate an algorithm review time;
  • Respond to the new status to the front end after three seconds;

Front-end modification:

useEffect(async () => {<!-- -->
    const ws = new WebSocket(`ws://localhost:9999`);

    ws.onopen = () => {<!-- -->
      console.log('Connection successful');
      timer.current = setInterval(() => {<!-- -->
        ws.send(1);
      }, 1000);
    };
    ws.onmessage = (e) => {<!-- -->
      console.log('Server response:', e);
      if (JSON.parse(e.data).result) {<!-- -->
        setOrderPass(true);
      }
    };
    ws.onclose = (e) => {<!-- -->
      console.log('Close the connection, server response:', e);
    };
  }, []);

  return <>Order status: {<!-- -->orderPass ? 'Passed' : 'Applying'}</>;

After the transformation, the front-end page is refreshed, and the order status is asynchronously changed to passed three seconds later, achieving the effect of page monitoring and real-time refresh. Is this effect better than the traditional Http scheduled polling?

image.png

End

This article uses Midway0~1 to open up the front-end and back-end ws links, and finally demonstrates the advantages of ws and its comparison with traditional round-robin through actual business scenarios. differences in inquiry plans.

If you like my article or want to join a big company, you can follow the public account “Quantum Frontend”, which will push good front-end articles and share employment information and tips from time to time. I also hope to have the opportunity to help you realize your dream one-on-one.