终于Edgeone的Pages也能用上中间件了
📝3436 个字
 | ⌛要看完怎么也得9分钟吧
事情还得从前段时间说起
- 最近因为工作上的原因,一直没啥时间写新的小作文了。主要还是年前因为接了个大活,所以年后就开始了没日没夜的倒腾🤪。也是最近才是稍微有点片刻喝茶的时间了,所以想着既然这么久没关注了,不如顺便上去看看后台日志吧。
- 结果刚刚打开日志,直接就是两眼一黑😵。也不造是不是打开方式出现了一些偏差认知,俺这小网站啥时候成了爬虫的心头好了🫢?每天的访问记录,都充斥着大量来自edgeone的无意义信息。最关键的访问就算了,用的还不是俺自己的域名,简直就是山中无老虎,猴子称大王了,关键还是硅基的🥹!
- 但是思前想后,之前刚刚部署到edgeone pages上的时候,明明专门写了个edge funtion,屏蔽了所有来自edgeone项目默认域名的访问。那现在的这些访问,又是走的哪条赛博高速公路🤔……于是将信将疑之余,决定自己用edgeone的项目域名打开试试,没准这些访问的记录只是一些美丽的意外呢😆。
- 结果一打开,好嘛,简直就是一马平川,畅通无阻啊🤡!最离谱的是啥,之前整的edge funtion除了屏蔽默认域名以外,还顺便整了一些api功能,但这些功能也确实在起作用🤨!那这不就见了鬼嘛,总不能是机宝双开回响形态,结果一张超频猛抽9张眩晕吧!主打一个脑瓜子嗡嗡的😵💫。
可恶的小bug,可别让我逮着了
- 首先是怀疑的,自然还是edge function脚本是不是逻辑出了什么问题。但其实就跟当初刚刚接入edgeone时的感受一样,edge funtion最麻烦的地方就是在debug调试上😑。主要是他的日志输出功能非常有限,甚至可以自信点说就是没有。不管是在本地使用cli,还是直接部署到线上环境,用控制台的日志功能,都无法捕获到实际脚本打印的日志信息。所以在怎么跟这厮较劲,也都是在无能狂怒🤡。
- 那咋办呢?日志瞅不着,断点更不用想,总不能直接把鉴权逻辑搬到客户端吧?嘿,你别说,还真就这么干了🤪。虽然有点裸奔的意思,但总归能遏制一下这些狂热的赛博粉丝了。反正需要的也只是些简单的域名校验逻辑,咱自己这小网站啥域名也不是啥见不得人的秘密😉。
- 但这总归也只是个权宜之计,毕竟客户端的代码是没有任何安全保障的。万一有些不讲武德的赛博生命体,屏蔽掉了俺的妙妙小代码,那又是一场新的狂欢🤡……所以还是得想办法治治这厮,不然俺这小网站迟早要不堪重负。
- 所以又久违的打开了edgeone的官方文档,上次打开还是去年年初的时候了🤔。因为之前一直是在被日志功能卡脖子,所以就看看官方文档上怎么说的,总不能大伙都是在盲人摸象吧🤣。结果这不就发现了,原来控制台上的日志功能,其实是给cloud function准备的。至于说和edge function的区别吧,其实也没啥区别,无非就是在一些大部分用户感知不到的细微变化上做文章🤫。但这并不影响咱们定位问题,所以不如死马当活马医,改成cloud function试试。
- 不过不得不提的是,其实当初接入edgeone的时候,就已经有了对应功能,不过那会我记得还是叫node function🤔。可能是藤子处于某种目的,后来又专门换了个新的名字吧,以至于现在官方文档上,时不时就会出现新老名字混用的情况,搞得人一头雾水😶🌫️。总之不管叫什么名字,反正就是那个功能了。于是就把之前的edge function脚本,稍微改了一下,部署成了cloud function。结果一部署上去,嘿,这小bug还真就一起移植过来了呢🥰!
- 但也不全是坏事,至少还是有点喜人的消息,就是咱能看到输出的日志了✌️!总算是不用盲人摸象了,所以一顿操作猛如虎,前前后后加上了各种各样的日志输出。本来精瘦精瘦的代码,一下子就变得臃肿了起来。但只要能定位到问题,那这点臃肿的影响也不过是细如牛毛了💪。
- 于是乎,带着三份欣喜和七分好奇,在本地运行了cli。结果你猜怎么着,不仅日志正常输出了,所有脚本原有的功能都在正常运作🥴!这不就又丈二的和尚摸不着头脑了嘛,难不成是edge function自己的妙妙小bug?换成了cloud function,就给误打误撞的解决了😵?

- 嘛,检验真理的唯一标准就是实践了,所以就把本地的修改,包含后来臃肿的日志,一起打包上传到了线上环境。也不造是错觉,还是雀食有段时间没写小作文了,总感觉edgeone的部署速度,都变得漫长了起来😞。不过倒是没啥部署报错,也雀食成功实装上线了。于是马不停蹄地用edgeone项目的默认域名打开了试试,但迎接俺的却是熟悉的一马平川……不过这次不慌了,毕竟是带着家伙来打仗的,辣么多的日志可不是吃素的💪!
- 但是吧,不出意外的时候一定还是会出意外的🤡。日志雀食是输出了,但是却只是个别自定义的接口,非常不情愿地挤出了几句短小的日志信息。但最关键的域名鉴权日志,却是找不到任何的蛛丝马迹😭。可能有的小伙伴会说,一定是日志输出存在着一定的延迟。但甭管俺怎么刷新和清除缓存,等来的依旧是原来的那些老面孔。这不就和之前初步判断的结论一样了嘛,脚本雀食在干活,但却属于是有选择性的上班💩。

- 这可咋整,就算有日志也不能提供一点帮助,一切又回到了最初的原点。而且最关键的是,本地明明是正常的,但部署线上了就开始耍性子了🥹。不过折腾了这么久,也算是摸索出一点门道了。所有能正常运行的都是咱自己定义的一些接口访问,而这些接口访问和正常网页的访问有啥不一样的地方呢🤔?对!以小伙伴们的聪明才智一定发现了,这些接口访问,并不存在实际的路径和资源。也就是说,能够正常走到咱脚本的逻辑,一定都是实际资源不存在的路径😲!
- 而证明的方式也很简单,只要自己胡编乱造个路径出来进行访问,比如说
/hahaha,如果在控制台日志上能看到这次访问产生的信息,那就说明了问题的根源了😎。事实证明,也确实如同之前的推测一样,只有访问那些实际资源不存在的路径,才会触发脚本的逻辑!那问题的答案就非常明显了,只可能是edgeone pages最近对pages function的调用优先级做了调整了。只要是当前的访问能找到对应的静态资源,都会优先自动获取,而不是调用对应的脚本逻辑😖。 - 但真要说的话,其实本身这个设计并没啥问题,甚至可以说其他平台也都这么干了,这也是为啥俺不用esa的原因之一🥴。毕竟没有了鉴权的能力,虽然说也不至于说是完全没有安全保障了,但总归是有点裸奔的感觉了。所以无奈找了个看着似乎有点活跃的edgeone官方仓库上,提交了个issue,一方面是想证实一下自己的猜想,另一方面自然就是看看能不能得到一些官方的解决方案了🥹。
- 不过意外的是,原以为是无人在意的仓库,结果却真的得到了官方的答复了😮!首先坏消息是雀食证实了俺的猜想,最近对pages function的调用优先级做过调整,静态资源的访问会优先于pages function的调用😞。所以对咱这种使用场景,传统的edge function和cloud function雀食都不能胜任。但也有个好消息,就是edgeone把中间件的功能也一起支持上了🤠!
还得是中间件好使啊
- 之前看官方文档的时候,主要一直在盯着pages function和日志那块的说明。所以一直没注意到,原来就在pages function下面,还有个中间件的功能啊🤡!当然还有一部分原因,就是之前在cloudflare上的文档写的一直是英文名middleware。这会儿突然来了个中文名,多少还是有点眼生了🤨。
- 那中间件和pages function的区别是什么呢?其实也很简单,主要就是在调用时机上做了区分。中间件的调用,是在所有的请求之前就介入的。也就是说,不管是访问静态资源,还是接口访问。只要路由配置上有匹配到中间件的路径,就会优先调用对应的中间件逻辑了。而这正好和咱们需要的鉴权逻辑的调用时机完全契合了😎!所以剩下的事情就很简单了,直接把之前page function中的逻辑,搬过来改成中间件就好了。
- 不过说句不相干的,因为咱这小网站最早是部署在cloudflare上的,所以其实之前在cloudflare上使用的就是对应的middleware功能了。但后来移植到edgeone的时候,因为只有pages function这个功能,所以就只能把之前的middleware逻辑,按照page function的api文档,又重新实现了一套。没想到现在又得改回去了,多少有点五味杂陈了😶🌫️。
- 接入的方法其实很简单,首先就是需要在项目根目录下新建一个middleware.js的文件。然后在脚本文件中,定义并导出一个名为middleware的函数,而这个函数就是中间件的入口函数了。
// middleware.js export function middleware(context) { // 请求直接透传 return context.next(); } - 这个函数的context参数和之前edge function的参数是一样的,都是一个包含了请求信息的对象,所以大部分逻辑都不需要更改。唯一需要改动的就是之前edge function中,直接返回响应的方式了。因为中间件的调用时机是在所有请求之前,所以它并不直接返回响应,而是通过一个next函数来控制后续逻辑的执行了。
- 类似于next,这个函数的context参数还拥有用于重定向的redirect方法、用于重写路径的rewrite方法。有使用到对应功能的时候,也需要把之前对应的实现方式,改成调用这些方法了。但是如果是需要fetch的话,还是可以直接使用fetch来进行请求的,毕竟中间件的调用时机是在所有请求之前,所以它也具备了fetch的能力了。最后实现的代码大致如下:
export async function middleware(context) { const request = context.request; const url = new URL(request.url); const requestHost = url.host; const pathname = url.pathname; if (!requestHost.endsWith("fantasydm.top")) { return context.redirect(context.env.url404); } if (pathname !== null && pathname.toLowerCase().startsWith('/api/xxx')) { return await fetch(context.env.apiPath); } // 请求直接透传 return context.next(); } - 最后是路由配置这块,咱需要同一个文件下,定义并导出一个名为config的对象,用于配置中间件的路由规则。因为咱的使用场景,非常简单粗暴,所以对应的路由规则也就非常简单。直接使用
'/:path*'把所有路径的访问,都匹配到这个中间件上了,酱紫就可以保证所有的访问都能触发中间件的逻辑了。当然不配置也行,因为edgeone官方默认的路由规则,用的也是这个全路径匹配的规则。// 配置路由匹配规则 export const config = { matcher: [ '/:path*', // 匹配所有路由 ], }; - 就酱部署之后,经过一番访问验证,雀食恢复到了以往正常的状态。后来又经过了几天的观察,访问记录里的赛博生命虽然还是会有,但至少是按照咱规定的入口来,不至于在一些未知的区域里疯狂开party💩……如果同样有需求,或者同样之前有这么使用过的小伙伴,最近可以关注一些page function的运行状态了,说不定也会因为edgeone的技术调整受到牵连。到时候就可以参考一下俺的经历和解决方案了,说不定还能帮上一点忙!😎