从定义表单-表单URL参数处理

  今天就算叙下好在从定义表单中之超过过程中URL参数传递的拍卖。

Koa是一个近似于Express的Web开发框架,创始人为是同一个人数。它的重要特点是,使用了ES6的Generator函数,进行了架的再规划。也就是说,Koa的法则与内部结构很像Express,但是语法和内部结构进行了晋升。

  平常的开销中,我们开展URL传参时,肯定还见面定义一个出意义之参数曰,让接受页面去Request获取,这未尝问题,但如是于自定义表单中,参数的名上相似还见面于的定势,系统识别的名称无非就是那么约定好之片,所以无克再次经更多的&符号来间隔的传递参数,那该如何处理为?我们好预定一定之格式,将参数进行数组化,通过固定的参数名传递,样子如下:PPI_ID|PPI20100304001,PSF_ID|PSF20101210002。

官方faq产生这般一个问题:”为什么koa不是Express
4.0?“,回答是这样的:”Koa与Express有良挺差异,整个规划还是见仁见智的,所以若将Express
3.0如约这种写法升级至4.0,就表示重写整个程序。所以,我们以为创造一个初的库,是重恰当的做法。“

  这样的参数名即简练而爱书写,而且系统可好好之辨认,很明朗的看出参数是透过”,”将依次参数进行分隔,再经”|”将参数名及参数值分隔,至于以URL中之名称即使吓从名叫了,就叫她Param了。

Koa应用

一个Koa应用即是一个靶,包含了一个middleware数组,这个数组由同样组Generator函数组成。这些函数负责对HTTP请求进行各种加工,比如生成缓存、指定代理、请求重定向等等。

1 var koa = require('koa');
2 var app = koa();
3 
4 app.use(function *(){
5   this.body = 'Hello World';
6 });
7 
8 app.listen(3000);

要安装koa才能测试

地方代码中,变量app就是一个Koa应用。它监听3000端口,返回一个内容为Hello
World的网页。

app.use方法用于为middleware数组添加Generator函数。

listen方法指定监听端口,并启动时采用。它实际等同于下的代码

1 var http = require('http');
2 var koa = require('koa');
3 var app = koa();
4 http.createServer(app.callback()).listen(3000);

  在收取页的拍卖地方,在这边是透过单独的参数近似来拍卖的,因为系统中之表单参数还是经过就类似措施传送,所以用到之地方得非常多,将参数功能独立化,也是挺便宜之,统一的法门去分析表单参数,更好之保安其。直接贴下代码吧:

中间件

下面是一个点滴个中等件级联的事例

1 app.use(function *() {
2   this.body = "header\n";
3   yield saveResults.call(this);
4   this.body += "footer\n";
5 });
6 
7 function *saveResults() {
8   this.body += "Results Saved!\n";
9 }

上面代码中,第一只中等件调用第二独中等件saveResults,它们都为this.body描绘副内容。最后,this.body的出口如下。

 1 header 2 Results Saved! 3 footer 

一经有一个当中件短少yield next言语,后面的中等件都不见面执行,这等同接触要引起注意。

假若想过了一个中件,可以直接在该中间件的首先执语句子写及return yield next

1 app.use(function* (next) {
2   if (skip) return yield next;
3 })
 1 /// <summary>
 2     /// 参数分隔辅助类,样式如:PPI_ID|PPI20100304001,PSF_ID|PSF20101210002,
 3     /// 提供对参数的分解及返回键值列表信息
 4     /// </summary>
 5     [Serializable]
 6     public class SplitParam
 7     {
 8         /// <summary>
 9         /// 参数字符串
10         /// </summary>
11         /// <param name="param"></param>
12         public SplitParam(string param)
13         {
14             this.Param = param;
15         }
16 
17         public override string ToString()
18         {
19             return Param;
20         }
21 
22         private string _param = string.Empty;
23         /// <summary>
24         /// 参数字符串
25         /// </summary>
26         public string Param
27         {
28             get { return _param; }
29             private set
30             {
31                 if (!string.IsNullOrEmpty(value))
32                 {
33                     _param = value;
34                 }
35             }
36         }
37 
38         /// <summary>
39         /// 保存解析过的参数列表
40         /// </summary>
41         private Dictionary<string, string> _paramList = null;
42 
43         /// <summary>
44         /// 获取解析后的参数列表信息,可用于参数替换等
45         /// </summary>
46         /// <returns></returns>
47         public Dictionary<string, string> GetParamList()
48         {
49             if (_paramList == null)
50             {
51                 _paramList = new Dictionary<string, string>();
52                 string[] listA = _param.Split(',');
53                 if (listA == null || listA.Length == 0)
54                 {
55                     return _paramList;
56                 }
57 
58                 string[] listB;
59                 foreach (string item in listA)
60                 {
61                     if (!string.IsNullOrEmpty(item))
62                     {
63                         listB = item.Split('|');
64                         if (listB != null & listB.Length > 1)
65                         {
66                             _paramList.Add(listB[0], listB[1]);
67                         }
68                     }
69                 }
70             }
71             return _paramList;
72         }
73 
74         /// <summary>
75         /// 根据键值获取获取值
76         /// </summary>
77         /// <param name="key"></param>
78         /// <returns></returns>
79         public string GetValueByKey(string key)
80         {
81             GetParamList();
82             if (_paramList != null && _paramList.ContainsKey(key))
83             {
84                 return _paramList[key];
85             }
86             return "";
87         }
88     }

路由

好透过this.path性能,判断用户要的路,从而从至路由作用。

 1 app.use(function* (next) {
 2   if (this.path === '/') {
 3     this.body = 'we are at home!';
 4   } else {
 5     yield next;
 6   }
 7 })
 8 
 9 // 等同于
10 
11 app.use(function* (next) {
12   if (this.path !== '/') return yield next;
13   this.body = 'we are at home!';
14 })

下面是大半路的例证。

 1 let koa = require('koa')
 2 
 3 let app = koa()
 4 
 5 // normal route
 6 app.use(function* (next) {
 7   if (this.path !== '/') {
 8     return yield next
 9   }
10 
11   this.body = 'hello world'
12 });
13 
14 // /404 route
15 app.use(function* (next) {
16   if (this.path !== '/404') {
17     return yield next;
18   }
19 
20   this.body = 'page not found'
21 });
22 
23 // /500 route
24 app.use(function* (next) {
25   if (this.path !== '/500') {
26     return yield next;
27   }
28 
29   this.body = 'internal server error'
30 });
31 
32 app.listen(8080)

方代码中,每一个中间件负责一个路子,如果路径不称,就传递给下一个中等件。

复杂的路由需要装koa-router插件。

 1 var app = require('koa')();
 2 var Router = require('koa-router');
 3 
 4 var myRouter = new Router();
 5 
 6 myRouter.get('/', function *(next) {
 7   this.response.body = 'Hello World!';
 8 });
 9 
10 app.use(myRouter.routes());
11 
12 app.listen(3000);

方代码对根本路径设置路由。

Koa-router实例提供相同密密麻麻动词方法,即一律种植HTTP动词对许平等种植方法。典型的动词方法有以下五栽。

  • router.get()
  • router.post()
  • router.put()
  • router.del()
  • router.patch()

这些动词方法好承受两单参数,第一单凡是途径模式,第二只是相应之控制器方法(中间件),定义用户要该路线时服务器行为。

1 router.get('/', function *(next) {
2   this.body = 'Hello World!';
3 });

上面代码中,router.get主意的第一独参数是根本路径,第二个参数是对应的函数方法。

在意,路径匹配的时,不见面管询问字符串考虑在内。比如,/index?param=xyz相当路径/index

聊路径模式比较复杂,Koa-router允许为路径模式起别名。起名时,别名要增长为动词方法的首先单参数,这时动词方法成为接受三独参数。

1 router.get('user', '/users/:id', function *(next) {
2  // ...
3 });

上面代码中,路径模式\users\:id的名字就是user。路径的名称,可以用来引用对应的具体路径,比如url方法可以根据路径名称,
结合给定的参数,生成具体的路径。

Koa-router允许为路径统一添加前缀。

1 var router = new Router({
2   prefix: '/users'
3 });
4 
5 router.get('/', ...); // 等同于"/users"
6 router.get('/:id', ...); // 等同于"/users/:id"

路线的参数经this.params特性获取,该属性返回一个目标,所有路线参数都是拖欠对象的成员。

// 访问 /programming/how-to-node
router.get('/:category/:title', function *(next) {
  console.log(this.params);
  // => { category: 'programming', title: 'how-to-node' }
});

param方法好对命名参数,设置验证条件。

 1 router
 2   .get('/users/:user', function *(next) {
 3     this.body = this.user;
 4   })
 5   .param('user', function *(id, next) {
 6     var users = [ '0号用户', '1号用户', '2号用户'];
 7     this.user = users[id];
 8     if (!this.user) return this.status = 404;
 9     yield next;
10   })

上面代码中,如果/users/:user的参数user对应之无是行用户(比如看/users/3),param方法注册之中间件会查到,就会回来404荒唐。

redirect方法会以某路径的请求,重定向到任何一个路,并返回301状态码。

1 router.redirect('/login', 'sign-in');
2 
3 // 等同于
4 router.all('/login', function *() {
5   this.redirect('/sign-in');
6   this.status = 301;
7 });

redirect方法之第一单参数是央来源,第二只参数是目的地,两者都好为此路模式之号代替。

此类将参数进行统一之管住,并提供方来因参数叫作博参数值。

错误处理机制

1 app.use(function *() {
2   try {
3     yield saveResults();
4   } catch (err) {
5     this.throw(400, '数据无效');
6   }
7 });

上面代码自行安排了try…catch代码块,一旦产生错误,就就此this.throw方法抛出。该措施好以点名的状态码和错误信息,返回给客户端。

于无捕获错误,可以装error事件之监听函数。

1 app.on('error', function(err){
2   log.error('server error', err);
3 });

this.throw方法用于为客户端抛来一个错。

 1 this.throw(403);
 2 this.throw('name required', 400);
 3 this.throw(400, 'name required');
 4 this.throw('something exploded');
 5 
 6 this.throw('name required', 400)
 7 // 等同于
 8 var err = new Error('name required');
 9 err.status = 400;
10 throw err;

this.throw措施的少数个参数,一个凡错误码,另一个凡报错信息。如果省略状态码,默认是500谬误。

this.assert办法用于在中等件中断言,用法类似于Node的assert模块

1 this.assert(this.user, 401, 'User not found. Please login!');

面代码中,如果this.user属性不设有,会丢来一个401左。

cookie

cookie的读取和装置。

1 this.cookies.get('view');
2 this.cookies.set('view', n);

get和set方法都得接受第三单参数,表示配置参数。其中的signed参数,用于指定cookie是否加密。如果指定加密的话,必须用app.keys指定加密短语。

1 app.keys = ['secret1', 'secret2'];
2 this.cookies.set('name', '张三', { signed: true });

this.cookie的安排对象的属性如下。

  • signed:cookie是否加密。
  • expires:cookie何时过
  • path:cookie的路径,默认是“/”。
  • domain:cookie的域名。
  • secure:cookie是否只有https请求下才发送。
  • httpOnly:是否只有服务器可以落到cookie,默认为true。

session

 1 var session = require('koa-session');
 2 var koa = require('koa');
 3 var app = koa();
 4 
 5 app.keys = ['some secret hurr'];
 6 app.use(session(app));
 7 
 8 app.use(function *(){
 9   var n = this.session.views || 0;
10   this.session.views = ++n;
11   this.body = n + ' views';
12 })
13 
14 app.listen(3000);
15 console.log('listening on port 3000');

 1 可以把session存到mysql中
 2 安装npm install koa-generic-session --save-dev
 3 2.app.js中
 4 var session = require('koa-generic-session');
 5 
 6 app.keys = ['my secret key'];  // needed for cookie-signing,设置一个签名 Cookie 的密钥
 7 app.use(session());
 8 
 9 3.
10 this.session.loginbean
11 
12 方法二:
13 session映射到mysql
14 1.加安装
15 npm install mysql --save-dev
16 npm install koa-mysql-session --save-dev
17 
18 app.js中:
19 var session = require('koa-generic-session');
20 const mysql = require('mysql');
21 const MysqlStore = require('koa-mysql-session');
22 
23 app.keys = ['my secret key'];  // needed for cookie-signing,设置一个签名 Cookie 的密钥
24 app.use(session({store:new MysqlStore({
25   host: 'localhost',       //主机  
26   user: 'root',               //MySQL认证用户名  
27   password: 'root',        //MySQL认证用户密码  
28   database: 'kameng',  
29   port: '3306',                   //端口号 
30   acquireTimeout:0
31 })}));

Request对象

Request对象表示HTTP请求。

(1)this.request.header

归来一个靶,包含有HTTP请求的峰信息。它也得以写成this.request.headers

(2)this.request.method

返HTTP请求的章程,该属性可读写。

(3)this.request.length

回到HTTP请求的Content-Length属性,取不交价值,则返回undefined。

(4)this.request.path

回来HTTP请求的路径,该属性可读写。

(5)this.request.href

返HTTP请求的整体路径,包括商、端口和url。

1 this.request.href
2 // http://example.com/foo/bar?q=1

(6)this.request.querystring

回来HTTP请求的询问字符串,不含问号。该属性可读写。

(7)this.request.search

回HTTP请求的查询字符串,含问号。该属性可读写。

(8)this.request.host

归来HTTP请求的主机(含端口号)。

(9)this.request.hostname

回去HTTP的主机名(不含端口号)。

(10)this.request.type

返回HTTP请求的Content-Type属性

1 var ct = this.request.type;
2 // "image/png"

(11)this.request.charset

回HTTP请求的字符集。

1 this.request.charset
2 // "utf-8

路由

可透过this.path特性,判断用户要的路,从而从及路由作用。

app.use(function* (next) {
  if (this.path === '/') {
    this.body = 'we are at home!';
  } else {
    yield next;
  }
})

// 等同于

app.use(function* (next) {
  if (this.path !== '/') return yield next;
  this.body = 'we are at home!';
})

下面是大抵路的例证。

let koa = require('koa')

let app = koa()

// normal route
app.use(function* (next) {
  if (this.path !== '/') {
    return yield next
  }

  this.body = 'hello world'
});

// /404 route
app.use(function* (next) {
  if (this.path !== '/404') {
    return yield next;
  }

  this.body = 'page not found'
});

// /500 route
app.use(function* (next) {
  if (this.path !== '/500') {
    return yield next;
  }

  this.body = 'internal server error'
});

app.listen(8080)

面代码中,每一个中间件负责一个路子,如果路径不符合,就传递给下一个中档件。

复杂的路由需要装koa-router插件。

var app = require('koa')();
var Router = require('koa-router');

var myRouter = new Router();

myRouter.get('/', function *(next) {
  this.response.body = 'Hello World!';
});

app.use(myRouter.routes());

app.listen(3000);

点代码对根路径设置路由。

Koa-router实例提供平等层层动词方法,即同种HTTP动词对诺同等栽方式。典型的动词方法发生以下五种植。

  • router.get()
  • router.post()
  • router.put()
  • router.del()
  • router.patch()

这些动词方法可接受两个参数,第一个凡是途径模式,第二单凡是呼应之控制器方法(中间件),定义用户请求该路线时服务器行为。

router.get('/', function *(next) {
  this.body = 'Hello World!';
});

方代码中,router.get措施的首先只参数是根路径,第二独参数是对应之函数方法。

小心,路径匹配的早晚,不会见将询问字符串考虑在内。比如,/index?param=xyz相当路径/index

微路径模式比较复杂,Koa-router允许为路径模式起别名。起名时,别名要增长为动词方法的率先个参数,这时动词方法成为接受三单参数。

router.get('user', '/users/:id', function *(next) {
 // ...
});

地方代码中,路径模式\users\:id的名便user。路径的称,可以据此来引用对应之切切实实路径,比如url方法可因路径名称,结合给定的参数,生成具体的路径。

router.url('user', 3);
// => "/users/3"

router.url('user', { id: 3 });
// => "/users/3"

点代码中,user就是路径模式的称号,对许切实路径/users/:id。url方法的第二只参数3,表示于定id的价是3,因此最终生成的不二法门是/users/3

Koa-router允许为路径统一上加前缀。

var router = new Router({
  prefix: '/users'
});

router.get('/', ...); // 等同于"/users"
router.get('/:id', ...); // 等同于"/users/:id"

途径的参数经this.params性获取,该属性返回一个目标,所有途径参数还是拖欠目标的积极分子。

// 访问 /programming/how-to-node
router.get('/:category/:title', function *(next) {
  console.log(this.params);
  // => { category: 'programming', title: 'how-to-node' }
});

param方法好对命名参数,设置验证条件。

router
  .get('/users/:user', function *(next) {
    this.body = this.user;
  })
  .param('user', function *(id, next) {
    var users = [ '0号用户', '1号用户', '2号用户'];
    this.user = users[id];
    if (!this.user) return this.status = 404;
    yield next;
  })

点代码中,如果/users/:user的参数user对应之不是可行用户(比如看/users/3),param方法注册之中游件会查到,就见面回来404谬误。

redirect方法会以有路径的乞求,重定向到任何一个途径,并回到301状态码。

router.redirect('/login', 'sign-in');

// 等同于
router.all('/login', function *() {
  this.redirect('/sign-in');
  this.status = 301;
});

redirect方法的第一单参数是要来源,第二只参数是目的地,两者都好为此路模式之号代替。

context对象

中件中的this表示上下文对象context,代表一致不善HTTP请求与回复,即一律糟糕做客/回应的装有消息,都得以自上下文对象获得。context对象封装了request和response对象,并且提供了有的援助方法。每次HTTP请求,就会创一个初的context对象。

app.use(function *(){
  this; // is the Context
  this.request; // is a koa Request
  this.response; // is a koa Response
});

context对象的很多主意,其实是概念在ctx.request对象要ctx.response对象方面,比如,ctx.type和ctx.length对应为ctx.response.type和ctx.response.length,ctx.path和ctx.method对承诺为ctx.request.path和ctx.request.method。

context对象的大局属性。

  • request:指向Request对象
  • response:指向Response对象
  • req:指向Node的request对象
  • res:指向Node的response对象
  • app:指向App对象
  • state:用于在中等件传递信息。

    this.state.user = yield User.find(id);

地方代码中,user属性存放于this.state目标方面,可以叫外一个中档件读取。

context对象的大局方法。

  • throw():抛来荒唐,直接控制了HTTP回应的状态码。
  • assert():如果一个表达式为false,则抛来一个错误。

    this.throw(403);
    this.throw(‘name required’, 400);
    this.throw(‘something exploded’);

    this.throw(400, ‘name required’);
    // 等同于
    var err = new Error(‘name required’);
    err.status = 400;
    throw err;

assert方法的起名例子。

// 格式
ctx.assert(value, [msg], [status], [properties])

// 例子
this.assert(this.user, 401, 'User not found. Please login!');

以下模块解析POST请求的数。

  • co-body
  • https://github.com/koajs/body-parser
  • https://github.com/koajs/body-parsers

    var parse = require(‘co-body’);

    // in Koa handler
    var body = yield parse(this);

错误处理机制

Koa提供放置的错误处理机制,任何中间件抛来之荒谬还见面给捕捉到,引发于客户端返回一个500错误,而不见面导致进程停止,因此也就非需forever这样的模块重开进程。

app.use(function *() {
  throw new Error();
});

点代码中,中间件之中抛来一个荒谬,并无见面招致Koa应用挂掉。Koa内置的错误处理机制,会捕捉到这个荒唐。

当然,也足以额外安排好之错误处理机制。

app.use(function *() {
  try {
    yield saveResults();
  } catch (err) {
    this.throw(400, '数据无效');
  }
});

方代码自行安排了try…catch代码块,一旦有错误,就用this.throw术抛出。该方法好以点名的状态码和错误信息,返回给客户端。

于无捕获错误,可以装error事件之监听函数。

app.on('error', function(err){
  log.error('server error', err);
});

error事件之监听函数还足以承受上下文对象,作为第二独参数。

app.on('error', function(err, ctx){
  log.error('server error', err, ctx);
});

倘一个荒谬没有被擒获,koa会于客户端返回一个500破绽百出“Internal Server
Error”。

this.throw方法用于为客户端抛来一个误。

this.throw(403);
this.throw('name required', 400);
this.throw(400, 'name required');
this.throw('something exploded');

this.throw('name required', 400)
// 等同于
var err = new Error('name required');
err.status = 400;
throw err;

this.throw艺术的片独参数,一个是错误码,另一个是报错信息。如果看略状态码,默认是500荒谬。

this.assert主意用于在中等件之中断言,用法类似于Node的assert模块。

this.assert(this.user, 401, 'User not found. Please login!');

方代码中,如果this.user属性不存,会废弃来一个401不当。

由于中等件是层级式调用,所以可以将try { yield next }真是第一独中等件。

app.use(function *(next) {
  try {
    yield next;
  } catch (err) {
    this.status = err.status || 500;
    this.body = err.message;
    this.app.emit('error', err, this);
  }
});

app.use(function *(next) {
  throw new Error('some error');
})

CSRF攻击

CSRF攻击是指用户的session被绑票,用来伪造用户之口诛笔伐。

koa-csrf插件用来预防CSRF攻击。原理是于session之中写副一个黑的token,用户每次用POST方法提交数据的时刻,必须含有这token,否则就见面扔来左。

 1 var koa = require('koa');
 2 var session = require('koa-session');
 3 var csrf = require('koa-csrf');
 4 var route = require('koa-route');
 5 
 6 var app = module.exports = koa();
 7 
 8 app.keys = ['session key', 'csrf example'];
 9 app.use(session(app));
10 
11 app.use(csrf());
12 
13 app.use(route.get('/token', token));
14 app.use(route.post('/post', post));
15 
16 function* token () {
17   this.body = this.csrf;
18 }
19 
20 function* post() {
21   this.body = {ok: true};
22 }
23 
24 app.listen(3000);

POST请求含有token,可以是以下几种植方法有,koa-csrf插件就是能博得token。

  • 表单的_csrf字段
  • 查询字符串的_csrf字段
  • HTTP请求头信息之x-csrf-token字段
  • HTTP请求头信息的x-xsrf-token字段 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图