node.js && egg.js

EggNode.js 社区广泛使用的框架,简洁且扩展性强,按照固定约定进行开发,低协作成本。

node.js 是一个异步的世界
egg.js核心的是Middleware、Koa中间件选择的是洋葱模型

Egg.js框架中,ctx 是一个非常核心且常用的对象,全称为 Context,它代表了当前 HTTP 请求的上下文。ctx 对象封装了关于当前请求的所有相关信息,并提供了丰富的操作方法,使得开发者能够便捷地处理请求、响应、中间件逻辑、数据校验、模板渲染等与请求生命周期相关的任务。

ctx.request ctx.response
ctx.path: 当前请求的路径。
ctx.routerPath: 不包含查询参数的请求路径。
ctx.method: 请求的HTTP方法(GET、POST、PUT、DELETE等)。

ctx.app: 访问到Egg应用实例,可用于操作全局应用级别的资源或配置。
ctx.cookies: 操作cookies,如读取、设置、删除。
ctx.session: 用于会话管理,支持读取、设置、销毁会话数据。

ctx.logger: 提供日志记录功能,可以根据需要记录不同级别的日志信息(debug、info、warn、error等)。
ctx.onerror(err): 用于捕获并处理在当前请求生命周期内发生的未被捕获的错误。 服务与依赖注入:

ctx.service: 访问服务层(Service)的快捷方式,用于调用业务逻辑相关的函数。
ctx.helper: 访问框架提供的辅助工具或自定义的帮助函数。

ctx.locals: 用于在中间件或控制器之间共享数据,生命周期仅限于单个请求。

在Egg.js的应用中,ctx 通常作为控制器(Controller)方法的参数传递,使得开发者可以直接在控制器中操作和响应当前请求。例如:

module.exports = {
  async list(ctx) {
    const userId = ctx.query.userId;
    const users = await ctx.service.user.list(userId);
    ctx.body = { users };
  },
};

在这个例子中,ctx 被用来获取请求查询参数(ctx.query.userId),调用服务层方法(ctx.service.user.list()),以及设置响应体(ctx.body)。这种设计使得代码逻辑清晰、模块化,并且易于维护和测试。

总之,ctx 在 Egg.js 中是处理 HTTP 请求的核心上下文对象,它集中了所有与当前请求相关的信息和操作,极大地简化了Web应用的开发过程。

参考文章 https://www.zhihu.com/column/c_1613568973672493056

八大模块:Controller、路由、Service、MiddleWare、数据库、View层使用EJS模版渲染、cookie、session、

1 简单Controller

控制器Controller,解析用户输入,处理后返回结果;
在代理服务器中,controller将用户请求转发到其他服务器上,并将服务结果返回给用户;

app下文件夹有controller文件夹和router.js文件
controller文件夹下有home.js文件
在这里插入图片描述
home.js文件

const Controller = require('egg').Controller;
class HomeController extends Controller {
  async index() {
    this.ctx.body = 'Hello World';
  }
}

module.exports = HomeController;

router.js文件

module.export = (app) => {
  // app指的是Egg.js的应用实例
  const { router, controller } = app;
  router.get('/', controller.home.index);
}

2 增加路由参数

2.1 query参数

'use strict';

const Controller = require('egg').Controller;
class GetController extends Controller {
  async getInfo() {
    const { ctx } = this;
    const { name, age } = ctx.query;
    ctx.body = {
      name, age,
    };
  }
}

module.exports = GetController;
router.get('/getInfo'. controller.get.getInfo);

在这里插入图片描述

2.2 params参数

  async getDetail() {
    const { ctx } = this;
    const { name, age } = ctx.params;
    ctx.body = {
      name, age,
    };
  }
router.get('/getDetail/:name/:age'. controller.get.getDetail);

在这里插入图片描述

2.3 post 请求

const Controller = require('egg').Controller;
class PostController extends Controller {
  async postParmas() {
    const { ctx } = this;
    const { name, age } = ctx.request.body;
    ctx.response.body = {
      name, age,
    };
  }
}

module.exports = PostController;
router.post('/postParams'. controller.post.postParmas);

限制body体内容大小
app文件夹下的config文件夹下的config.default.js文件

const config = export = {
  bodyParser: {
    jsonLimit: '1mb',
    formLimit: '1mb',
  },
};

3 service

service在复杂业务中用来做业务封装的抽象层;
service 在app文件夹下;
app文件夹下建立一个service文件夹下;
service文件夹下新建一个new.js 文件;

const Service = require('egg').Service;
class NewService extends Service {
  async getNewInfo(name, age) {
    return {
      name,
      age,
      id: 1,
      arr: ['1', '2', '3'],
    };
  }
}

module.exports = NewService;
const Controller = require('egg').Controller;
class NewController extends Controller {
  async newIndex() {
    const { ctx } = this;
    ctx.body = '这是一个新的controller';
  }

  async getNewInfo() {
    const { ctx } = this;
    const { name, age } = ctx.query;
    const data = await ctx.service.new.getNewInfo(name, age);
    ctx.body = data;
  }
}

module.exports = NewController;
router.get('/newInfo'. controller.new.getNewInfo);

4 View层使用EJS模版渲染

app文件夹下的config文件夹下的plugin.js文件

module.export = {
  ejs: {
    enable: true,
    package: 'egg-view-ejs',
  },
}

app文件夹下的config文件夹下的config.default.js文件

config.view = {
  mapping: {
    '.html': 'ejs',
  }
}

controller文件

'use strict';

const Controller = require('egg').Controller;
class EjsController extends Controller {
  async index() {
    const { ctx } = this;
    const { name, age } = ctx.query;
    const data = await ctx.service.new.getNewInfo(name, age);
    await ctx.render('index.html', data);
  }
}

module.export = EjsController;
router.get('/index'. controller.ejs.index);

4.1 引入html文件

app文件夹下新建一个view文件夹
view文件夹下新建index.html文件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA_Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <!-- 引入其他html文件 -->
  <% - include ("header.html") %>
  <h1>这是ejs ---首页</h1>
  <h1><%=id %></h1>
  <h1><%=name %></h1>
  <h1><%=age %></h1>
  <ul>
    <% for(var i=0;i<arr.length;i++){ %>
      <li><%=arr[i] %></li>
    <% } %>
  </ul>
</body>
</html>

4.2 引入css文件

app文件夹下的public文件夹下新建common.css文件

  <title>Document</title>
  <link rel="stylesheet" href="public/common.css">

5 cookie的增删改查

通过路由,找到对应的cookie.html,渲染cookie.html

cookies.html的script中写入函数

function addCookies() {
  fetch('/addCookies', {
    method: 'post',
    headers: {
      'Content-type': 'application/json'
    }
  });
}

配置路由

// cookie路由
router.post('/addCookies', controller.cookies.addCookies);
router.post('/delCookies', controller.cookies.delCookies);
router.post('/updateCookies', controller.cookies.updateCookies);
router.post('/getCookies', controller.cookies.getCookies);

this.ctx.request.body可以获取request body;
this.ctx.body 可以设置response body;

async addCookies() {
  const { ctx } = this;
  ctx.cookies.set('user', 'zhangsan', {
    maxAge: 1000 * 3, // 设置cookie时效性:3秒
  });

  ctx.cookies.set('user', '张三', {
    maxAge: 1000 * 3, // 设置cookie时效性:3秒
    encrypt: true, // cookie可以设置为中文
  });
  // Reponse Headers 内容 set-cookie
  ctx.body = {
    status: 200,
    data: '添加成功',
  };
}

async updateCookies() {
  const { ctx } = this;
  ctx.cookies.set('user', 'lisi');
  ctx.body = {
    status: 200,
    data: '更新成功',
  };
}

async getCookies() {
  const { ctx } = this;
  const user = ctx.cookies.get('user', {
    encrypt: true,
  });
  ctx.body = {
    user,
    status: 200,
    data: '查询成功',
  };
}

async delCookies() {
  const { ctx } = this;
  ctx.cookies.set('user', null);
  ctx.body = {
    status: 200,
    data: '删除成功',
  };
}

这里要注意:
在 Egg.js 中,可以通过 app.get() 方法来定义处理 GET 请求的路由,也可以处理网页路径;
通常对于接口请求,可以在路由配置上加一个/api;

  • /api/users 通常暗示这是一个API端点,用于数据交换;
  • /users 更倾向于一个网页路径,可能用于直接展示用户列表页面给用户看。

API路径(如 /api/users)返回数据格式(如JSON),而页面路径(如 /users)通过渲染模板来返回HTML页面给浏览器。因此,即便调用了相同的控制器方法,该方法内部也应根据请求的上下文(ctx)来判断响应类型。

6 Session

新建一个Controller

async addSession() {
 const { ctx } = this;
 //添加session
 ctx.session.username = 'zhangsan';
 ctx.body = {
    status: 200,
 };
}
//访问session
async sessionIndex() {
    const { ctx } = this;
    // 获取Session
    const username = ctx.session.username;
    await ctx.render(
      'session.html', {
        id: 1,
        name: '张三',
        age: 18,
        // 赋值给模板
        username,
     });
 }

在view中新建session.html

<body>
 <button onclick="addSession()">添加session</button>
 <h3><%=username%></h3>
</body>
<script>
 function addSession(){
   fetch("/addSession",{
     method:"post",
     headers:{
      "Content-type":"application/json"
      }
     })
  }
</script>

路由配置

// post接口
router.post('/addSession', controller.session.addSession);
// 获取页面
router.get('/session', controller.session.sessionIndex);

session 配置是在config.default.js文件中

config.session = {    
  key :"EGG_SESS",   // 设置Key的默认值    
  httpOnly:true,      // 设置服务端操作    
  maxAge:1000*60  ,   // 设置最大有效时间     
  renew: true,        // 页面有访问动作自动刷新session 
}

7 中间件

在app/middleware目录下的单独文件 error_handler.js

module.exports = () => {
 return async function errorHandler(ctx, next) {
    try {
      await next();
      if (ctx.status === 404 && !ctx.body) {
          ctx.body = {
             msg: `请求地址${ctx.request.url}不存在`,
             status: 201,
             data: null,
           };
       }
     } catch (err) {
        // 所有的异常都在 app 上触发一个 error 事件,框架会记录一条错误日志
        ctx.app.emit('error', err, ctx);
        const status = err.status || 500;
        // 生产环境时 500 错误的详细错误内容不返回给客户端,因为可能包含敏感信息
        const error = status === 500 && ctx.app.config.env === 'prod'
         ? 'Internal Server Error'
         : err.message;
        // 从 error 对象上读出各个属性,设置到响应中
        ctx.body = { status, msg: error };
        if (status === 422) {
           ctx.body = { status, msg: err.errors };
        }
        ctx.status = status;
     }
   };
 };

在应用中使用中间件,所有的页面都能够使用到该中间件;
config.default.js 文件中加入下面的配置就完成了中间件的开启和配置:

module.exports = {
  // 配置需要的中间件,数组顺序即为中间件的加载顺序
  middleware: ['error_handler'],
};

要想在某个路由中使用中间件,不必让所有的页面都能够使用到该中间件;
在handler的controller使用中间件;

module.exports = (app) => {
  const error_handler = app.middleware.error_handler();
  app.router.get('/testMiddeware', error_handler, app.controller.handler);
};

8 MySql数据库

8.1 安装 egg-mysql插件

npm i --save egg-mysql

8.2 开启插件

config/plugin.js 中开启

module.exports = {
  ejs: {
    enable: true,
    package: 'egg-view-ejs',
  },
  mysql: {
    enable: true,
    package: 'egg-mysql',
  },
};

config.default.js 中 配置连接数据库

module.exports = appInfo => {
 // 数据库连接
 exports.mysql = {
   // 单数据库信息配置
   client: {
      // host
      host: '127.0.0.1',
      // 端口号
      port: '3306',
      // 用户名
      user: 'web',
      // 密码
      password: '123456',
      // 数据库名
      database: 'web',
  },
   // 是否加载到 app 上,默认开启
   app: true,
   // 是否加载到 agent 上,默认关闭
   agent: false,
 };
};

8.3 Egg.js 操作 MySql 数据库

  1. 新建一个 Controller stu.js
const Controller = require('egg').Controller;
class StuController extends Controller {
	// 添加学生信息
	 async addStu() {
	     const { ctx } = this;
	     const { name, age } = ctx.request.body;
	     const result = await ctx.service.stu.addStu(name, age);
	     if (result) {
	        ctx.body = {
	          status: 200,
	          msg: '添加成功',
	          data: {},
	        };
	    } else {
	        ctx.body = {
	          status: 201,
	          msg: '添加失败',
	          data: {},
	      };
	    }
	 }
}
  1. 新建一个stu.js service
    this.app.mysql.insert(‘stu’, params);
const Service = require('egg').Service;

class StuService extends Service {
    // 添加学生信息
	async addStu(name, age) {
	 try {
	    const params = {
	       name,
	       age,
	    };
	    const result = await this.app.mysql.insert('stu', params);
	    const insertSuccess = result.affectedRows === 1;
	    if (insertSuccess) {
	      return result;
	    }
	    return null;
	  } catch (error) {
	      console.log(error);
	  }
	}
}
module.exports = StuService;
  1. 添加路由
 router.post('/addStu', controller.stu.addStu);

8.4 总结

  • this.app.mysql.insert(‘stu’, params); // 增
  • this.app.mysql.get(‘stu’, params); // 查
  • this.app.mysql.update(‘stu’, params); // 改
  • this.app.mysql.delete(‘stu’, { id }); // 删
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值