JavaScript基础之用koa处理url
来源:互联网 发布:安卓数据恢复 编辑:程序博客网 时间:2024/06/10 01:59
在hello-koa工程中,我们处理http请求一律返回相同的HTML,这样虽然非常简单,但是用浏览器一测,随便输入任何URL都会返回相同的网页。
正常情况下,我们应该对不同的URL调用不同的处理函数,这样才能返回不同的结果。例如像这样写:
app.use(async (ctx, next) => { if (ctx.request.path === '/') { ctx.response.body = 'index page'; } else { await next(); }});app.use(async (ctx, next) => { if (ctx.request.path === '/test') { ctx.response.body = 'TEST page'; } else { await next(); }});app.use(async (ctx, next) => { if (ctx.request.path === '/error') { ctx.response.body = 'ERROR page'; } else { await next(); }});
这么写是可以运行的,但是好像有点蠢。
应该有一个能集中处理URL的middleware,它根据不同的URL调用不同的处理函数,这样,我们才能专心为每个URL编写处理函数。
koa-router
为了处理URL,我们需要引入koa-router这个middleware,让它负责处理URL映射。
我们把上一节的hello-koa工程复制一份,重命名为url-koa。
先在package.json中添加依赖项:
"koa-router": "7.0.0"
然后用npm install安装。
接下来,我们修改app.js,使用koa-router来处理URL:
const Koa = require('koa');// 注意require('koa-router')返回的是函数:const router = require('koa-router')();const app = new Koa();// log request URL:app.use(async (ctx, next) => { console.log(`Process ${ctx.request.method} ${ctx.request.url}...`); await next();});// add url-route:router.get('/hello/:name', async (ctx, next) => { var name = ctx.params.name; ctx.response.body = `<h1>Hello, ${name}!</h1>`;});router.get('/', async (ctx, next) => { ctx.response.body = '<h1>Index</h1>';});// add router middleware:app.use(router.routes());app.listen(3000);console.log('app started at port 3000...');
注意导入koa-router的语句最后的()是函数调用:
const router = require('koa-router')();
相当于:
const fn_router = require('koa-router');const router = fn_router();
然后,我们使用router.get(‘/path’, async fn)来注册一个GET请求。可以在请求路径中使用带变量的/hello/:name,变量可以通过ctx.params.name访问。
再运行app.js,我们就可以测试不同的URL:
输入首页:http://localhost:3000/
输入:http://localhost:3000/hello/koa
处理post请求
用router.get(‘/path’, async fn)处理的是get请求。如果要处理post请求,可以用router.post(‘/path’, async fn)。
用post请求处理URL时,我们会遇到一个问题:post请求通常会发送一个表单,或者JSON,它作为request的body发送,但无论是Node.js提供的原始request对象,还是koa提供的request对象,都不提供解析request的body的功能!
所以,我们又需要引入另一个middleware来解析原始request请求,然后,把解析后的参数,绑定到ctx.request.body中。
koa-bodyparser就是用来干这个活的。
我们在package.json中添加依赖项:
"koa-bodyparser": "3.2.0"
然后使用npm install安装。
下面,修改app.js,引入koa-bodyparser:
const bodyParser = require('koa-bodyparser');
在合适的位置加上:
app.use(bodyParser());
由于middleware的顺序很重要,这个koa-bodyparser必须在router之前被注册到app对象上。
现在我们就可以处理post请求了。写一个简单的登录表单:
router.get('/', async (ctx, next) => { ctx.response.body = `<h1>Index</h1> <form action="/signin" method="post"> <p>Name: <input name="name" value="koa"></p> <p>Password: <input name="password" type="password"></p> <p><input type="submit" value="Submit"></p> </form>`;});router.post('/signin', async (ctx, next) => { var name = ctx.request.body.name || '', password = ctx.request.body.password || ''; console.log(`signin with name: ${name}, password: ${password}`); if (name === 'koa' && password === '12345') { ctx.response.body = `<h1>Welcome, ${name}!</h1>`; } else { ctx.response.body = `<h1>Login failed!</h1> <p><a href="/">Try again</a></p>`; }});
注意到我们用var name = ctx.request.body.name || ”拿到表单的name字段,如果该字段不存在,默认值设置为”。
类似的,put、delete、head请求也可以由router处理。
重构
现在,我们已经可以处理不同的URL了,但是看看app.js,总觉得还是有点不对劲。
所有的URL处理函数都放到app.js里显得很乱,而且,每加一个URL,就需要修改app.js。随着URL越来越多,app.js就会越来越长。
如果能把URL处理函数集中到某个js文件,或者某几个js文件中就好了,然后让app.js自动导入所有处理URL的函数。这样,代码一分离,逻辑就显得清楚了。最好是这样:
url2-koa/|+- .vscode/| || +- launch.json <-- VSCode 配置文件|+- controllers/| || +- login.js <-- 处理login相关URL| || +- users.js <-- 处理用户管理相关URL|+- app.js <-- 使用koa的js|+- package.json <-- 项目描述文件|+- node_modules/ <-- npm安装的所有依赖包
于是我们把url-koa复制一份,重命名为url2-koa,准备重构这个项目。
我们先在controllers目录下编写index.js:
var fn_index = async (ctx, next) => { ctx.response.body = `<h1>Index</h1> <form action="/signin" method="post"> <p>Name: <input name="name" value="koa"></p> <p>Password: <input name="password" type="password"></p> <p><input type="submit" value="Submit"></p> </form>`;};var fn_signin = async (ctx, next) => { var name = ctx.request.body.name || '', password = ctx.request.body.password || ''; console.log(`signin with name: ${name}, password: ${password}`); if (name === 'koa' && password === '12345') { ctx.response.body = `<h1>Welcome, ${name}!</h1>`; } else { ctx.response.body = `<h1>Login failed!</h1> <p><a href="/">Try again</a></p>`; }};module.exports = { 'GET /': fn_index, 'POST /signin': fn_signin};
这个index.js通过module.exports把两个URL处理函数暴露出来。
类似的,hello.js把一个URL处理函数暴露出来:
var fn_hello = async (ctx, next) => { var name = ctx.params.name; ctx.response.body = `<h1>Hello, ${name}!</h1>`;};module.exports = { 'GET /hello/:name': fn_hello};
现在,我们修改app.js,让它自动扫描controllers目录,找到所有js文件,导入,然后注册每个URL:
// 先导入fs模块,然后用readdirSync列出文件// 这里可以用sync是因为启动时只运行一次,不存在性能问题:var files = fs.readdirSync(__dirname + '/controllers');// 过滤出.js文件:var js_files = files.filter((f)=>{ return f.endsWith('.js');});// 处理每个js文件:for (var f of js_files) { console.log(`process controller: ${f}...`); // 导入js文件: let mapping = require(__dirname + '/controllers/' + f); for (var url in mapping) { if (url.startsWith('GET ')) { // 如果url类似"GET xxx": var path = url.substring(4); router.get(path, mapping[url]); console.log(`register URL mapping: GET ${path}`); } else if (url.startsWith('POST ')) { // 如果url类似"POST xxx": var path = url.substring(5); router.post(path, mapping[url]); console.log(`register URL mapping: POST ${path}`); } else { // 无效的URL: console.log(`invalid URL: ${url}`); } }}
如果上面的大段代码看起来还是有点费劲,那就把它拆成更小单元的函数:
function addMapping(router, mapping) { for (var url in mapping) { if (url.startsWith('GET ')) { var path = url.substring(4); router.get(path, mapping[url]); console.log(`register URL mapping: GET ${path}`); } else if (url.startsWith('POST ')) { var path = url.substring(5); router.post(path, mapping[url]); console.log(`register URL mapping: POST ${path}`); } else { console.log(`invalid URL: ${url}`); } }}function addControllers(router) { var files = fs.readdirSync(__dirname + '/controllers'); var js_files = files.filter((f) => { return f.endsWith('.js'); }); for (var f of js_files) { console.log(`process controller: ${f}...`); let mapping = require(__dirname + '/controllers/' + f); addMapping(router, mapping); }}addControllers(router);
确保每个函数功能非常简单,一眼能看明白,是代码可维护的关键。
Controller Middleware
最后,我们把扫描controllers目录和创建router的代码从app.js中提取出来,作为一个简单的middleware使用,命名为controller.js:
const fs = require('fs');function addMapping(router, mapping) { ...}function addControllers(router, dir) { ...}module.exports = function (dir) { let controllers_dir = dir || 'controllers', // 如果不传参数,扫描目录默认为'controllers' router = require('koa-router')(); addControllers(router, controllers_dir); return router.routes();};
这样一来,我们在app.js的代码又简化了:
...// 导入controller middleware:const controller = require('./controller');...// 使用middleware:app.use(controller());...
经过重新整理后的工程url2-koa目前具备非常好的模块化,所有处理URL的函数按功能组存放在controllers目录,今后我们也只需要不断往这个目录下加东西就可以了,app.js保持不变。
- JavaScript基础之用koa处理url
- JavaScript基础之koa
- JavaScript基础之koa入门
- JavaScript之node.js-web框架Koa学习
- 【知识整理】Node.js-Koa之错误处理
- Javascript的URL处理
- JavaScript基础系列之七 错误处理
- nodeJs之koa入门
- koa之处理器模块化
- python的web 开发基础之url处理
- Koa
- HTTp基础之URL
- 【javaScript基础】异常处理
- JavaScript表单处理基础
- JavaScript基础之基础
- express和koa中的超时处理
- javascript 分析url处理搜索参数
- javascript传递url中文乱码处理
- php-laravel4.0框架 简单快速入门
- ID3决策树(R实现)
- Java反射机制
- 关于java io 的 write 与 操作系统
- 教程:简单几步制作出酷炫网页版简历
- JavaScript基础之用koa处理url
- 专用于高并发的map类-----Map的并发处理(ConcurrentHashMap)
- 自定义控件onDraw的paint
- kotlin学习笔记——类型、变量、属性
- JS中如何获取JSON子项的个数或叫length
- unity 转盘抽奖
- 关于IOS使用Cordova camera 无法压缩图片
- uiautomator 之UiSelector学习
- eclipse中git的安装、使用、问题