Oak 是一款用来开发 http server 的中间件框架,其包含了 Router 路由中间件。
此外,如果读者有使用 Node.js 的经验,可能有听过 Koa 、 Express 等後端框架。 Oak 其实就是致敬 Koa 开发出来的,因此在使用上,两者有许多重叠的地方,除了第三方的中间件不可共用以外,大部分的观念都能套用到 Oak 身上。
笔者第一次看到 Oak 时,真的直接笑出来。想说这群开发者到底是夺懒,也不花点时间想名字就把 Koa 的名字重组 XDD
如图,每个 Oak 的中间层都像是洋葱的其中一圈,当後端程序收到 Http Request 会经过每层中间件的处理,最後变为 Response 传送回去。
本图取自该网站。
让我们进一步透过范例观察:
import { Application } from "https://deno.land/x/oak/mod.ts";
const app = new Application();
// Logger
app.use(async (ctx, next) => {
await next();
const rt = ctx.response.headers.get("X-Response-Time");
console.log(`${ctx.request.method} ${ctx.request.url} - ${rt}`);
});
// Timing
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.response.headers.set("X-Response-Time", `${ms}ms`);
});
// Hello World!
app.use((ctx) => {
ctx.response.body = "Hello World!";
});
await app.listen({ port: 8000 });
我们可以在范例中看到 use()
以及 next()
方法, use()
用於注册中间件, next()
则用来告诉 Oak 可以先跳到下一个中间件,假设该范例程序正在执行并收到一个请求,中间件的执行顺序如下:
Logger -> Timing -> Hello World! -> Timing -> Logger
这样的顺序就如同洋葱图所表示的一样,我们更可以利用这个特性去纪录从 Request 到 Response 一共花了多少时间:
// Timing
app.use(async (ctx, next) => {
const start = Date.now();// 纪录收到时间
await next();// 跳到下一个中间件
const ms = Date.now() - start;// 回到该中间件时再次纪录时间
ctx.response.headers.set("X-Response-Time", `${ms}ms`);
});
以上程序码需要注意的事项有两点:
开始使用前必须记得将 Oak 引入:
import { Application } from "https://deno.land/x/oak/mod.ts";
启动时需加入 --allow-net
标签。
在实作前後端分离的系统时,以 SPA (Single Page Application) 为例,都会有多个虚拟路由,每个路由都代表了不同的页面,像是: Home
、 About
、 Contact
等等。
因此,我们在实作 API 时也可以将它拆分成对应的路由, Oak 内建了高度致敬 Koa-Router 的路由中间件: Oak-Router
。
import { Application, Router } from 'https://deno.land/x/oak/mod.ts';
const app = new Application();
const router = new Router();
router
.get('/home', (context) => { context.response.body = "This is Home!"; })
.get('/about', (context) => { context.response.body = "This is About Page."; })
app.use(async (context, next) => {
await next();
console.log(`${context.request.method} ${context.request.url}`);
});
app.use(router.routes());
app.use(router.allowedMethods());
await app.listen({ port: 8000 });
上面的范例中,一共实作了两个路由: home
以及 about
。
实际上,最常见的请求方法有分为 get
以及 post
方法, get
为上面范例所使用到的:
// ...
router
.get('/home', (context) => { context.response.body = "This is Home!"; })
// ...
而 post
的用法并不难,收先要做的就是将 get
关键字换成 post
关键字:
// ...
.post('/api/movies', async (context) => {
const data = await context.request.body();
// ...
context.response.body = //... ;
});
// ...
get 常用来做查询资料,因为 get 的查询参数可以直接在网址上看到,像是:
https://www.findprice.com.tw/g/Ipad
此为比价网站 findprice
的网址,而网址中的 Ipad
表示查询的关键字,在後端程序中,程序将它作为参数作为数入并进行相关操作。
Post 的话则比较常用来做写入操作,因为相关参数会被放到 http 请求当中,所以我们常常看到大家建议使用 post 而不是使用 get ,因为 get 方法会将请求参数暴露给使用者知道,会更容易出现 SQL Injection
以及 XSS
等安全漏洞。
只要开发者喜欢,要用 get 或是 post 做查询、写入都可以。只是使用 get 做查询相对直观一些,因为使用者可以透过网址知道现在在浏览网站的第几页、自己搜寻了什麽关键字...等等。
我们都知道, JavaScript 是使用非同步处理机制的,至於要如何让 JS 做到同步呢?
我们可以善用 Promise
以及 async/await
,会特别提到同步机制的原因如下:
从一开始的洋葱图可以知道,一个中间件是有可能被通过两次的。若在请求通过中间件第二次时,第一次的操作根本还没做完便有可能造成严重的影响,像是:
// ...
app.use((ctx, next) => {
const start = Date.now();// 纪录收到时间
Insert(start);// 将时间存放至资料库
await next();// 跳到下一个中间件
const ms = Date.now() - start;// 回到该中间件时再次纪录时间
ctx.response.headers.set("X-Response-Time", `${ms}ms`);
});
// ...
若没有确保在 Insert()
完成前就跳到下一个中间件,便有可能在实际应用中出现巨大的问题,这时候我们可以使用 Promise
以及 async/await
改善:
// ...
app.use(async (ctx, next) => {
const start = Date.now();// 纪录收到时间
await Insert(start);// 将时间存放至资料库
await next();// 跳到下一个中间件
const ms = Date.now() - start;// 回到该中间件时再次纪录时间
ctx.response.headers.set("X-Response-Time", `${ms}ms`);
});
// ...
这边要注意的是: 我们必须确保使用到
await
的函式在实作中是以Promise
方法达成的,笔者会将相关文章放到本日的延伸阅读。
如果有读者对 同步
一词有疑问, 同步
在这边代表做完一件事才能做下一件事,如果对非同步的概念很模糊,可以回去看强型闯入DenoLand[24] - 使用 Deno 打造多线程应用(1),在本篇笔者有提到 Chrome V8 是如何做任务排程的。
本篇先将 Oak 的重要观念先提一遍,之後会针对每个部分逐一讲解,如果有空,笔者会再花时间把 Promise()
以及 async/await
的教学补上,还请见谅 ORZ
同样的事情在不同人眼中可能会有不同的见解、看法。
在读完本篇以後,笔者也强烈建议大家去看看以下文章,或许会对型别、变数宣告...等观念有更深层的看法唷!
就如标题所说的、 HDMI 端子可以说是目前最通用的端子、现在的电视几乎都只有 HDMI 端子、顶多...
Best Time to Buy and Sell Stock 题目连结:https://leet...
javap介绍 javap是jdk工具中自带的反编译工具,它是根据class位元组码档案,反解析出当...
如何做选择 选择一个好的开源专案来当作起点,可以省下很多重新发明轮子的时间,把精力专注在想要改善和提...
Multiple criteria filter 继众里寻它後,我们想继续看是否有各个栏位都符合关...