Koa處理url


一、koa-router

  為了處理URL,我們需要引入koa-router這個middleware,讓它負責處理URL映射。

  我們修改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();

二、處理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就是用來干這個活的。使用npm install安裝。

  接下來,修改app.js,引入koa-bodyparser

const bodyParser = require('koa-bodyparser');

  在合適的位置加上

app.use(bodyParser());

  由於middleware的順序很重要,這個koa-bodyparser必須在router之前被注冊到app對象上

三、重構

  所有的URL處理函數都放到app.js里顯得很亂,而且,每加一個URL,就需要修改app.js。隨着URL越來越多,app.js就會越來越長。

  如果能把URL處理函數集中到某個js文件,或者某幾個js文件中就好了,然后讓app.js自動導入所有處理URL的函數。這樣,代碼一分離,邏輯就顯得清楚了。最好是這樣:

url2-koa/
+- controllers/
|  |
|  +- login.js <-- 處理login相關URL |  |
|  +- users.js <-- 處理用戶管理相關URL |
+- app.js <-- 使用koa的js |
+- package.json <-- 項目描述文件 |
+- node_modules/ <-- npm安裝的所有依賴包

  我們先在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}`); } } }

四、Controller Middleware

  最后,我們把掃描controllers目錄和創建router的代碼從app.js中提取出來,作為一個簡單的middleware使用,命名為controller.js

const fs = require("fs") 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, dir) { // 先導入fs模塊,然后用readdirSync列出文件 // 這里可以用sync是因為啟動時只運行一次,不存在性能問題:
    var files = fs.readdirSync(__dirname + dir); // 過濾出.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); addMapping(router, mapping); } } module.exports = function (dir) { let controllers_dir = dir || '/controllers', router = require("koa-router")() addControllers(router, controllers_dir) return router.routes() }

  這樣一來,我們在app.js的代碼又簡化了:

const Koa = require("koa") const bodyParser = require("koa-bodyparser") const controller = require("./controller") const app = new Koa() app.use(bodyParser()) // add router middleware:
app.use(controller()); app.listen(8080) console.log("app started at port 8080")

  經過重新整理后的工程url2-koa目前具備非常好的模塊化,所有處理URL的函數按功能組存放在controllers目錄,今后我們也只需要不斷往這個目錄下加東西就可以了,app.js保持不變。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM