前言

小说中,终成眷属一般就结局了,但现实是生活一直在继续,我们一直在调试生活的方式来满足我们有恃无恐的安全感。
上一篇只是简单地搭建了后端服务器。这回我们要利用koa的脚手架搭建后端项目框架。

搭建koa项目框架

创建koa应用

koa-generator,koa的脚手架。就像vue与vue-cll(或vite)关系一样,koa-generato会帮助生成基础的应用项目。

安装koa-generato:

npm install koa-generator -g

在这里插入图片描述

在项目根目录下(/hello-node/)创建koa应用

 koa  server2

在这里插入图片描述
server2是项目的名字。

生成的项目目录如图:
在这里插入图片描述然后按照终端的提示分别输入以下指令
1.进入server2目录

cd server2

2.安装依赖

npm install

3.启动服务器

npm start

4.在浏览器地址栏输入127.0.0.0:3000即可打开应用
在这里插入图片描述

koa指令报错

当安装完koa-generato,用koa指令创建应用时,可能会在终端报错:

koa : 无法加载文件 C:\Users\Administrator\AppData\Roaming\npm\koa.ps1,因为在此系统上禁止运行脚本。

在这里插入图片描述

这应该是由于计算机在启动 Windows PowerShell 时,执行策略很可能是 Restricted(受限制的),也就是默认设置。
Restricted这个执行策略不允许任何脚本运行。
可以打开PowerShell 输入 get-executionpolicy来查看,计算机目前的执行策略。如出现:Restricted,则说明执行策略受限,不允许运行未签名的脚本。
解决:
以管理员身份打开PowerShell 输入 set-executionpolicy remotesigned(设置执行策略远程签名)
————————————————————————————————
*版权声明:本文为CSDN博主「shelleyHLX」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_27009517/article/details/115293568*

1.以管理员身份运行PowerShell

在这里插入图片描述

  1. 设置执行策略远程签名
  • 输入 set-executionpolicy remotesigned
  • 输入A
  • 输入koa -v(如果要用koa2指令生成koa2应用,则输入koa2 -v

在这里插入图片描述
这是在vscode终端输入koa --version,会显示koa的版本号。若还是报错,重启一下 vscode,重启vscode还报错,那就重启系统(并点上三根香保佑一下)。

分析并修改koa项目代码

koa-generato生成的项目目录如下:
在这里插入图片描述

项目文件组成

  1. package.json 配置文件

    在这里插入图片描述
    其中
    script.start运行node bin/www指令,启动应用,但不监视文件变化。(适用于生产环境)。

    script.dev运行./node_modules/.bin/nodemon bin/www指令(其实就是nodemon指令),启动应用,并监视文件变化。(适用于开发环境)。

    script.prd运行pm2 start bin/www指令,启动应用(怎么启动还没研究)。PM2 是一个守护进程管理工具。开机自启动也可以用它写。

  2. /bin/www.js 服务器入口(启动)文件

    可以在里面修改应用进程的端口号,默认是3000
    在这里插入图片描述

  3. /app.js 项目入口文件

    用于整合 koa中间件
    在这里插入图片描述app.use(function):将给定的中间件方法(即function)添加到此应用程序。如果你使用过Vue,可以把看成是VUE.use()。

  4. /routes/ 路由目录
    koa-generato脚手架默认会在 /routes/目录下创建index.js和user.js,这两个都是用来匹配路由的。
    设置ctx.body表示响应的数据。
    以index.js为例:

    const router = require("koa-router")();//引入路由中间件koa-router
    
    /**路由匹配规则 Start*/
    router.get("/", async (ctx, next) => {
      await ctx.render("index", {
        title: "Hello Koa 2!",
      });
      next();
    });
    
    router.get("/string", async (ctx, next) => {
      ctx.body = "koa2 string";
    });
    
    router.get("/json", async (ctx, next) => {
      ctx.body = {
        title: "koa2 json",
      };
    });
    /**路由匹配规则 End*/
    
    module.exports = router;//导出路由模块
    

    首先引入koa-router中间件,然后编写路由匹配规则,最后将路由模块导出,以备app.js中引入并安装该中间件。

    app.js

    const Koa = require("koa");
    const app = new Koa();
    
    /***引入路由中间件*/
    const index = require("./routes/index");
    const users = require("./routes/users");
    /***引入路由中间件*/
    
    .......
    
    /***安装路由中间件*/
    app.use(index.routes(), index.allowedMethods());
    app.use(users.routes(), users.allowedMethods());
    /***安装路由中间件*/
    
    module.exports = app;
    
    
  5. /view/ 视图目录
    视图层,类似于vue的作用,这里用的Pug模板(我们是前后端分离,这里可忽略)。

  6. /public/ 公共资源目录
    一般用于存放静态文件,比如图片等。

项目流程

1./bin/www.js为项目入口,运行该文件,将创建一个http服务器var server = http.createServer(app.callback());,并监听端口

app.callback()返回适用于 http.createServer() 方法的回调函数来处理请求。你也可以使用此回调函数将 koa 应用程序挂载到 Connect/Express 应用程序中。

2.进入app.js,添加需要的Koa中间件。
3.app.js中的路由中间件具体匹配规则被代码分离到 /routes/ 目录下的各个文件中。
4.当请求发生时,各个中间件按序触发,对请求、响应进行处理。直到完成此次握手。

中间件原理

通俗的讲:中间件就是 匹配路由之前或者匹配路由完成后 做的一系列的操作(注意,中间件是服务器收到请求时执行的一系列代码),我们就可以把它叫做中间件。
Koa的中间件(Middleware) 是一个函数,它可以访问请求对象(request object (req)), 响应对象(response object (res))。在 Koa中处理请求、响应循环流程的变量一般被命名为 next 变量。
如果我们的get、post回调函数中,没有next参数,那么匹配到这个路由就不会继续往下匹配了。如果想往下匹配的话,那么需要写next() 。

中间件函数有两个参数:ctxnext
ctx是一个对象,其中包含了请求对象、响应对象等。next是一个函数,调用该函数Koa会进入下一个中间件函数,直到当前中间件函数执行完毕(遇到return)会返回到上一个中间件函数的next处继续向后执行。

Koa把这种中间件执行方式叫做洋葱圈模型

在这里插入图片描述

const Koa = require("koa");
const app = new Koa();
app.use(async (ctx, next) => {
  console.log("111");
  await next();
  console.log("666");
});
app.use(async (ctx, next) => {
  console.log("222");
  await next();
  console.log("555");
});
app.use(async (ctx, next) => {
  console.log("333");
  await next();
  console.log("444");
});

当有请求发生时输出:

111
222
333
444
555
666

这个执行顺序有点像递归函数,next()前面为前递归,后面为后递归(实际上应该使用generator函数写的)。而next就像一个指针,指向下一个中间件。

const Koa = require("koa");
const app = new Koa();
app.use(async (ctx, next) => {
  console.log("111");
  await next();
  console.log("666");
});
app.use(async (ctx, next) => {
  console.log("222");
  //await next();
  console.log("555");
});
app.use(async (ctx, next) => {
  console.log("333");
  await next();
  console.log("444");
});

当有请求发生时输出:

111
222
555
666

由于第二个中间件没有调用next(),所以到第二个中间件的执行链就断了,不会执行下一个中间件了。

路由中间件也是一样的道理

const Koa = require("koa");
const app = new Koa();
const router = require("koa-router")();

/**路由匹配规则*/
router.get("/", async (ctx, next) => {
  await ctx.render("index", {
    title: "Hello Koa 2!",
  });
  next();
});
router.get("/string", async (ctx, next) => {
  ctx.body = "koa2 string";
});
/**路由匹配规则*/

app.use(async (ctx, next) => {
  console.log("111");
  await next();
  console.log("666");
});
app.use(async (ctx, next) => {
  console.log("222");
  await next();
  console.log("555");
});
/**路由中间件*/
app.use(router.routes(), router.allowedMethods());
/**路由中间价*/
app.use(async (ctx, next) => {
  console.log("333");
  await next();
  console.log("444");
});

几个常用中间件

koa-bodyparse:

koa-bodyparse用来解析请求和响应中的body。

  • 解析POST请求中的参数(处理成对象),存储在ctx.request.body中。
  • 响应时,可以设置ctx.body为对象(GET、POST都适用)。
	const Koa = require('koa');
	const bodyParser = require('koa-bodyparser');
	
	const app = new Koa();
	app.use(bodyParser());
	
	app.use(async ctx => {
	  // the parsed body will store in ctx.request.body
	  // if nothing was parsed, body will be an empty object {}
	  ctx.body = ctx.request.body;
	});

koa-router

koa-router:路由处理

const router = require("koa-router")();
//Get:不带参数
router.post("/hello", async (ctx, next) => {
  ctx.body = `Hello Node! I Post`;
});
//Get:带参数
router.get("/hello/:name", async (ctx, next) => {
  var name = ctx.params.name;
  ctx.body = {title:'node',name:name};
});

//Post
router.post("/goodbye", async (ctx, next) => {
	const name= ctx.request.body.name;
  ctx.body = {name:name,type:'node';
});

router.prefix(s)设置当前路由的前缀,比如:

const router = require("koa-router")();

router.prefix("/index");

/**请求地址为/index匹配该路由*/
router.get("/", async (ctx, next) => {
  await ctx.render("index", {
    title: "Hello Koa 2!",
  });
  next();
});

/**请求地址为/index/string匹配该路由*/
router.get("/string", async (ctx, next) => {
  ctx.body = "koa2 string";
});

koa-logger

koa-logger:日志打印,适用于开发环境,打印请求和响应信息

const logger = require('koa-logger')
const Koa = require('koa')
 
const app = new Koa()
app.use(logger())

自定义打印信息

const logger = require('koa-logger')
const Koa = require('koa')
 
const app = new Koa()
app.use(logger((str, args) => {
  // redirect koa logger to other output pipe
  // default is process.stdout(by console.log function)
}))

app.use(logger({
  transporter: (str, args) => {
    // ...
  }
}))

str 是一个字符串类型,在发生请求时 str 包含 请求类型、请求路径信息,在发生响应时 str 包含
响应状态码、响应时长、响应文件大小信息。
args是一个数组类型,在发生请求时会将请求类型、请求路径放在该数组中,在发生响应时会将响应状态码、响应时长、响应文件大小信息放入该数组中。

koa-json

koa-json:格式化响应文本。

虽然可以设置ctx.body为JSON对象(响应数据),但其实响应的数据都是文本(字符串)类型的。因为使用了 koa-bodyparse 中间件,它会把设置的JSON对象处理成字符串,然后再发出响应。

做个试验,我们把koa-bodyparse 中间件去掉,再去设置ctx.body为JSON对象,那时http的响应为“无响应”。除非你用JSON.stringify()把对象处理成字符串。

既然响应是字符串,那么我们以何种格式设置它,它就会以同样的格式给请求。这样很难保证可读性,这时koa-json就起到作用了,它可以让JSON字符串显示得更像JSON的格式,增加可读性。

配置默认:

app.use(json());

自定义配置:

const json = require("koa-json");
app.use(json({pretty: false, param: 'pretty',spaces:4}));
  • pretty:是否格式化响应的JSON字符串,默认true
  • param:可选查询字符串,默认为空。当pretty为false,请求中查询字符串参数有该字段设置的字符串时,格式化响应的JSON字符串。
  • spaces:每组key-value前空格的数量,默认为2。

比如,在/routes/index.js中添加路由配置

router.get("/json", async (ctx, next) => {
  ctx.body = {
    title: "koa2 json",
    sub: "hah",
    tree: {
      leaf: "234",
    },
  };
});

app.js使用koa-json默认配置

app.use(json());

浏览器地址中输入localhost:3000/json,显示为:
在这里插入图片描述修改koa-json配置

app.use(json({ pretty: false, param: "pretty", spaces: 8 }));

浏览器地址中输入localhost:3000/json,显示为:

在这里插入图片描述
浏览器地址中输入localhost:3000/json?pretty,显示为:

在这里插入图片描述
也可以在Edge浏览器的DevTools中看到实际响应的数据:

在这里插入图片描述

koa-json一般都是用于开发环境调试时用的。而前端一般会将响应的JSON格式字符串转化为JSON对象,所以不太会在意JSON格式字符串的格式。

koa-static

koa-static:静态资源管理。

如果不用koa-static中间件,以该项目目前的代码是访问不到静态资源的。可以做个测试:在/public/images/目录中放一张图片
在这里插入图片描述
你可以在浏览器地址栏尝试输入:localhost://3000/public/1.jpeg ,结果是页面没有显示图片,而是 404 not found
为甚?因为node是一种服务,localhost://3000/上的所有请求都是服务,而/public/1.jpeg这个请求在该node服务中没有被路由注册,所以就会返回404。

koa-static加载说明:

app.use(require('koa-static')(root, opts));
  • root:String,静态资源的根目录,请求该目录下的文件不会被当成服务
  • opts:Object,配置参数
    • maxage:浏览器缓存max-age数值,单位ms,默认0。
    • hidden:是否允许传输隐藏的文件,默认false。
    • index:默认文件名,默认为"index.html"。
    • defer:如果为true,则在返回next()后服务,这将允许任何下游中间件首先响应。默认false。
    • gzip:当客户端支持gzip并且请求的扩展名为.gz的文件存在时,尝试自动提供文件的gzip版本。默认为true。
    • br:当客户端支持brotli并且请求的扩展名为.br的文件存在时,尝试自动提供文件的brotli版本(注意,brotli仅通过https接受)。默认为true。
    • setHeaders:Function(res, path, stats),自定义设置响应头。
    • extensions:当URL中没有足够的扩展名时,尝试从传递的数组中匹配扩展名以搜索文件。先找到的先匹配。默认为false。

基础用法(配置项全部默认)

app.use(require("koa-static")(__dirname + "/public"));

__dirname是node的常量,存储当前目录路径的字符串。

在浏览器地址栏输入:http://localhost:3000/images/1.jpeg,图片就显示出来了。

注意!因为设置koa-static的root为/public/,所以在请求静态资源时不需要加/public/(可以把koa-static看成是静态资源服务器,其设置的root,即是该服务器的根目录)。

设置静态资源缓存

app.use(require("koa-static")(__dirname + "/public"{maxage:60000}));

可以在DevTools看到,响应头中的Cache-Control:max-age=60;
在这里插入图片描述

设置默认文件名

app.use(require("koa-static")(__dirname + "/public"{index:"index.html"}));

在/public/目录下新建一个index.html
在这里插入图片描述
在浏览器地址栏输入:http://localhost:3000/,就会显示/public/index.html页面。
这就相当于koa-router设置默认。

如果koa-router也设置了默认:

const router = require("koa-router")();
router.get("/", async (ctx, next) => {
  await ctx.render("index", {
    title: "Hello Koa 2!",
  });
  next();
});

那么谁有效就要看中间件的加载顺序了:谁先加载谁有效!另外,要是koa-static设置的index静态资源没找到,那么他就不会起作用。

比如,把index设置index2.html,因为 /public/ 目录下没有index2.html,所以即使koa-static的加载顺序要靠在koa-router前面也不会有效:他会去匹配后面的koa-router默认。

defer设置

app.use(require("koa-static")(__dirname + "/public"{defer: true}));

上面讲到koa-router和koa-static同时设置了默认路由 谁先加载谁有效 ,其实不准确,那得是有一个前提,就是defer这个配置项为false。一旦defer设置为true,那么不管加载顺序怎样,都是koa-router的默认路由有效。因为defer=true相当于滞后生效。

自定义设置响应头

app.use(require("koa-static")(__dirname + "/public"{
	setHeaders: (res, path, stats) => {
      res.setHeader("name", "xy");
   	},
}));

在浏览器地址栏输入:http://localhost:3000/images/1.jpeg,可以看到DevTools中的响应头:
在这里插入图片描述setHeaders配置项是一个Function,其有三个参数res, path, stats:

  • res:相当于ctx.res,是Node 的 response 对象.(ctx为Koa上下文),其setHeader()方法可以设置响应头。
  • path:请求的文件地址字符串。
  • stats:是Node 的 stats对象,存储着文件信息。

extensions配置

app.use(
  require("koa-static")(__dirname + "/public", {
    extensions: [".png", ".jpeg"],
  })
);

在浏览器地址栏输入:http://localhost:3000/images/1 (不加文件后缀),图片依然可以显示。这是因为设置了extensions,其中有 .jpeg 的扩展匹配。

可以设置多个静态资源管理器:

const app = new Koa();
app.use(
require("koa-static")(__dirname + "/public")
);
app.use(
require("koa-static")(__dirname + "/static")
);

番外:koa-static-match-path是静态资源映射器。它可以将原资源目录映射为指定目录名称:

const app = new Koa();
const handleStatic = require('koa-static-path')
app.use(handleStatic(__dirname+'./public/','static'))

浏览器地址栏输入http://localhost:3000/static/images/1.jpeg

koa-views

koa-views视图模板渲染。
前后端分离,这个项目目前用不到,搁置-----主要是写不动了。

参考资料

GitHub 加速计划 / vu / vue
207.54 K
33.66 K
下载
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:2 个月前 )
73486cb5 * chore: fix link broken Signed-off-by: snoppy <michaleli@foxmail.com> * Update packages/template-compiler/README.md [skip ci] --------- Signed-off-by: snoppy <michaleli@foxmail.com> Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 4 个月前
e428d891 Updated Browser Compatibility reference. The previous currently returns HTTP 404. 5 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐