nodejs后端相关知识总结

1.koa

koa执行逻辑代码:

app.use(async (ctx, next) => {
    await next();
    ctx.response.type = 'text/html';
    ctx.response.body = '<h1>Hello, koa2!</h1>';
});

每收到一个http请求,koa就会调用通过app.use()注册的async函数,并传入ctxnext参数。

我们可以对ctx操作,并设置返回内容。但是为什么要调用await next()

原因是koa把很多async函数组成一个处理链,每个async函数都可以做一些自己的事情,然后用await next()来调用下一个async函数。我们把每个async函数称为middleware,这些middleware可以组合起来,完成很多有用的功能。

middleware的顺序很重要,也就是调用app.use()的顺序决定了middleware的顺序。

此外,如果一个middleware没有调用await next(),会怎么办?答案是后续的middleware将不再执行了。这种情况也很常见,例如,一个检测用户权限的middleware可以决定是否继续处理请求,还是直接返回403错误:

app.use(async (ctx, next) => {
    if (await checkUserPermission(ctx)) {
        await next();
    } else {
        ctx.response.status = 403;
    }
});

最后注意ctx对象有一些简写的方法,例如ctx.url相当于ctx.request.urlctx.type相当于ctx.response.type

2.koa-router

为了处理URL,需要引入koa-router这个middleware,让它负责处理URL映射。 

 使用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')();

使用router.get('/path', async fn)来注册一个GET请求。可以在请求路径中使用带变量的/hello/:name,变量可以通过ctx.params.name访问。

3.koa-bodyparser处理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的功能!

所以,我们又需要引入koa-bodyparser来解析原始request请求,然后,把解析后的参数,绑定到ctx.request.body中。

引入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处理。

4.Nunjucks渲染模板

Nunjucks是一个模板引擎,即基于模板配合数据构造出字符串输出的一个组件,以下为一个模板引擎的例子:

function examResult (data) {
    return `${data.name}同学一年级期末考试语文${data.chinese}分,数学${data.math}分,位于年级第${data.ranking}名。`
}

如果我们输入数据如下:

examResult({
    name: '小明',
    chinese: 78,
    math: 87,
    ranking: 999
});

该模板引擎把模板字符串里面对应的变量替换以后,就可以得到以下输出:

小明同学一年级期末考试语文78分,数学87分,位于年级第999名。

模板引擎最常见的输出就是输出网页,也就是HTML文本。当然,也可以输出任意格式的文本,比如Text,XML,Markdown等等。

app.js中编写Nunjucks的函数render如下:

const nunjucks = require('nunjucks');

function createEnv(path, opts) {
    var
        autoescape = opts.autoescape === undefined ? true : opts.autoescape,
        noCache = opts.noCache || false,
        watch = opts.watch || false,
        throwOnUndefined = opts.throwOnUndefined || false,
        env = new nunjucks.Environment(
            new nunjucks.FileSystemLoader('views', {
                noCache: noCache,
                watch: watch,
            }), {
                autoescape: autoescape,
                throwOnUndefined: throwOnUndefined
            });
    if (opts.filters) {
        for (var f in opts.filters) {
            env.addFilter(f, opts.filters[f]);
        }
    }
    return env;
}

var env = createEnv('views', {
    watch: true,
    filters: {
        hex: function (n) {
            return '0x' + n.toString(16);
        }
    }
});

变量env就表示Nunjucks模板引擎对象,它有一个render(view, model)方法,正好传入viewmodel两个参数,并返回字符串。

创建env需要的参数可以查看文档获知。我们用autoescape = opts.autoescape && true这样的代码给每个参数加上默认值,最后使用new nunjucks.FileSystemLoader('views')创建一个文件系统加载器,从views目录读取模板。

我们编写一个hello.html模板文件,放到views目录下,内容如下:

<h1>Hello {{ name }}</h1>

然后,我们就可以用下面的代码来渲染这个模板:

var s = env.render('hello.html', { name: '小明' });
console.log(s);

获得输出如下:

<h1>Hello 小明</h1>

此外,可以使用Nunjucks提供的功能强大的tag,编写条件判断、循环等功能,例如:

<!-- 循环输出名字 -->
<body>
    <h3>Fruits List</h3>
    {% for f in fruits %}
    <p>{{ f }}</p>
    {% endfor %}
</body>

Nunjucks模板引擎最强大的功能在于模板的继承。仔细观察各种网站可以发现,网站的结构实际上是类似的,头部、尾部都是固定格式,只有中间页面部分内容不同。如果每个模板都重复头尾,一旦要修改头部或尾部,那就需要改动所有模板。

更好的方式是使用继承。先定义一个基本的网页框架base.html

<html><body>
{% block header %} <h3>Unnamed</h3> {% endblock %}
{% block body %} <div>No body</div> {% endblock %}
{% block footer %} <div>copyright</div> {% endblock %}
</body>

base.html定义了三个可编辑的块,分别命名为headerbodyfooter。子模板可以有选择地对块进行重新定义:

{% extends 'base.html' %}

{% block header %}<h1>{{ header }}</h1>{% endblock %}

{% block body %}<p>{{ body }}</p>{% endblock %}

然后,我们对子模板进行渲染:

console.log(env.render('extend.html', {
    header: 'Hello',
    body: 'bla bla bla...'
}));

输出HTML如下:

<html><body>
<h1>Hello</h1>
<p>bla bla bla...</p>
<div>copyright</div> <-- footer没有重定义,所以仍使用父模板的内容
</body>

5.使用MVC

当用户通过浏览器请求一个URL时,koa将调用某个异步函数处理该URL。在这个异步函数内部,我们用一行代码:

ctx.render('home.html', { name: 'Michael' });

通过Nunjucks把数据用指定的模板渲染成HTML,然后输出给浏览器,用户就可以看到渲染后的页面了:

mvc

这就是传说中的MVC:Model-View-Controller,中文名“模型-视图-控制器”。

异步函数是C:Controller,Controller负责业务逻辑,比如检查用户名是否存在,取出用户信息等等;

包含变量{{ name }}的模板就是V:View,View负责显示逻辑,通过简单地替换一些变量,View最终输出的就是用户看到的HTML。

MVC中的Model在哪?Model是用来传给View的,这样View在替换变量的时候,就可以从Model中取出相应的数据。

上面的例子中,Model就是一个JavaScript对象:

{ name: 'Michael' }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值