<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	
	xmlns:georss="http://www.georss.org/georss"
	xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
	>

<channel>
	<title>node.js &#8211; Blog of Code</title>
	<atom:link href="https://www.cztcode.com/category/web-build/node-js/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.cztcode.com</link>
	<description></description>
	<lastBuildDate>Wed, 12 Aug 2020 08:50:00 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://www.cztcode.com/wp-content/uploads/2024/02/cropped-logo-32x32.webp</url>
	<title>node.js &#8211; Blog of Code</title>
	<link>https://www.cztcode.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">217219486</site>	<item>
		<title>Mongoose中_id类型导致的bug</title>
		<link>https://www.cztcode.com/2020/3369/</link>
					<comments>https://www.cztcode.com/2020/3369/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Wed, 12 Aug 2020 08:50:00 +0000</pubDate>
				<category><![CDATA[node.js]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3369</guid>

					<description><![CDATA[最近使用Mongoose进行数据库连接。使用postman进行测试非法的id长度时报了如下错误。]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p class="is-style-iw-2em">最近使用Mongoose进行数据库连接。使用postman进行测试非法的id长度时报了如下错误。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-33.png" alt="" class="wp-image-3370"/></figure>



<p class="is-style-iw-2em">刚开始我是不明白这个报的是什么错，经过查找后发现错误在这句代码。</p>



<pre class="wp-block-code"><code> const user=await User.findById(ctx.params.id);</code></pre>



<p class="is-style-iw-2em">也就是会把传进来的id在数据库中查询。这个findByid默认就是查询_id字段。而_id是mongoose.Types.ObjectId类型</p>



<p class="is-style-iw-2em">ObjectId 是一个12字节 BSON 类型数据，有以下格式：</p>



<ul class="wp-block-list"><li>前4个字节表示时间戳</li><li>接下来的3个字节是机器标识码</li><li>紧接的两个字节由进程id组成（PID）</li><li>最后三个字节是随机数。</li></ul>



<p class="is-style-iw-2em">MongoDB中存储的文档必须有一个&#8221;_id&#8221;键。这个键的值可以是任何类型的，默认是个ObjectId对象。</p>



<p class="is-style-iw-2em">在一个集合里面，每个文档都有唯一的&#8221;_id&#8221;值，来确保集合里面每个文档都能被唯一标识。</p>



<p class="is-style-iw-2em">_id的长度是24位，上面那个报错是因为长度不正确导致查询的时候无法转化成ObjectId类型。这里加一个长度判断就可以了！</p>



<pre class="wp-block-code"><code> async checkUserExist(ctx,next){
        if(ctx.params.id.length!=24){
            ctx.throw(404,"没有此用户,id长度不匹配");
        }
        const user=await User.findById(ctx.params.id);//数据库查询必须使用await，否则不等到查询完成就执行下面语句
    
      
        if(!user){
            ctx.throw(404,"没有此用户");
        }
        await next();
    }</code></pre>



<p class="is-style-iw-2em"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3369/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3369</post-id>	</item>
		<item>
		<title>koa实现jwt用户登录和授权</title>
		<link>https://www.cztcode.com/2020/3323/</link>
					<comments>https://www.cztcode.com/2020/3323/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Sun, 09 Aug 2020 15:12:46 +0000</pubDate>
				<category><![CDATA[node.js]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3323</guid>

					<description><![CDATA[我们使用jsonwebtoken来生成和验证token，然后手写一个登录和授权的逻辑。使用koa-jwt中间件]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p class="is-style-iw-2em">我们使用jsonwebtoken来生成和验证token，然后手写一个登录和授权的逻辑。使用koa-jwt中间件。</p>



<p class="is-style-iw-2em">参考文章：<a href="https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html" class="rank-math-link" target="_blank" rel="noopener">JSON Web Token 入门教程</a></p>



<h2 class="wp-block-heading">JWT原理</h2>



<p class="is-style-iw-2em">JWT 的原理是，服务器认证以后，生成一个 JSON 对象，发回给用户，就像下面这样。</p>



<pre class="wp-block-code"><code>
{
  "姓名": "张三",
  "角色": "管理员",
  "到期时间": "2018年7月1日0点0分"
}
</code></pre>



<p class="is-style-iw-2em">以后，用户与服务端通信的时候，都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据，服务器在生成这个对象的时候，会加上签名。</p>



<h3 class="wp-block-heading">JWT 的三个部分</h3>



<p class="is-style-iw-2em">使用JWT使用token作为验证信息，比如下面这个就是一个token</p>



<pre class="wp-block-code"><code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOiI1ZjMwMDE4OTg1NzcxYTUwNjQwOWU1MjQiLCJuYW1lIjoid3d3IiwiaWF0IjoxNTk2OTgxNjc4LCJleHAiOjE1OTcwNjgwNzh9.3wM-YYxZ2ccAzCwvC4jUVQUSkaeMRuEjuAdZocQAyWA</code></pre>



<p class="is-style-iw-2em">这个长的字符串由两个小数点分割成三个部分</p>



<ul class="wp-block-list"><li>Header（头部）</li><li>Payload（负载）</li><li>Signature（签名）</li></ul>



<h3 class="wp-block-heading">Header</h3>



<p class="is-style-iw-2em">Header 部分是一个 JSON 对象，描述 JWT 的元数据，通常是下面的样子。</p>



<pre class="wp-block-code"><code>
{
  "alg": "HS256",
  "typ": "JWT"
}
</code></pre>



<p class="is-style-iw-2em">上面代码中，<code>alg</code>属性表示签名的算法（algorithm），默认是 HMAC SHA256（写成 HS256）；<code>typ</code>属性表示这个令牌（token）的类型（type），JWT 令牌统一写为<code>JWT</code>。</p>



<p class="is-style-iw-2em">最后，将上面的 JSON 对象使用 Base64URL 算法转成字符串。</p>



<h3 class="wp-block-heading">Payload</h3>



<p class="is-style-iw-2em">Payload 部分也是一个 JSON 对象，用来存放实际需要传递的数据。JWT 规定了7个官方字段供选用。Payload就是有效载荷，传递的主要内容放置在这里。</p>



<ul class="wp-block-list"><li>iss (issuer)：签发人</li><li>exp (expiration time)：过期时间</li><li>sub (subject)：主题</li><li>aud (audience)：受众</li><li>nbf (Not Before)：生效时间</li><li>iat (Issued At)：签发时间</li><li>jti (JWT ID)：编号</li></ul>



<p class="is-style-iw-2em">除了官方字段，你还可以在这个部分定义私有字段，下面就是一个例子。</p>



<pre class="wp-block-code"><code>
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}
</code></pre>



<p class="is-style-iw-2em">注意，JWT 默认是不加密的，任何人都可以读到，任何放在token里面的信息，如果被截获了，对任何人别人是可读的。因此，我们不应该在Payload里面存放任何黑客可以利用的用户信息。但是可以使用RSA加密算法得到加密。</p>



<p class="is-style-iw-2em">这个 JSON 对象也要使用 Base64URL 算法转成字符串。</p>



<h3 class="wp-block-heading">Signature</h3>



<p class="is-style-iw-2em">Signature 部分是对前两部分的签名，防止数据篡改。</p>



<p class="is-style-iw-2em">首先，需要指定一个密钥（secret）。这个密钥只有服务器才知道，不能泄露给用户。然后，使用 Header 里面指定的签名算法（默认是 HMAC SHA256），按照下面的公式产生签名。</p>



<pre class="wp-block-code"><code>
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)
</code></pre>



<p class="is-style-iw-2em">算出签名以后，把 Header、Payload、Signature 三个部分拼成一个字符串，每个部分之间用&#8221;点&#8221;（<code>.</code>）分隔，就可以返回给用户。</p>



<h3 class="wp-block-heading">验证过程</h3>



<ol class="wp-block-list"><li>用户向认证服务器提交用户名和密码，认证服务器也可以和应用服务器部署在一起，但往往是独立的居多；</li><li>认证服务器校验用户名和密码组合，然后创建一个JWT token，token的Payload里面包含用户的身份信息，以及过期时间戳；</li><li>认证服务器使用密钥对Header和Payload进行签名，然后发送给客户浏览器；</li><li>浏览器获取到经过签名的JWT token，然后在之后的每个HTTP请求中附带着发送给应用服务器。经过签名的JWT就像一个临时的用户凭证，代替了用户名和密码组合，之后都是JWT token和应用服务器打交道了；</li><li>应用服务器检查JWT签名，确认Payload确实是由密钥拥有者签过名的；</li><li>Payload身份信息代表了某个用户；</li><li>只有认证服务器拥有私钥，并且认证服务器只把token发给提供了正确密码的用户；</li><li>因此应用服务器可以认为这个token是由认证服务器颁发的也是安全的，因为该用户具有了正确的密码；</li><li>应用服务器继续完成HTTP请求，并认为这些请求确实属于这个用户；</li></ol>



<p class="is-style-iw-2em">这样的话，黑客假扮合法用户的办法要么是盗到了用户名和密码组合，要么盗到了认证服务器上的签名私钥。</p>



<h2 class="wp-block-heading">操作步骤</h2>



<h4 class="wp-block-heading">实现登录</h4>



<p class="is-style-iw-2em">写一个登录逻辑，首先验证传进来的数据类型是否正确，然后在数据库中查询这个用户。如果找到了就把user中的id和name取出。使用jsonwebtoken模块加密。secret是提前保存的密钥，就是一个string。</p>



<pre class="wp-block-code"><code> async login(ctx) {
        ctx.verifyParams({
            name: {type: `string`, required: true}, //默认require也是true
            password: {type: `string`, required: true},
        })
        const user = await User.findOne(ctx.request.body);
        if (!user) {
            ctx.throw(401, "账号密码不正确");
        }
        const {_id, name} = user;
        const token = jsonwebtoken.sign({_id, name}, secret, {expiresIn: `1d`});//有效时间1d
        ctx.body = {token};
   }
</code></pre>



<p class="is-style-iw-2em">这样登陆成功就会返回一个token</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-29.png" alt="" class="wp-image-3324"/></figure>



<p class="is-style-iw-2em">账号密码不正确，也会报错。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-30.png" alt="" class="wp-image-3325"/></figure>



<h3 class="wp-block-heading">实现授权</h3>



<p class="is-style-iw-2em">首先写一个授权中间件，目的是当请求某个特定的操作时，比如delete和update。需要识别这个用户，并将验证信息解码保存到ctx.state.user</p>



<pre class="wp-block-code"><code>const auth=async (ctx,next)=>{
    const {authorization=''}=ctx.request.header;
    const token=authorization.replace("Bearer ",'');//token默认有一个Bearer头
    try {
        const user=jsonwebtoken.verify(token,secret);
        ctx.state.user=user;  //约定
    }catch (err){
        ctx.throw(401,err.message);//401验证不符合
    }
    await next();
}</code></pre>



<p class="is-style-iw-2em">然后写一个验证中间件，上一个中间件会把用户请求附带的token解码成用户信息，验证中间件可以根据这个信息判断用户是不是有这个权限来进行操作。</p>



<pre class="wp-block-code"><code>   async checkOwner(ctx, next) {
        if (ctx.params.id != ctx.state.users._id) {
            ctx.throw(4.3, "没有权限");
        }
        await next();
    }</code></pre>



<p class="is-style-iw-2em">这样在进行敏感操作时，就可以保证用户只能修改自己的信息了</p>



<pre class="wp-block-code"><code>router.patch(`/:id`,auth,checkOwner,update)  //put是整体替换
router.delete(`/:id`,auth,checkOwner,deleteByid)</code></pre>



<h3 class="wp-block-heading">使用koa-jwt中间件</h3>



<p class="is-style-iw-2em">koa-jwt中间件为我们写好了auth逻辑，只需要简单传递secret即可</p>



<pre class="wp-block-code"><code>const jwt=require("koa-jwt");
const auth=jwt({secret});</code></pre>



<p class="is-style-iw-2em">效果和</p>



<pre class="wp-block-code"><code>const auth=async (ctx,next)=>{
    const {authorization=''}=ctx.request.header;
    const token=authorization.replace("Bearer ",'');//token默认有一个Bearer头
    try {
        const user=jsonwebtoken.verify(token,secret);
        ctx.state.user=user;  //约定
    }catch (err){
        ctx.throw(401,err.message);//401验证不符合
    }
    await next();
}</code></pre>



<p class="is-style-iw-2em">一样，其它不需要任何改动。</p>



<h3 class="wp-block-heading">使用postman验证接口</h3>



<p class="is-style-iw-2em">postman如果手动输入token很麻烦，这里写了一段程序可以方便的帮助我们添加token。</p>



<pre class="wp-block-code"><code>var jsonData=pm.response.json();
pm.globals.set("token",jsonData.token);</code></pre>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-31.png" alt="" class="wp-image-3333"/></figure>



<p class="is-style-iw-2em">在需要输入token的地方，使用这个全局变量即可</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-32.png" alt="" class="wp-image-3334"/></figure>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3323/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3323</post-id>	</item>
		<item>
		<title>mongoose实现CRUD</title>
		<link>https://www.cztcode.com/2020/3302/</link>
					<comments>https://www.cztcode.com/2020/3302/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Sat, 08 Aug 2020 15:13:52 +0000</pubDate>
				<category><![CDATA[node.js]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3302</guid>

					<description><![CDATA[一般我们不直接用MongoDB的函数来操作MongoDB数据库 Mongose就是一套操作MongoDB数据库的接口。]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p class="is-style-iw-2em">一般我们不直接用MongoDB的函数来操作MongoDB数据库 Mongose就是一套操作MongoDB数据库的接口。</p>



<h2 class="wp-block-heading">配置mongoose</h2>



<p class="is-style-iw-2em">链接mongoDB，这里我使用的是mongoDB Atlas免费服务。</p>



<pre class="wp-block-code"><code>const mongoose=require("mongoose");
const {connectionStr}=require("./config");

mongoose.connect(connectionStr,{ useNewUrlParser: true,useUnifiedTopology: true },()=>{
    console.log("数据库链接成功");
});
mongoose.connection.on(`error`,console.error);
mongoose.set('useFindAndModify', false);</code></pre>



<p class="is-style-iw-2em">把数据库链接String放到config配置文件中去了。</p>



<h2 class="wp-block-heading">创建Schema和model</h2>



<p class="is-style-iw-2em">Schema是一个骨架，里面定义着数据类型。</p>



<pre class="wp-block-code"><code>const mongoose=require("mongoose");

const {Schema,model}=mongoose;

const userSchema=new Schema({
    name:{type: String,required:true}, //默认require也是true
    age:{type: Number,required: false}
})

module.exports=model('User',userSchema); //模型的别名，模型就是模式的实例化</code></pre>



<h2 class="wp-block-heading">增删改查举例</h2>



<p class="is-style-iw-2em">还是很清晰的，就不一一解释了，注意数据库查询要使用异步方式，否则会阻塞进程。</p>



<pre class="wp-block-code"><code>const User = require("../models/users.js");

class UserRoute {
    async create(ctx) {
        ctx.verifyParams({
            name: {type: `string`, required: true}, //默认require也是true
            age: {type: `number`, required: false}
        })
        const user=await new User(ctx.request.body).save();
        ctx.body=user;
    }

    async update(ctx) {
        ctx.verifyParams({
            name: {type: `string`, required: true}, //默认require也是true
            age: {type: `number`, required: false}
        })
        const user=await User.findByIdAndUpdate(ctx.params.id,ctx.request.body,{new:true});
        if (!user)
            ctx.throw(404, "用户不存在");
        ctx.body=user;
    }

    async deleteByid(ctx) {
       const user=await User.findByIdAndRemove(ctx.params.id);
        if (!user)
            ctx.throw(404, "用户不存在");
        ctx.status = 204;
    }

    async findByid(ctx) {
        const user = await User.findById(ctx.params.id);
        if (!user)
            ctx.throw(404, "用户不存在");
        ctx.body = user;
    }

    async getAll(ctx) {
        ctx.body = await User.find();
    }
}

module.exports = new UserRoute();</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3302/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3302</post-id>	</item>
		<item>
		<title>koa-parameter</title>
		<link>https://www.cztcode.com/2020/3297/</link>
					<comments>https://www.cztcode.com/2020/3297/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Sat, 08 Aug 2020 09:04:16 +0000</pubDate>
				<category><![CDATA[node.js]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3297</guid>

					<description><![CDATA[koa-parameter是一个校验参数内容合法性的中间件。]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p class="is-style-iw-2em">koa-parameter是一个校验参数内容合法性的中间件。</p>



<p class="is-style-iw-2em">使用时放在koa-body-parser后面。校验request.body，传入参数app使这个中间件可以作为全局使用。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-24.png" alt="" class="wp-image-3298"/></figure>



<p class="is-style-iw-2em">使用时，只需要在需要获取request内容时校验对应的数据类型即可。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-25.png" alt="" class="wp-image-3299"/></figure>



<p class="is-style-iw-2em">不合法时返回422错误。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-26.png" alt="" class="wp-image-3300"/></figure>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3297/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3297</post-id>	</item>
		<item>
		<title>koa-json-error</title>
		<link>https://www.cztcode.com/2020/3291/</link>
					<comments>https://www.cztcode.com/2020/3291/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Sat, 08 Aug 2020 08:29:20 +0000</pubDate>
				<category><![CDATA[node.js]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3291</guid>

					<description><![CDATA[在写接口时，返回json格式且易读的错误提示是有必要的，koa-json-error中间件帮我们做到了。 412错误 先决条件出错 返回错误信息 比如我们请求了一个不存在的数据，自动返回412和错误信息。 可以看到，返回了对栈信息，但很多时候我们并不想告诉别人内部是哪里出错。 这里使用环境变量来配置，如果为生产环境，不返回stack的信息。如果为调试环境则全返回。 在intellij中，可以方便的 [&#8230;]]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p class="is-style-iw-2em">在写接口时，返回json格式且易读的错误提示是有必要的，koa-json-error中间件帮我们做到了。</p>



<h4 class="wp-block-heading">412错误 先决条件出错</h4>



<pre class="wp-block-code"><code>// code 
ctx.throw(412, '先决条件失败：id大于数据长度')
复制代码</code></pre>



<p class="is-style-iw-2em">返回错误信息</p>



<pre class="wp-block-code"><code>{
    "message":"先决条件失败：id大于数据长度",
    "name":"PreconditionFailedError",
    "status":412
}</code></pre>



<p class="is-style-iw-2em">比如我们请求了一个不存在的数据，自动返回412和错误信息。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-21.png" alt="" class="wp-image-3293"/></figure>



<p class="is-style-iw-2em">可以看到，返回了对栈信息，但很多时候我们并不想告诉别人内部是哪里出错。</p>



<p class="is-style-iw-2em">这里使用环境变量来配置，如果为生产环境，不返回stack的信息。如果为调试环境则全返回。</p>



<pre class="wp-block-code"><code>app.use(error({
    postFormat: (e,{stack,...rest})=>process.env.NODE_ENV===`production`?rest:{stack,...rest}
}));</code></pre>



<p class="is-style-iw-2em">在intellij中，可以方便的添加环境变量</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-22.png" alt="" class="wp-image-3294"/></figure>



<p class="is-style-iw-2em">这样获得请求的信息就是安全的了</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-23.png" alt="" class="wp-image-3295"/></figure>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3291/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3291</post-id>	</item>
		<item>
		<title>Koa的简易目录结构设计</title>
		<link>https://www.cztcode.com/2020/3288/</link>
					<comments>https://www.cztcode.com/2020/3288/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Sat, 08 Aug 2020 08:22:58 +0000</pubDate>
				<category><![CDATA[node.js]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3288</guid>

					<description><![CDATA[Koa的简易目录结构设计]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p class="is-style-iw-2em">koa-generator：koa-生成器是一个npm。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>&nbsp;&nbsp;&nbsp;&nbsp;1.打开cmd进入<strong>npm install -g koa-generator</strong>安装。</p><p>&nbsp;&nbsp;&nbsp;&nbsp;2.进入想要放项目的文件位置<strong>koa projectname。</strong></p></blockquote>



<p class="is-style-iw-2em">使用上面的方法可以自动生成一个目录结构，为了一步一步讲解。我们手动生成，因为我们暂时不需要自动生成的结构中的一些文件夹。</p>



<h2 class="wp-block-heading">控制器和路由</h2>



<p class="is-style-iw-2em">主要分两个部分，下面是设置好后的结构。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-19.png" alt="" class="wp-image-3289"/></figure>



<h3 class="wp-block-heading">控制器</h3>



<p class="is-style-iw-2em">控制器用来对链接进行操作，这里没链接数据库举了一个例子。user.js文件</p>



<pre class="wp-block-code"><code>let db=&#91;];

class UserRoute{
    create(ctx){
        db.push(ctx.request.body);
        ctx.body=ctx.request.body;
    }
    update(ctx){
        db&#91;ctx.params.id]=ctx.request.body;
        ctx.status=200;
    }
    deleteByid(ctx){
        db.splice(+ctx.params.id,1);
        ctx.status=204;
    }
    findByid(ctx){
        if(+ctx.params.id>=db.length){
            ctx.throw(412);
        }
        ctx.body = db&#91;+ctx.params.id];
    }
    getAll(ctx){
        ctx.body=db;
    }
}

module.exports=new UserRoute();</code></pre>



<p class="is-style-iw-2em">这个文件处理用户的操作，可以看到CRUD。通过一个类中不同的方法实现操作，传入ctx参数得到数据。</p>



<h3 class="wp-block-heading">路由</h3>



<p class="is-style-iw-2em">user.js的路由，包含进控制器的user.js。也很好理解。</p>



<pre class="wp-block-code"><code>const Router = require("koa-router");

const router = new Router({prefix: `/users`});
const {create,update,deleteByid,findByid,getAll}=require("../controllers/users.js");

router.get('/:id',findByid)
router.post(`/`,create)
router.put(`/:id`,update)
router.delete(`/:id`,deleteByid)
router.get('/',getAll);

module.exports=router;</code></pre>



<p class="is-style-iw-2em">如果我们有很多路由，将每个路由文件都添加到app.js中就太麻烦了，这里写一个自动化脚本帮我们导入。 index.js：</p>



<pre class="wp-block-code"><code>const fs = require("fs");
module.exports = (app) => {
    fs.readdirSync(__dirname).forEach(file => {
        if (file === "index.js")
            return;
        const route = require(`./${file}`);
        app .use(route.routes()).use(route.allowedMethods());
    })
}</code></pre>



<h3 class="wp-block-heading">app.js</h3>



<p class="is-style-iw-2em">在app.js中，集成我们需要的模块，注意模块之间是有顺序关系的，如果下一个模块使用了上一个模块，则排在后面。</p>



<pre class="wp-block-code"><code>"use strict"

const Koa = require("koa");
const bodyparser=require("koa-body-parser");
const routing=require('./routes/index.js');
const error=require("koa-json-error");
const app = new Koa();
 
app.use(error());//中间件加入有顺序
app.use(bodyparser());
routing(app);
app.listen(3000,()=>{console.log("成功启动")});</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3288/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3288</post-id>	</item>
		<item>
		<title>OPTION请求</title>
		<link>https://www.cztcode.com/2020/3260/</link>
					<comments>https://www.cztcode.com/2020/3260/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Fri, 07 Aug 2020 04:16:44 +0000</pubDate>
				<category><![CDATA[node.js]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3260</guid>

					<description><![CDATA[获取服务器支持的HTTP请求方法。
用来检查服务器的性能。例如：AJAX进行跨域请求时的预检，需要向另外一个域名的资源发送一个HTTP OPTIONS请求头，用以判断实际发送的请求是否安全。]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p class="is-style-iw-2em">OPTIONS请求方法的主要用途有两个：</p>



<ol class="wp-block-list"><li>获取服务器支持的HTTP请求方法。</li><li>用来检查服务器的性能。例如：AJAX进行跨域请求时的预检，需要向另外一个域名的资源发送一个HTTP OPTIONS请求头，用以判断实际发送的请求是否安全。</li></ol>



<h2 class="wp-block-heading">在koa-router中相应option请求</h2>



<h3 class="wp-block-heading">获取返回的类型</h3>



<p class="is-style-iw-2em">在koa-router方法中，使用router.allowedMethods() 为某个router开启option请求。</p>



<pre class="wp-block-preformatted">var Koa = require('koa');
var Router = require('koa-router');

var app = new Koa();
var router = new Router();

app.use(router.routes());
app.use(router.allowedMethods());</pre>



<p class="is-style-iw-2em">这样当我们访问时，就可以使用option方式来查看这个链接支持哪些请求。支持的类型在Header中</p>



<pre class="wp-block-code"><code>
usersRouter.get(`/`,(ctx)=>{
    ctx.body="users"
})
app.use(usersRouter.routes());
app.use(usersRouter.allowedMethods());</code></pre>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-14.png" alt="" class="wp-image-3262"/></figure>



<p class="is-style-iw-2em">可以看到，users路由支持get请求。</p>



<h2 class="wp-block-heading">405和501状态码</h2>



<h2 class="wp-block-heading">405</h2>



<p class="is-style-iw-2em">405是说，这个请求方法（GET、PUT、DELETE）我们api server认识，但是对于你请求的资源（URI），你使用的方法我们不支持，这叫做方法不允许，同时response要包含一个Allow头，返回支持的HTTP请求方法。</p>



<p class="is-style-iw-2em">比如上面的链接只支持get方法，但如果我们请求了post方法就会返回405.</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-15.png" alt="" class="wp-image-3263"/></figure>



<h2 class="wp-block-heading">501</h2>



<p class="is-style-iw-2em">501是说，你的请求方法（例如PROPFIND方法），我们api server不认识（无法进行匹配），因此叫做未实现。 返回501主要是受限制于API Server实现。</p>



<p class="is-style-iw-2em">请求Link方法就会得到501，因为koa-router只支持常用的方法，不支持这个方法。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-16.png" alt="" class="wp-image-3264"/></figure>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3260/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3260</post-id>	</item>
		<item>
		<title>koa-router</title>
		<link>https://www.cztcode.com/2020/3248/</link>
					<comments>https://www.cztcode.com/2020/3248/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Thu, 06 Aug 2020 14:43:30 +0000</pubDate>
				<category><![CDATA[node.js]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3248</guid>

					<description><![CDATA[koa-router实现方便的路由跳转]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p class="is-style-iw-2em">koa-router可以很方便的写出路由跳转的代码。</p>



<pre class="wp-block-code"><code>安装koa                   cnpm install koa --save 

安装koa-router         cnpm install koa-router --save</code></pre>



<p class="is-style-iw-2em">放个api文档，不过我有一点补充鉴权的部分。</p>



<p class="is-style-iw-2em"><a href="https://github.com/ZijianHe/koa-router" class="rank-math-link" target="_blank" rel="noopener">https://github.com/ZijianHe/koa-router</a></p>



<p class="is-style-iw-2em"><a href="http://zhangxiang958.github.io/2018/06/03/%E5%85%A8%E9%9D%A2%E7%90%86%E8%A7%A3%20koa-router/" class="rank-math-link" target="_blank" rel="noopener">koa-router源码详解</a></p>



<h2 class="wp-block-heading">简单示例</h2>



<p class="is-style-iw-2em">我们先写一个get请求，</p>



<pre class="wp-block-code"><code>"use strict"

const Koa = require("koa");
const  Router=require("koa-router");
const app = new Koa();
const router=new Router();

router.get('/',(ctx)=>{
    ctx.body="主页";
})

app.use(router.routes());//将中间件加入app中
app.listen(3000);</code></pre>



<p class="is-style-iw-2em">使用postman验证api</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-7.png" alt="" class="wp-image-3249"/></figure>



<h2 class="wp-block-heading">使用前缀</h2>



<p class="is-style-iw-2em">比如像这段代码，在创建router时就可以带上前缀</p>



<pre class="wp-block-code"><code>var router = new Router({
  prefix: '/users'
});

router.get('/', ...); // responds to "/users"
router.get('/:id', ...); // responds to "/users/:id"</code></pre>



<h2 class="wp-block-heading">获取URL参数</h2>



<p class="is-style-iw-2em">router的参数存放在ctx.params中</p>



<pre class="wp-block-code"><code>router.get('/:category/:title', (ctx, next) => {
  console.log(ctx.params);
  // => { category: 'programming', title: 'how-to-node' }
});</code></pre>



<p class="is-style-iw-2em">实际举一个例子</p>



<pre class="wp-block-code"><code>"use strict"

const Koa = require("koa");
const  Router=require("koa-router");
const app = new Koa();
const router=new Router();


router.get('/',(ctx)=>{
    ctx.body="主页";
})
router.get('/users/:id',(ctx)=>{
    ctx.body=`${ctx.params.id}`; //这个id就是上面冒号后id的内容
})
app.use(router.routes());
app.listen(3000)c</code></pre>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-8.png" alt="" class="wp-image-3250"/></figure>



<p class="is-style-iw-2em">多级嵌套时，直接在参数中写我们需要的值就可以取出</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-9.png" alt="" class="wp-image-3251"/></figure>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-10.png" alt="" class="wp-image-3252"/></figure>



<h2 class="wp-block-heading">鉴权</h2>



<p class="is-style-iw-2em">这里举个简单的例子，鉴权可以用于过滤掉一些不合法的请求，首先写一个函数。当不符合时返回401.</p>



<pre class="wp-block-code"><code>const auth = async (ctx, next) => {
    if (ctx.url !== `/users`) {
        ctx.throw(401).catch();
    }
    await next();
}</code></pre>



<p class="is-style-iw-2em">注意，这时url必须为http://localhost:3000/users。</p>



<pre class="wp-block-code"><code>usersRouter.get('/:id', auth, (ctx) => {
    ctx.body = `${ctx.params.id}`;
})
usersRouter.get(`/`,auth,(ctx)=>{
    ctx.body="users"
})</code></pre>



<p class="is-style-iw-2em">访问其它链接即为无效</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-11.png" alt="" class="wp-image-3256"/></figure>



<p class="is-style-iw-2em">如果想访问动态链接，可以使用正则表达式</p>



<pre class="wp-block-code"><code>const auth = async (ctx, next) => {
    if (ctx.url !== ctx.url.match(/\/users\/\d+/).toString()) {
        ctx.throw(401).catch();
    }
    await next();
}</code></pre>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-12.png" alt="" class="wp-image-3257"/></figure>



<p class="is-style-iw-2em">这里我写了几个常用的方法，注意delete方法约定返回的状态码是204</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-17.png" alt="" class="wp-image-3270"/></figure>



<p class="is-style-iw-2em"><strong>其它方法可以去上面给的链接里找到</strong></p>



<h3 class="wp-block-heading">模拟请求演示</h3>



<p class="is-style-iw-2em">这里我使用数组模拟数据库请求。</p>



<p class="is-style-iw-2em">使用koa-body-parser模块来获取post中request的body。</p>



<pre class="wp-block-code"><code>"use strict"

const Koa = require("koa");
const Router = require("koa-router");
const bodyparser=require("koa-body-parser");
const app = new Koa();
const router = new Router();
const usersRouter = new Router({prefix: `/users`})

let db=&#91;];
router.get('/', (ctx) => {
    ctx.body = "主页";
});

usersRouter.get('/:id', (ctx) => {
    ctx.body = db&#91;+ctx.params.id];
})

usersRouter.post(`/`,(ctx)=>{
    db.push(ctx.request.body);
    ctx.body=ctx.request.body;//request的body
})
usersRouter.put(`/:id`,(ctx)=>{
    db&#91;ctx.params.id]=ctx.request.body;
    ctx.status=200;
})
usersRouter.delete(`/:id`,(ctx)=>{
    db.splice(+ctx.params.id,1);
    ctx.status=204;
})
app.use(router.routes());
app.use(bodyparser());
app.use(usersRouter.routes());
app.use(usersRouter.allowedMethods());
app.listen(3000)</code></pre>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-18.png" alt="" class="wp-image-3278"/></figure>



<p class="is-style-iw-2em">  这里直接可以使用postman进行相应的请求方法测试。</p>



<p class="is-style-iw-2em"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3248/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3248</post-id>	</item>
		<item>
		<title>使用Koa开发RESTful API</title>
		<link>https://www.cztcode.com/2020/3217/</link>
					<comments>https://www.cztcode.com/2020/3217/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Mon, 03 Aug 2020 02:01:03 +0000</pubDate>
				<category><![CDATA[node.js]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3217</guid>

					<description><![CDATA[本篇文章介绍Koa，RESTful概念，并搭建一个Koa Demo。]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p class="is-style-iw-2em">本篇文章介绍Koa，RESTful概念，并搭建一个Koa Demo。</p>



<h2 class="wp-block-heading">什么是RESTfulAPI？</h2>



<p class="is-style-iw-2em"><a rel="noreferrer noopener" href="http://www.ruanyifeng.com/blog/2011/09/restful.html" target="_blank" class="rank-math-link">RESTful</a> 是目前最流行的 API 设计规范，用于 Web 数据接口的设计。用通俗的话讲</p>



<ul class="wp-block-list"><li>看Url就知道要什么</li><li>看http method就知道干什么</li><li>看http status code就知道结果如何</li></ul>



<p class="is-style-iw-2em">具体详细介绍参见：<a href="https://www.zhihu.com/question/28557115/answer/48094438" class="rank-math-link" target="_blank" rel="noopener">怎样用通俗的语言解释REST，以及RESTful？</a></p>



<h2 class="wp-block-heading">什么是Koa？</h2>



<p class="is-style-iw-2em">Koa 是一个新的 web 框架，由 Express 幕后的原班人马打造， 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数，Koa 帮你丢弃回调函数，并有力地增强错误处理。 Koa 并没有捆绑任何中间件， 而是提供了一套优雅的方法，帮助您快速而愉快地编写服务端应用程序。</p>



<p class="is-style-iw-2em">也就是说，koa是最新的开发node.js服务端的框架。很高兴我们能使用最新的技术。</p>



<h2 class="wp-block-heading">搭建Koa</h2>



<p class="is-style-iw-2em">我比较习惯idea，就使用intellij unlimited 版本了。实际上也确实比vscode方便，可以省去一些指令步骤。</p>



<p class="is-style-iw-2em">首先创建一个node.js项目。然后使用npm导入koa包，这里可以直接在设置中导入。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image.png" alt="" class="wp-image-3218"/></figure>



<p class="is-style-iw-2em">点击加号后输入koa，勾选版本即可导入。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-1-1024x702.png" alt="" class="wp-image-3219"/></figure>



<p class="is-style-iw-2em">为了能够快速重启node.js模块，我们还需要导入nodemon包。注意标记为dev模式，这意味着在工作环境中是不会导入这个包的。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-2.png" alt="" class="wp-image-3220"/></figure>



<p class="is-style-iw-2em">创建一个新文件，并写入hello world程序。</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-3.png" alt="" class="wp-image-3221"/></figure>



<p class="is-style-iw-2em">在终端中输入nodemon  文件名</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-5.png" alt="" class="wp-image-3223"/></figure>



<p class="is-style-iw-2em">然后在浏览器中打开链接</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/08/image-6.png" alt="" class="wp-image-3224"/></figure>



<p class="is-style-iw-2em">demo就完成了，这里暂时没有用到RESTful，因为没有设计复杂的链接，后续会继续更新。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3217/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3217</post-id>	</item>
		<item>
		<title>IDEA创建Node.js项目</title>
		<link>https://www.cztcode.com/2020/3140/</link>
					<comments>https://www.cztcode.com/2020/3140/#respond</comments>
		
		<dc:creator><![CDATA[Jellow]]></dc:creator>
		<pubDate>Tue, 21 Jul 2020 07:39:30 +0000</pubDate>
				<category><![CDATA[node.js]]></category>
		<guid isPermaLink="false">https://www.cztcode.com/?p=3140</guid>

					<description><![CDATA[首先下载node.js插件，这个我安装IDEA的时候就已经安装好了。 新建node文件，很简单，找到node.js选项 我们首先创建一个js文件 添加运行配置 设置浏览器启动 点击运行直接弹出结果 这个简单实例就完成了。]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div>
<p class="is-style-iw-2em">首先下载node.js插件，这个我安装IDEA的时候就已经安装好了。</p>



<p class="is-style-iw-2em">新建node文件，很简单，找到node.js选项</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-93-1024x520.png" alt="" class="wp-image-3141"/></figure>



<p class="is-style-iw-2em">我们首先创建一个js文件</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-94-1024x240.png" alt="" class="wp-image-3142"/></figure>



<pre class="wp-block-code"><code>var http=require("http");

var server=http.createServer(function (req,res) {
    res.writeHead(200,{"Content-type":"text/html;charset=UTF-8"});
    res.end("hello world");
});

server.listen(3000,"127.0.0.1");</code></pre>



<h4 class="wp-block-heading">添加运行配置</h4>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-95-1024x650.png" alt="" class="wp-image-3143"/></figure>



<p class="is-style-iw-2em">设置浏览器启动</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-96.png" alt="" class="wp-image-3144"/></figure>



<p class="is-style-iw-2em">点击运行直接弹出结果</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://www.cztcode.com/wp-content/uploads/2020/07/image-97.png" alt="" class="wp-image-3145"/></figure>



<p class="is-style-iw-2em">这个简单实例就完成了。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.cztcode.com/2020/3140/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">3140</post-id>	</item>
	</channel>
</rss>
