标签: json

  • 状态码为200时 jQuery ajax报错

    1. 问题现象
    HTTP 状态码为 200 OK 时, jquery ajax报错
    2. 问题原因
    jquery ajax的dataType字段包含:json, 但是服务端返回的数据不是规范的json格式,导致jquery解析json字符串报错,最终导致ajax报错。
    jQuery ajax 官方文档上说明:
    “json”: Evaluates the response as JSON and returns a JavaScript object. Cross-domain “json” requests are converted to “jsonp” unless the request includes jsonp: false in its request options. The JSON data is parsed in a strict manner; any malformed JSON is rejected and a parse error is thrown. As of jQuery 1.9, an empty response is also rejected; the server should return a response of null or {} instead. (See json.org for more information on proper JSON formatting.)
    设置dataType为json时,jquery就会去解析响应体为JavaScript对象。跨域的json请求会被转化成jsonp, 除非设置了jsonp: false。JSON数据会以严格模式去解析,任何不规范的JSON字符串都会解析异常并抛出错误。从jQuery 1.9起,一个空的响应也会被抛出异常。服务端应该返回一个null或者{}去代替空响应。参考json.org, 查看更多内容
    3. 解决方案
    这个问题的原因有两个。

    后端返回的json数据格式不规范
    HTTP状态码为200,但是返回空的响应

    所以后端在返回结果时,不要使用空的响应,也不应该去手动拼接JSON字符串,而应该交给响应的库来实现JSON序列化字符串工作。

    方案1:如果后端确定响应体中不返回数据,那么就把状态码设置为204,而不是200。状态码为204时,jQuery就知道响应体中没有数据,也不需要去解析。我一直逼着后端同事这么做。

    方案2:如果后端接口想返回200,那么请返回一个null或者{}去代替空响应

    方案3:别用jQuery的ajax,换个其他的库试试

    4. 参考

    Ajax request returns 200 OK, but an error event is fired instead of success
    jQuery.ajax

  • JWT – just what?

    初见JWT,不知所云,赶紧Google(百度)一下,原来是跨域身份验证解决方案。
    JWT只是缩写,全拼则是 JSON Web Tokens ,是目前流行的跨域认证解决方案,一种基于JSON的、用于在网络上声明某种主张的令牌(token)。
    JWT 原理
    jwt验证方式是将用户信息通过加密生成token,每次请求服务端只需要使用保存的密钥验证token的正确性,不用再保存任何session数据了,进而服务端变得无状态,容易实现拓展。
    加密前的用户信息,如:
    {
    “username”: “vist”,
    “role”: “admin”,
    “expire”: “2018-12-08 20:20:20”
    }
    客户端收到的token:
    7cd357af816b907f2cc9acbe9c3b4625
    JWT 结构
    一个token分为3部分:

    头部(header)
    载荷(payload)
    签名(signature)

    3个部分用“.”分隔,如:
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
    头部
    JWT的头部分是一个JSON对象,描述元数据,通常是:
    {
    “typ”: “JWT”,
    “alg”: “HS256”
    }

    typ 为声明类型,指定 “JWT”
    alg 为加密的算法,默认是 “HS256”

    也可以是下列中的算法:

    JWS
    算法名称
    描述

    HS256
    HMAC256
    HMAC with SHA-256

    HS384
    HMAC384
    HMAC with SHA-384

    HS512
    HMAC512
    HMAC with SHA-512

    RS256
    RSA256
    RSASSA-PKCS1-v1_5 with SHA-256

    RS384
    RSA384
    RSASSA-PKCS1-v1_5 with SHA-384

    RS512
    RSA512
    RSASSA-PKCS1-v1_5 with SHA-512

    ES256
    ECDSA256
    ECDSA with curve P-256 and SHA-256

    ES384
    ECDSA384
    ECDSA with curve P-384 and SHA-384

    ES512
    ECDSA512
    ECDSA with curve P-521 and SHA-512

    载荷
    载荷(payload)是数据的载体,用来存放实际需要传递的数据信息,也是一个JSON对象。JWT官方推荐字段:

    iss: jwt签发者
    sub: jwt所面向的用户
    aud: 接收jwt的一方
    exp: jwt的过期时间,这个过期时间必须要大于签发时间
    nbf: 定义在什么时间之前,该jwt都是不可用的.
    iat: jwt的签发时间
    jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

    也可以使用自定义字段,如:
    {
    “username”: “vist”,
    “role”: “admin”
    }
    签名
    签名部分是对前两部分(头部,载荷)的签名,防止数据篡改。
    按下列步骤生成:1、先指定密钥(secret)2、把头部(header)和载荷(payload)信息分别base64转换3、使用头部(header)指定的算法加密最终,签名(signature) = HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload),secret)
    客户端得到的签名:
    header.payload.signature
    也可以对JWT进行再加密。
    JWT 使用
    1、服务端根据用户登录状态,将用户信息加密到token中,返给客户端2、客户端收到服务端返回的token,存储在cookie中3、客户端和服务端每次通信都带上token,可以放在http请求头信息中,如:Authorization字段里面4、服务端解密token,验证内容,完成相应逻辑
    JWT 特点

    JWT更加简洁,更适合在HTML和HTTP环境中传递
    JWT适合一次性验证,如:激活邮件
    JWT适合无状态认证
    JWT适合服务端CDN分发内容
    相对于数据库Session查询更加省时
    JWT默认不加密
    使用期间不可取消令牌或更改令牌的权限
    JWT建议使用HTTPS协议来传输代码

    原文:https://www.bestvist.com/p/62

  • 初识区块链 – 用JS构建你自己的区块链

    前言
    区块链太复杂,那我们就讲点简单的。用JS来构建你自己的区块链系统,寥寥几行代码就可以说明区块链的底层数据结构,POW挖矿思想和交易过程等。当然了,真实的场景远远远比这复杂。本文的目的仅限于让大家初步了解,初步认识区块链。
    文章内容主要参考视频:使用Javascript建立区块链(https://www.youtube.com/playlist?list=PLzvRQMJ9HDiTqZmbtFisdXFxul5k0F-Q4)
    感谢原作者,本文在原视频基础上做了修改补充,并加入了个人理解。
    认识区块链
    区块链顾名思义是由区块连接而成的链,因此最基本的数据结构是块。每个块都含有时间戳,数据,散列,previousHash等信息。其中数据用来存储数据,previousHash是前一个区块的哈希值示意如下:

    哈希是对区块信息的摘要存储,哈希的好处是任意长度的信息经过哈希都可以映射成固定长度的字符串,如可用SHA256:
    calculateHash() {
    return SHA256(this.previousHash + this.timestamp + JSON.stringify(this.data)).toString();
    }

    块的数据结构
    块的最基本数据结构如下:
    class Block {
    constructor(timestamp, data, previousHash = ”) {
    this.timestamp = timestamp;
    this.data = data;
    this.previousHash = previousHash;
    //对hash的计算必须放在最后,保证所有数据赋值正确后再计算
    this.hash = this.calculateHash();
    }

    calculateHash() {
    return SHA256(this.previousHash + this.timestamp + JSON.stringify(this.data)).toString();
    }
    }

    BlockChain的数据结构
    多个区块链接而成BlockChain,显然可用用数组或链表来表示,如:
    class BlockChain {
    constructor() {
    this.chain = [];
    }
    }

    创世区块
    正所谓万物始于一,区块链的第一个区块总是需要人为来手动创建,这个区块的previousHash为空,如:
    createGenesisBlock() {
    return new Block(“2018-11-11 00:00:00”, “Genesis block of simple chain”, “”);
    }

    区块链的构造方法也应改为:
    class BlockChain {
    constructor() {
    this.chain = [this.createGenesisBlock()];
    }
    }

    添加区块
    每新加一个区块,必须保证与原有区块链连接起来,即:
    class BlockChain {
    getLatestBlock() {
    return this.chain[this.chain.length – 1];
    }

    addBlock(newBlock) {
    //新区块的前一个hash值是现有区块链的最后一个区块的hash值;
    newBlock.previousHash = this.getLatestBlock().hash;
    //重新计算新区块的hash值(因为指定了previousHash);
    newBlock.hash = newBlock.calculateHash();
    //把新区块加入到链中;
    this.chain.push(newBlock);
    }

    }

    校验区块链
    区块链数据结构的核心是保证前后链接,无法篡改,但是如果有人真的篡改了某个区块,我们该如何校验发现呢?最笨也是最自然是想法就是遍历所有情况,逐一校验,如:
    isChainValid() {
    //遍历所有区块
    for (let i = 1; i < this.chain.length; i++) {
    const currentBlock = this.chain[i];
    const previousBlock = this.chain[i – 1];
    //重新计算当前区块的hash值,若发现hash值对不上,说明该区块有数据被篡改,hash值未重新计算
    if (currentBlock.hash !== currentBlock.calculateHash()) {
    console.error(“hash not equal: ” + JSON.stringify(currentBlock));
    return false;
    }
    //判断当前区块的previousHash是否真的等于前一个区块的hash,若不等,说明前一个区块被篡改,虽然hash值被重新计算正确,但是后续区块的hash值并未重新计算,导致整个链断裂
    if (currentBlock.previousHash !== previousBlock.calculateHash) {
    console.error(“previous hash not right: ” + JSON.stringify(currentBlock));
    return false;
    }
    }
    return true;
    }

    跑吧
    跑起来看看,即:
    let simpleChain = new BlockChain();
    simpleChain.addBlock(new Block(“2018-11-11 00:00:01”, {amount: 10}));
    simpleChain.addBlock(new Block(“2018-11-11 00:00:02”, {amount: 20}));

    console.log(JSON.stringify(simpleChain, null, 4));

    console.log(“is the chain valid? ” + simpleChain.isChainValid());

    结果如下:
    ali-186590cc4a7f:simple-chain shanyao$ node main_1.js
    {
    “chain”: [
    {
    “timestamp”: “2018-11-11 00:00:00”,
    “data”: “Genesis block of simple chain”,
    “previousHash”: “”,
    “hash”: “fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89”
    },
    {
    “timestamp”: “2018-11-11 00:00:01”,
    “data”: {
    “amount”: 10
    },
    “previousHash”: “fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89”,
    “hash”: “150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529”
    },
    {
    “timestamp”: “2018-11-11 00:00:02”,
    “data”: {
    “amount”: 20
    },
    “previousHash”: “150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529”,
    “hash”: “274a7a13ed20118e8cb745654934a7e24a4d59333ba17dfbf5d4cfe0fa8a6e34”
    }
    ]
    }
    is the chain valid? true

    注意看其中的previousHash与哈希,确实是当前区块的previousHash指向前一个区块的哈希值。
    篡改下试试
    都说区块链不可篡改,是真的吗让我们篡改第2个区块试试,如?
    let simpleChain = new BlockChain();
    simpleChain.addBlock(new Block(“2018-11-11 00:00:01”, {amount: 10}));
    simpleChain.addBlock(new Block(“2018-11-11 00:00:02”, {amount: 20}));

    console.log(“is the chain valid? ” + simpleChain.isChainValid());

    //将第2个区块的数据,由10改为15
    simpleChain.chain[1].data = {amount: 15};

    console.log(“is the chain still valid? ” + simpleChain.isChainValid());
    console.log(JSON.stringify(simpleChain, null, 4));

    结果如下:
    ali-186590cc4a7f:simple-chain shanyao$ node main_1.js
    is the chain valid? true
    hash not equal: {“timestamp”:”2018-11-11 00:00:01″,”data”:{“amount”:15},”previousHash”:”fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89″,”hash”:”150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529″}
    is the chain still valid? false
    {
    “chain”: [
    {
    “timestamp”: “2018-11-11 00:00:00”,
    “data”: “Genesis block of simple chain”,
    “previousHash”: “”,
    “hash”: “fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89”
    },
    {
    “timestamp”: “2018-11-11 00:00:01”,
    “data”: {
    “amount”: 15
    },
    “previousHash”: “fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89”,
    “hash”: “150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529”
    },
    {
    “timestamp”: “2018-11-11 00:00:02”,
    “data”: {
    “amount”: 20
    },
    “previousHash”: “150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529”,
    “hash”: “274a7a13ed20118e8cb745654934a7e24a4d59333ba17dfbf5d4cfe0fa8a6e34”
    }
    ]
    }

    显然,篡改了数据之后,哈希值并未重新计算,导致该区块的哈希值对不上。
    再篡改下试试
    那么,如果我们聪明点,篡改后把哈希值也重新计算会如何?
    let simpleChain = new BlockChain();
    simpleChain.addBlock(new Block(“2018-11-11 00:00:01”, {amount: 10}));
    simpleChain.addBlock(new Block(“2018-11-11 00:00:02”, {amount: 20}));

    console.log(“is the chain valid? ” + simpleChain.isChainValid());
    //篡改后重新计算hash值
    simpleChain.chain[1].data = {amount: 15};
    simpleChain.chain[1].hash = simpleChain.chain[1].calculateHash();
    console.log(“is the chain still valid? ” + simpleChain.isChainValid());
    console.log(JSON.stringify(simpleChain, null, 4));

    结果如下:
    ali-186590cc4a7f:simple-chain shanyao$ node main_1.js
    is the chain valid? true
    previous hash not right: {“timestamp”:”2018-11-11 00:00:02″,”data”:{“amount”:20},”previousHash”:”150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529″,”hash”:”274a7a13ed20118e8cb745654934a7e24a4d59333ba17dfbf5d4cfe0fa8a6e34″}
    is the chain still valid? false
    {
    “chain”: [
    {
    “timestamp”: “2018-11-11 00:00:00”,
    “data”: “Genesis block of simple chain”,
    “previousHash”: “”,
    “hash”: “fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89”
    },
    {
    “timestamp”: “2018-11-11 00:00:01”,
    “data”: {
    “amount”: 15
    },
    “previousHash”: “fd56967ff621a4090ff71ce88fdd456547d1c92d2e93766b7e8791f7a5f91f89”,
    “hash”: “74d139274fb692495b7c805dd5822faa0c5b5e6058b6beef96e87e18ab83a6b1”
    },
    {
    “timestamp”: “2018-11-11 00:00:02”,
    “data”: {
    “amount”: 20
    },
    “previousHash”: “150b196268a0152e9f0e719ac131a722472a809f49bd507965029a78c7400529”,
    “hash”: “274a7a13ed20118e8cb745654934a7e24a4d59333ba17dfbf5d4cfe0fa8a6e34”
    }
    ]
    }

    显然,第3个区块的previousHash并未指向第2个区块的哈希值。
    是真的无法篡改吗
    其实并不是,如果我们再聪明一点,把后续区块的哈希值也重新计算一下,不就OK了吗?确实如此,如:
    let simpleChain = new BlockChain();
    simpleChain.addBlock(new Block(“2018-11-11 00:00:01”, {amount: 10}));
    simpleChain.addBlock(new Block(“2018-11-11 00:00:02”, {amount: 20}));

    console.log(“is the chain valid? ” + simpleChain.isChainValid());
    //篡改第2个区块
    simpleChain.chain[1].data = {amount: 15};
    simpleChain.chain[1].hash = simpleChain.chain[1].calculateHash();
    //并把第3个区块也重新计算
    simpleChain.chain[2].previousHash = simpleChain.chain[1].hash;
    simpleChain.chain[2].hash = simpleChain.chain[2].calculateHash();
    console.log(“is the chain still valid? ” + simpleChain.isChainValid());
    console.log(JSON.stringify(simpleChain, null, 4

    本文作者:扁鹊他大哥阅读原文
    本文为云栖社区原创内容,未经允许不得转载。

  • JSON数据从MongoDB迁移到MaxCompute最佳实践

    摘要: 本文为您介绍如何利用DataWorks数据集成直接从MongoDB提取JSON字段到MaxCompute。
    数据及账号准备
    首先您需要将数据上传至您的MongoDB数据库。本例中使用阿里云的云数据库 MongoDB 版,网络类型为VPC(需申请公网地址,否则无法与DataWorks默认资源组互通),测试数据如下。

    {
    “store”: {
    “book”: [
    {
    “category”: “reference”,
    “author”: “Nigel Rees”,
    “title”: “Sayings of the Century”,
    “price”: 8.95
    },
    {
    “category”: “fiction”,
    “author”: “Evelyn Waugh”,
    “title”: “Sword of Honour”,
    “price”: 12.99
    },
    {
    “category”: “fiction”,
    “author”: “J. R. R. Tolkien”,
    “title”: “The Lord of the Rings”,
    “isbn”: “0-395-19395-8”,
    “price”: 22.99
    }
    ],
    “bicycle”: {
    “color”: “red”,
    “price”: 19.95
    }
    },
    “expensive”: 10
    }
    登录MongoDB的DMS控制台,本例中使用的数据库为 admin,集合为 userlog,您可以在查询窗口使用db.userlog.find().limit(10)命令查看已上传好的数据,如下图所示。 此外,需提前在数据库内新建用户,用于DataWorks添加数据源。本例中使用命令db.createUser({user:”bookuser”,pwd:”123456″,roles:[“root”]}),新建用户名为 bookuser,密码为 123456,权限为root。
    使用DataWorks提取数据到MaxCompute
    新增MongoDB数据源进入DataWorks数据集成控制台,新增MongoDB类型数据源。 

    具体参数如下所示,测试数据源连通性通过即可点击完成。由于本文中MongoDB处于VPC环境下,因此 数据源类型需选择 有公网IP。 
    访问地址及端口号可通过在MongoDB管理控制台点击实例名称获取,如下图所示。 
    新建数据同步任务在DataWorks上新建数据同步类型节点。 

    新建的同时,在DataWorks新建一个[建表任务](https://help.aliyun.com/document_detail/85302.html#concept-qlb-gd4-q2b “本文向您介绍如何对数据表进行创建、提交和查询。”),用于存放JSON数据,本例中新建表名为mqdata。 

    表参数可以通过图形化界面完成。本例中mqdata表仅有一列,类型为string,列名为MQ data。 

    完成上述新建后,您可以在图形化界面进行数据同步任务参数的初步配置,如下图所示。选择目标数据源名称为odps_first,选择目标表为刚建立的mqdata。数据来源类型为MongoDB,选择我们刚创建的数据源mongodb_userlog。完成上述配置后, 点击转换为脚本,跳转到脚本模式。 

    脚本模式代码示例如下。

    {
    “type”: “job”,
    “steps”: [
    {
    “stepType”: “mongodb”,
    “parameter”: {
    “datasource”: “mongodb_userlog”,
    //数据源名称
    “column”: [
    {
    “name”: “store.bicycle.color”, //JSON字段路径,本例中提取color值
    “type”: “document.document.string” //本栏目的字段数需和name一致。假如您选取的JSON字段为一级字段,如本例中的expensive,则直接填写string即可。
    }
    ],
    “collectionName //集合名称”: “userlog”
    },
    “name”: “Reader”,
    “category”: “reader”
    },
    {
    “stepType”: “odps”,
    “parameter”: {
    “partition”: “”,
    “isCompress”: false,
    “truncate”: true,
    “datasource”: “odps_first”,
    “column”: [
    //MaxCompute表列名 “mqdata”
    ],
    “emptyAsNull”: false,
    “table”: “mqdata”
    },
    “name”: “Writer”,
    “category”: “writer”
    }
    ],
    “version”: “2.0”,
    “order”: {
    “hops”: [
    {
    “from”: “Reader”,
    “to”: “Writer”
    }
    ]
    },
    “setting”: {
    “errorLimit”: {
    “record”: “”
    },
    “speed”: {
    “concurrent”: 2,
    “throttle”: false,
    “dmu”: 1
    }
    }
    }
    完成上述配置后,点击运行接即可。运行成功日志示例如下所示。 

    结果验证
    在您的业务流程中新建一个ODPS SQL节点。 
    您可以输入 SELECT * from mqdata;语句,查看当前mqdata表中数据。当然这一步您也可以直接在MaxCompute客户端中输入命令运行。 

    本文作者:付帅阅读原文
    本文为云栖社区原创内容,未经允许不得转载。

  • 一种巧妙的对象映射关系设计–JSON-ORM

    项目介绍
    这是标准数据库封装的上半部分,智能查询(JSON-ORM)的实现。完整代码:https://github.com/zhoutk/gels
    设计思路
    我们通用的ORM,基本模式都是想要脱离数据库的,几乎都在编程语言层面建立模型,由程序去与数据库打交道。虽然脱离了数据库的具体操作,但我们要建立各种模型文档,用代码去写表之间的关系等等操作,让初学者一时如坠云雾。我的想法是,将关系数据库拥有的完善设计工具之优势与微服务结合起来,数据设计提供结构信息;前端送到后端的json对象自动映射成为标准的SQL查询语句。只要我们理解了标准的SQL语言,我们就能够完成数据库查询操作。我的这种ORM方式,服务端不需要写一行代码,只需完成关系数据库的设计,就能为前端提供标准服务接口。并且遵循一套统一的接口(已经实践检验,满足百分之九九的查询需求)来实现数据库封装,达到业务层可以随意切换数据库的目的。
    数据库查询操作接口。
    export default interface IDao {
    select(tablename: string, params: object, fields?: Array<string>): Promise<any>; //自动生成sql语句
    execSql(sql: string, values: Array<any>, params: object, fields?: Array<string>): Promise<any>; //执行手动sql语句
    }
    智能查询(JSON-ORM)
    查询保留字:fields, page, size, sort, search, lks, ins, ors, count, sum, group

    fields, 定义查询结果字段,支持数组和逗号分隔字符串两种形式由前端来确定返回的数据库字段信息,这样后端的设计可以适用面更广泛,而不会造成网络带宽的浪费。在KOA2的框架下,GET请求要支持输入数组,只能把同一个key多次输入,如:age=11&age=22。这样很不方便,我实现了一个参数转换函数,针对数组提供多种输入形式。arryParse
    arryParse(arr): Array<any>|null { //返回值为数据或空值
    try {
    if (Array.isArray(arr) || G.L.isNull(arr)) { //如果输入是数组或空,直接返回
    return arr
    } else if (typeof arr === ‘string’) { //若是字符串
    if (arr.startsWith(‘[‘)) { //数组的字符串形式,进行转换
    arr = JSON.parse(arr)
    } else {
    //逗号拼接的字符串,mysql的驱动同时支持参数以字符串形式或数组形式提供,
    //所以这里可以不加判断,直接用split函数将字符串转化为数组
    arr = arr.split(‘,’)
    }
    }
    } catch (err) {
    arr = null //数组的字符串形式转换失败,刘明输入参数是错误的
    }
    return arr
    }
    查询示例:
    请求URL: /rs/users?username=white&age=22&fields=[“username”,”age”]
    生成sql: SELECT username,age FROM users WHERE username = ? and age = ?

    page, size, sort, 分页排序在mysql中这比较好实现,limit来分页是很方便的,排序只需将参数直接拼接到order by后就好了。 查询示例:
    请求URL: /rs/users?page=1&size=10&sort=age desc
    生成sql: SELECT * FROM users ORDER BY age desc LIMIT 0,10

    search, 模糊查询切换参数,不提供时为精确匹配提供字段查询的精确匹配与模糊匹配的切换,实现过程中,注意参数化送入参数时,like匹配,是要在参数两边加%,而不是在占位符两边加%。另外,同一个字段匹配两次模糊查询,需要特别处理,我提供了一种巧妙的方法:
    value = pool.escape(value).replace(/\’, \’/g, “%’ and ” + key + ” like ‘%”) //将值用escape编码,数组将转化为逗号连接的字符串,用正则全局替换,变成and连接
    value = value.substring(1, value.length – 1) //去掉两头多余的引号
    where += key + ” like ‘%” + value + “%’” //补齐条件查询语句,这种方式,比用循环处理来得快捷,它统一了数组与其它形式的处理方式
    查询示例
    请求URL: /rs/users?username=i&password=1&search

    ins, lks, ors这是最重要的三种查询方式,如何找出它们之间的共同点,减少冗余代码是关键。

    ins, 数据库表单字段in查询,一字段对多个值,例: 查询示例:
    请求URL: /rs/users?ins=[“age”,11,22,26]
    生成sql: SELECT * FROM users WHERE age in ( ? )

    ors, 数据库表多字段精确查询,or连接,多个字段对多个值,支持null值查询,例: 查询示例:
    请求URL: /rs/users?ors=[“age”,1,”age”,22,”password”,null]
    生成sql: SELECT * FROM users WHERE ( age = ? or age = ? or password is null )

    lks, 数据库表多字段模糊查询,or连接,多个字段对多个值,支持null值查询,例:查询示例:
    请求URL: /rs/users?lks=[“username”,”i”,”password”,null]
    生成sql: SELECT * FROM users WHERE ( username like ? or password is null )

    count, sum这两个统计求和,处理方式也类似,查询时一般要配合group与fields使用。

    count, 数据库查询函数count,行统计,例:查询示例:
    请求URL: /rs/users?count=[“1″,”total”]&fields=[“username”]
    生成sql: SELECT username,count(1) as total FROM users

    sum, 数据库查询函数sum,字段求和,例:查询示例:
    请求URL: /rs/users?sum=[“age”,”ageSum”]&fields=[“username”]

    group, 数据库分组函数group,例: 查询示例:
    请求URL: /rs/users?group=age&count=[“*”,”total”]&fields=[“age”]
    生成sql: SELECT age,count(*) as total FROM users GROUP BY age

    不等操作符查询支持
    支持的不等操作符有:>, >=, <, <=, <>, =;逗号符为分隔符,一个字段支持一或二个操作。 特殊处:使用”=”可以使某个字段跳过search影响,让模糊匹配与精确匹配同时出现在一个查询语句中

    一个字段一个操作,示例:查询示例:
    请求URL: /rs/users?age=>,10
    生成sql: SELECT * FROM users WHERE age> ?

    一个字段二个操作,示例:查询示例:
    请求URL: /rs/users?age=>,10,<=,35
    生成sql: SELECT * FROM users WHERE age> ? and age<= ?

    使用”=”去除字段的search影响,示例:查询示例:
    请求URL: /rs/users?age==,22&username=i&search
    生成sql: SELECT * FROM users WHERE age= ? and username like ?

    相关视频课程
    运用typescript进行node.js后端开发精要 nodejs实战之智能微服务快速开发框架 JSON-ORM(对象关系映射)设计与实现
    资源地址
    凝胶(gels)项目: https://github.com/zhoutk/gels视频讲座资料: https://github.com/zhoutk/sifou个人博客: https://github.com/zhoutk/blog

  • JSON数据从OSS迁移到MaxCompute最佳实践

    本文为您介绍如何利用DataWorks数据集成将JSON数据从OSS迁移到MaxCompute,并使用MaxCompute内置字符串函数GET_JSON_OBJECT提取JSON信息。
    数据上传OSS
    将您的JSON文件重命名后缀为TXT文件,并上传到OSS。本文中使用的JSON文件示例如下。

    {
    “store”: {
    “book”: [
    {
    “category”: “reference”,
    “author”: “Nigel Rees”,
    “title”: “Sayings of the Century”,
    “price”: 8.95
    },
    {
    “category”: “fiction”,
    “author”: “Evelyn Waugh”,
    “title”: “Sword of Honour”,
    “price”: 12.99
    },
    {
    “category”: “fiction”,
    “author”: “J. R. R. Tolkien”,
    “title”: “The Lord of the Rings”,
    “isbn”: “0-395-19395-8”,
    “price”: 22.99
    }
    ],
    “bicycle”: {
    “color”: “red”,
    “price”: 19.95
    }
    },
    “expensive”: 10
    }
    将applog.txt文件上传到OSS,本文中OSS Bucket位于华东2区。 

    使用DataWorks导入数据到MaxCompute
    新增OSS数据源
    进入DataWorks数据集成控制台,新增OSS类型数据源。 

    具体参数如下所示,测试数据源连通性通过即可点击完成。Endpoint地址请参见OSS各区域的外网、内网地址,本例中为http://oss-cn-shanghai.aliyun… http://oss-cn-shanghai-internal.aliyuncs.com(由于本文中OSS和DataWorks项目处于同一个region中,本文选用后者,通过内网连接)。 

    新建数据同步任务
    在DataWorks上新建数据同步类型节点。 

    新建的同时,在DataWorks新建一个建表任务,用于存放JSON数据,本例中新建表名为mqdata。 

    表参数可以通过图形化界面完成。本例中mqdata表仅有一列,类型为string,列名为MQ data。 

    完成上述新建后,您可以在图形化界面配置数据同步任务参数,如下图所示。选择目标数据源名称为odps_first,选择目标表为刚建立的mqdata。数据来源类型为OSS,Object前缀可填写文件路径及名称。列分隔符使用TXT文件中不存在的字符即可,本文中使用 ^(对于OSS中的TXT格式数据源,Dataworks支持多字符分隔符,所以您可以使用例如 %&%#^$$^%这样很难出现的字符作为列分隔符,保证分割为一列)。 

    映射方式选择默认的同行映射即可。 

    点击左上方的切换脚本按钮,切换为脚本模式。修改fileFormat参数为: “fileFormat”:”binary”。该步骤可以保证OSS中的JSON文件同步到MaxCompute之后存在同一行数据中,即为一个字段。其他参数保持不变,脚本模式代码示例如下。

    {
    “type”: “job”,
    “steps”: [
    {
    “stepType”: “oss”,
    “parameter”: {
    “fieldDelimiterOrigin”: “^”,
    “nullFormat”: “”,
    “compress”: “”,
    “datasource”: “OSS_userlog”,
    “column”: [
    {
    “name”: 0,
    “type”: “string”,
    “index”: 0
    }
    ],
    “skipHeader”: “false”,
    “encoding”: “UTF-8”,
    “fieldDelimiter”: “^”,
    “fileFormat”: “binary”,
    “object”: [
    “applog.txt”
    ]
    },
    “name”: “Reader”,
    “category”: “reader”
    },
    {
    “stepType”: “odps”,
    “parameter”: {
    “partition”: “”,
    “isCompress”: false,
    “truncate”: true,
    “datasource”: “odps_first”,
    “column”: [
    “mqdata”
    ],
    “emptyAsNull”: false,
    “table”: “mqdata”
    },
    “name”: “Writer”,
    “category”: “writer”
    }
    ],
    “version”: “2.0”,
    “order”: {
    “hops”: [
    {
    “from”: “Reader”,
    “to”: “Writer”
    }
    ]
    },
    “setting”: {
    “errorLimit”: {
    “record”: “”
    },
    “speed”: {
    “concurrent”: 2,
    “throttle”: false,
    “dmu”: 1
    }
    }
    }
    完成上述配置后,点击运行接即可。运行成功日志示例如下所示。 

    获取JSON字段信息
    在您的业务流程中新建一个ODPS SQL节点。 
    您可以首先输入 SELECT*from mqdata;语句,查看当前mqdata表中数据。当然这一步及后续步骤,您也可以直接在MaxCompute客户端中输入命令运行。 

    确认导入表中的数据结果无误后,您可以使用MaxCompute内建字符串函数GET_JSON_OBJECT获取您想要的JSON数据。本例中使用 SELECT GET_JSON_OBJECT(mqdata.MQdata,’$.expensive’) FROM mqdata;获取JSON文件中的 expensive值。如下图所示,可以看到已成功获取数据。 

    本文作者:付帅阅读原文
    本文为云栖社区原创内容,未经允许不得转载。

  • Python3爬取英雄联盟英雄皮肤大图

    前言
    上篇文章,说到了,爬取LOL英雄皮肤的高清图片,最近有事,也没怎么去研究,所以,现在才去看了下,并且写了Python脚本来抓取皮肤图片。需要说明一下,这个脚本有部分英雄没有抓取到,但是具体原因,我目前还没搞懂,我是相当纳闷的。大家有兴趣的,可以看看后面遗留问题,一起研究下。
    爬虫思路
    初步尝试
    我先查看了network,并没有发现有可用的API;然后又用bs4去分析英雄列表页,但是请求到html里面,并没有英雄列表,在英雄列表的节点上,只有“正在加载中”这样的字样;同样的方法,分析英雄详情也是这种情况,所以我猜测,这些数据应该是Javascript负责加载的。

    继续尝试
    然后我就查看了英雄列表的源代码,查看外部引入的js文件,以及行内的js脚本,大概在368行,发现了有处理英雄列表的js注释,然后继续往下读这些代码,发现了第一个彩蛋,也就是他引入了一个champion.js的文件,我猜测,这个应该就是英雄列表大全了,然后我打开了这个链接的js,一眼看过去,黑麻麻一片,然后格式化了一下压缩的js,确定这就是英雄列表的js数据文件了。

    接着尝试
    前面通过查看列表的源代码,找到了英雄列表的js数据文件,那么,我继续随机点开了一个英雄的详情,然后查看英雄详情源代码,然后大概在568行看到有一个showSkin的js方法,通过这里,发现了第二个彩蛋,也就是皮肤图片的URL地址拼接方法。

    最后尝试
    上面找到了皮肤图片URL的拼接方法,并且发现了一行很关键的代码var skin =LOLherojs.champion[heroid].data.skins,也就是,这个skin变量,就是英雄皮肤的所有图片数组,但是这个文件内,并没有LOLherojs这个变量,也就是外部引入的,所以,需要继续查看下面的源代码,找到引入这个变量的位置,果不其然,在757行,发现了最后一个彩蛋,也就是,英雄皮肤的js文件,通过这里可以知道,每个英雄都有一个单独的js文件,并且知道了这个js文件的URL拼接方法。

    思路总结
    通过上面的分析,我们就得到了爬取LOL皮肤图片的所有数据准备了,也就是,直接,只需要提取js中的英雄列表以及英雄详情数据,就可实现我们的需求了。下面是运行后抓取到的图片……

    运行环境
    Python运行环境:python3.6 用到的模块:requests、json、urllib、os 未安装的模块,请使用pip instatll进行安装,例如:pip install requests
    完整代码
    其他啥的废话就不多说了,直接上完整代码,有问题,直接留言给我就行,另外,代码已上传GitHub。再说明一下,那些有问题的英雄详情的js文件,大家有时间也可以琢磨下,或者有其他的更加快捷的爬取这些图片的方法,也可以拿出来交流和讨论,谢谢。
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-

    “””
    抓取英雄联盟英雄全皮肤
    author: gxcuizy
    date: 2018-11-13
    “””

    import requests
    import json
    from urllib import parse
    import os

    class GetLolSkin(object):
    “””抓取LOL英雄皮肤”””

    def __init__(self):
    “””初始化变量”””
    self.hero_url = ‘https://lol.qq.com/biz/hero/champion.js’
    self.hero_detail_url = ‘http://lol.qq.com/biz/hero/’
    self.skin_folder = ‘skin’
    self.skin_url = ‘https://ossweb-img.qq.com/images/lol/web201310/skin/big’

    @staticmethod
    def get_html(url):
    “””下载html”””
    request = requests.get(url)
    request.encoding = ‘gbk’
    if request.status_code == 200:
    return request.text
    else:
    return “{}”

    def get_hero_list(self):
    “””获取英雄的完整信息列表”””
    hero_js = self.get_html(self.hero_url)
    # 删除左右的多余信息,得到json数据
    out_left = “if(!LOLherojs)var LOLherojs={};LOLherojs.champion=”
    out_right = ‘;’
    hero_list = hero_js.replace(out_left, ”).rstrip(out_right)
    return json.loads(hero_list)

    def get_hero_info(self, hero_id):
    “””获取英雄的详细信息”””
    # 获取js详情
    detail_url = parse.urljoin(self.hero_detail_url, hero_id + ‘.js’)
    detail_js = self.get_html(detail_url)
    # 删除左右的多余信息,得到json数据
    out_left = “if(!herojs)var herojs={champion:{}};herojs[‘champion’][%s]=” % hero_id
    out_right = ‘;’
    hero_info = detail_js.replace(out_left, ”).rstrip(out_right)
    return json.loads(hero_info)

    def download_skin_list(self, skin_list, hero_name):
    “””下载皮肤列表”””
    # 循环下载皮肤
    for skin_info in skin_list:
    # 拼接图片名字
    if skin_info[‘name’] == ‘default’:
    skin_name = ‘默认皮肤’
    else:
    if ‘ ‘ in skin_info[‘name’]:
    name_info = skin_info[‘name’].split(‘ ‘)
    skin_name = name_info[0]
    else:
    skin_name = skin_info[‘name’]
    hero_skin_name = hero_name + ‘-‘ + skin_name + ‘.jpg’
    self.download_skin(skin_info[‘id’], hero_skin_name)

    def download_skin(self, skin_id, skin_name):
    “””下载皮肤图片”””
    # 下载图片
    img_url = self.skin_url + skin_id + ‘.jpg’
    request = requests.get(img_url)
    if request.status_code == 200:
    print(‘downloading……%s’ % skin_name)
    img_path = os.path.join(self.skin_folder, skin_name)
    with open(img_path, ‘wb’) as img:
    img.write(request.content)
    else:
    print(‘img error!’)

    def make_folder(self):
    “””初始化,创建图片文件夹”””
    if not os.path.exists(self.skin_folder):
    os.mkdir(self.skin_folder)

    def run(self):
    # 获取英雄列表信息
    hero_json = self.get_hero_list()
    hero_keys = hero_json[‘keys’]
    # 循环遍历英雄
    for hero_id, hero_code in hero_keys.items():
    hero_name = hero_json[‘data’][hero_code][‘name’]
    hero_info = self.get_hero_info(hero_id)
    if hero_info:
    skin_list = hero_info[‘result’][hero_id][‘skins’]
    # 下载皮肤
    self.download_skin_list(skin_list, hero_name)
    else:
    print(‘英雄【%s】的皮肤获取有问题……’ % hero_name)

    # 程序执行入口
    if __name__ == ‘__main__’:
    lol = GetLolSkin()
    # 创建图片存储文件
    lol.make_folder()
    # 执行脚本
    lol.run()

  • Characters_of_the_Three_Kingdoms – 三国人物结构化数据

    Characters_of_the_Three_Kingdoms – 三国人物结构化数据
    三国人物结构化数据
    为什么会有这个项目

    需求1:摆脱网上那些长篇累牍的文章;
    需求2:只是想简单查看下人物姓甚名谁、生辰八字、家住何地、三姑六婆;
    需求3:只是想简单查看下人物的历史简介、演义简介;
    需求4:只是想简单查看下人物的历史评价;
    需求5:只是想简单查看下人物的…
    需求6:想集中查看多个人物的资料;
    需求7:想获取完整而不累赘的结构化数据,自己开发应用尽情发挥;

    需求N:…

    有了数据能干嘛
    有了数据,除了不能上天入地,剩下的就看少年你自己的活泼思想了。
    数据来源
    数据主要整理自 维基百科 、百度百科 和其他网络资源。
    数据展示 DEMO

    所有已经完成的人物数据可查看数据展示 DEMO。
    DEMO 页面使用 ajax 获取 characters 文件夹的 json 文件,若要本地运行 DEMO 页面,需本地启动 server。将项目 clone 到本地后,执行:

    npm run start

    gulp
    然后浏览器打开 localhost:4300 即可。
    数据示例
    {
    // 姓名
    “name”: “刘备”,
    // 字
    “courtesyName”: “玄德”,
    // 号
    “pseudonym”: null,
    // 其他称谓
    “aliase”: [
    {
    “name”: “汉先主”,
    “desc”: null
    },
    {
    “name”: “先主”,
    “desc”: “三国志、华阳国志等称为先主”
    },
    {
    “name”: “汉主”,
    “desc”: “资治通鉴称刘备父子为汉主”
    }
    ],
    // 乳名、小名、小字
    “infantName”: null,
    // 性别:1 男,2 女
    “gender”: 1,
    // 头像
    “avatar”: “./images/avatars/刘备.jpg”,
    // 所属势力
    “faction”: “蜀汉”,
    // 出生日期
    “birthdate”: “161年”,
    // 出生地点:古时地名
    “birthplace”: “幽州涿郡涿县”,
    // 出生地点:现在地名
    “birthplacePresentDay”: “河北省涿州市”,
    // 逝世日期
    “deathdate”: “223年6月10日”,
    // 逝世地点:古时地名
    “deathplace”: “白帝城永安宫”,
    // 逝世地点:现在地名
    “deathplacePresentDay”: “重庆市奉节县”,
    // 在位时期
    “tenure”: “汉中王:219年-221年;蜀主:221年5月15日-223年6月10日”,
    // 职位
    “position”: [“蜀国皇帝”],
    // 封爵
    “peerage”: null,
    // 封地
    “enfeoffment”: null,
    // 侍奉的帝王
    “monarch”: null,
    // 谥号
    “posthumousName”: [“昭烈皇帝”],
    // 庙号
    “templeName”: [“烈祖”],
    // 世系、氏族
    “genealogy”: null,
    // 历史上的简介
    “historicalBriefIIntroduction”: “蜀汉的开国皇帝,相传是汉景帝之子中山靖王刘胜的后代…”,
    // 演义上的简介
    “novelisticBriefIIntroduction”: “刘备,蜀汉的开国皇帝,汉景帝之子中山靖王刘胜的后代…”,
    // 家庭成员
    // 若名不详,则 name 字段为 名不详
    “family”: {
    “father”: {
    “character”: [
    {
    “name”: “刘弘”,
    “desc”: “东汉末年的州郡小官”
    }
    ],
    “desc”: null
    },
    “mother”: {
    “character”: [
    {
    “name”: “名不详”,
    “desc”: null
    }
    ],
    “desc”: null
    },
    “brothers”: null,
    “sisters”: null,
    “spouse”: {
    “character”: [
    {
    “name”: “甘夫人”,
    “desc”: “沛人,妾室,刘禅生母,曾于长阪被困,幸得赵云解救。后病死,谥皇思夫人,后再追谥昭烈皇后,与刘备合葬。”
    },
    {
    “name”: “糜夫人”,
    “desc”: “麋竺之妹,于刘备在豫州落难时,麋竺将她嫁给刘备。”
    },
    {
    “name”: “孙夫人”,
    “desc”: “孙权之妹,与刘备结为政治婚姻,后刘备入蜀,孙权接回她,再无记录。”
    },
    {
    “name”: “穆皇后”,
    “desc”: “吴氏,吴懿之妹,刘瑁遗孀,刘备入蜀后纳为夫人,后为汉中王后。刘禅即位时,尊她为皇太后,称长乐宫。延熙八年病死,与刘备合葬。”
    }
    ],
    “desc”: “甘夫人被刘备纳为妾室时,因他“数丧嫡室”,而主内事。数位嫡室的身份已不可考。仅知建安元年(196年),吕布曾俘虏刘备的妻儿[32],转至广陵郡海西县时,又娶了麋夫人。次子刘永和三子刘理各自的生母亦不可考,仅知非正室且非同一人。”
    },
    “sons”: {
    “character”: [
    {
    “name”: “刘禅”,
    “desc”: “字公嗣,刘备长子。后登上皇位。乳名阿斗。”
    },
    {
    “name”: “刘永”,
    “desc”: “字公寿,刘备次子。先为鲁王,后封为甘陵王。与刘禅宠臣黄皓不和,被刘禅疏远。后东迁洛阳,拜奉车都尉,封为乡侯。”
    },
    {
    “name”: “刘理”,
    “desc”: “字奉孝,刘备三子。先为梁王,后封为安平王。早卒,谥为悼王。”
    },
    {
    “name”: “刘封”,
    “desc”: “刘备养子。本姓寇,刘备入蜀后委任为将,但因关羽兵败时不予救援及逼反孟达丧失上庸之责遭赐死。”
    }
    ],
    “desc”: null
    },
    “daughters”: {
    “character”: [
    {
    “name”: “名不详”,
    “desc”: null
    },
    {
    “name”: “名不详”,
    “desc”: null
    }
    ],
    “desc”: “有二女于刘备南逃至长坂时被曹将曹纯所俘。”
    }
    },
    // 历史评价
    “historicalEvaluations”: [
    “刘元起:“吾宗中有此儿,非常人也。”(《三国志·蜀书·先主传第二》)”,
    “陈登:“雄姿杰出,有王霸之略,吾敬刘玄德。”(《三国志·魏书·桓二陈徐卫卢传第二十二》)”,
    “袁绍:“刘玄德弘雅有信义,今徐州乐戴之,诚副所望也。”(《三国志·蜀书·先主传第二》)”
    ]
    }
    已经完成的人物数据
    所有已经完成的人物数据可查看 DEMO。

    刘备
    诸葛亮
    曹操
    孙权
    张让
    张角
    张宝
    张梁
    张飞
    张钧

    张举
    张纯
    张济
    张辽
    张郃
    张邈
    张超
    张杨
    张虎
    张统

    张闿
    张燕
    张昭
    张纮
    张英
    张勋
    张绣
    张鲁
    张道陵
    张衡

    张????
    张南
    张南
    张武
    张温
    张温
    张允
    张横
    张既
    张卫

    张松
    张任
    张肃
    张翼
    张著
    张音
    张爽
    张裔
    张达
    张苞

    张嶷
    张休
    张茂
    张当
    张特
    张约
    张缉

    意见建议
    所有数据整理自网络,且鄙人才疏学浅,一定会有疏忽错误,欢迎指正。
    如果你有好的建议或意见,欢迎提 issue 反馈。
    联系方式
    Email: 851399101@qq.com
    License
    MIT
    Copyright (c) 2018-present, myvin

  • Characters_of_the_Three_Kingdoms – 三国人物结构化数据

    Characters_of_the_Three_Kingdoms – 三国人物结构化数据
    三国人物结构化数据
    为什么会有这个项目

    需求1:摆脱网上那些长篇累牍的文章;
    需求2:只是想简单查看下人物姓甚名谁、生辰八字、家住何地、三姑六婆;
    需求3:只是想简单查看下人物的历史简介、演义简介;
    需求4:只是想简单查看下人物的历史评价;
    需求5:只是想简单查看下人物的…
    需求6:想集中查看多个人物的资料;
    需求7:想获取完整而不累赘的结构化数据,自己开发应用尽情发挥;

    需求N:…

    有了数据能干嘛
    有了数据,除了不能上天入地,剩下的就看少年你自己的活泼思想了。
    数据来源
    数据主要整理自 维基百科 、百度百科 和其他网络资源。
    数据展示 DEMO

    所有已经完成的人物数据可查看数据展示 DEMO。
    DEMO 页面使用 ajax 获取 characters 文件夹的 json 文件,若要本地运行 DEMO 页面,需本地启动 server。将项目 clone 到本地后,执行:

    npm run start

    gulp
    然后浏览器打开 localhost:4300 即可。
    数据示例
    {
    // 姓名
    “name”: “刘备”,
    // 字
    “courtesyName”: “玄德”,
    // 号
    “pseudonym”: null,
    // 其他称谓
    “aliase”: [
    {
    “name”: “汉先主”,
    “desc”: null
    },
    {
    “name”: “先主”,
    “desc”: “三国志、华阳国志等称为先主”
    },
    {
    “name”: “汉主”,
    “desc”: “资治通鉴称刘备父子为汉主”
    }
    ],
    // 乳名、小名、小字
    “infantName”: null,
    // 性别:1 男,2 女
    “gender”: 1,
    // 头像
    “avatar”: “./images/avatars/刘备.jpg”,
    // 所属势力
    “faction”: “蜀汉”,
    // 出生日期
    “birthdate”: “161年”,
    // 出生地点:古时地名
    “birthplace”: “幽州涿郡涿县”,
    // 出生地点:现在地名
    “birthplacePresentDay”: “河北省涿州市”,
    // 逝世日期
    “deathdate”: “223年6月10日”,
    // 逝世地点:古时地名
    “deathplace”: “白帝城永安宫”,
    // 逝世地点:现在地名
    “deathplacePresentDay”: “重庆市奉节县”,
    // 在位时期
    “tenure”: “汉中王:219年-221年;蜀主:221年5月15日-223年6月10日”,
    // 职位
    “position”: [“蜀国皇帝”],
    // 封爵
    “peerage”: null,
    // 封地
    “enfeoffment”: null,
    // 侍奉的帝王
    “monarch”: null,
    // 谥号
    “posthumousName”: [“昭烈皇帝”],
    // 庙号
    “templeName”: [“烈祖”],
    // 世系、氏族
    “genealogy”: null,
    // 历史上的简介
    “historicalBriefIIntroduction”: “蜀汉的开国皇帝,相传是汉景帝之子中山靖王刘胜的后代…”,
    // 演义上的简介
    “novelisticBriefIIntroduction”: “刘备,蜀汉的开国皇帝,汉景帝之子中山靖王刘胜的后代…”,
    // 家庭成员
    // 若名不详,则 name 字段为 名不详
    “family”: {
    “father”: {
    “character”: [
    {
    “name”: “刘弘”,
    “desc”: “东汉末年的州郡小官”
    }
    ],
    “desc”: null
    },
    “mother”: {
    “character”: [
    {
    “name”: “名不详”,
    “desc”: null
    }
    ],
    “desc”: null
    },
    “brothers”: null,
    “sisters”: null,
    “spouse”: {
    “character”: [
    {
    “name”: “甘夫人”,
    “desc”: “沛人,妾室,刘禅生母,曾于长阪被困,幸得赵云解救。后病死,谥皇思夫人,后再追谥昭烈皇后,与刘备合葬。”
    },
    {
    “name”: “糜夫人”,
    “desc”: “麋竺之妹,于刘备在豫州落难时,麋竺将她嫁给刘备。”
    },
    {
    “name”: “孙夫人”,
    “desc”: “孙权之妹,与刘备结为政治婚姻,后刘备入蜀,孙权接回她,再无记录。”
    },
    {
    “name”: “穆皇后”,
    “desc”: “吴氏,吴懿之妹,刘瑁遗孀,刘备入蜀后纳为夫人,后为汉中王后。刘禅即位时,尊她为皇太后,称长乐宫。延熙八年病死,与刘备合葬。”
    }
    ],
    “desc”: “甘夫人被刘备纳为妾室时,因他“数丧嫡室”,而主内事。数位嫡室的身份已不可考。仅知建安元年(196年),吕布曾俘虏刘备的妻儿[32],转至广陵郡海西县时,又娶了麋夫人。次子刘永和三子刘理各自的生母亦不可考,仅知非正室且非同一人。”
    },
    “sons”: {
    “character”: [
    {
    “name”: “刘禅”,
    “desc”: “字公嗣,刘备长子。后登上皇位。乳名阿斗。”
    },
    {
    “name”: “刘永”,
    “desc”: “字公寿,刘备次子。先为鲁王,后封为甘陵王。与刘禅宠臣黄皓不和,被刘禅疏远。后东迁洛阳,拜奉车都尉,封为乡侯。”
    },
    {
    “name”: “刘理”,
    “desc”: “字奉孝,刘备三子。先为梁王,后封为安平王。早卒,谥为悼王。”
    },
    {
    “name”: “刘封”,
    “desc”: “刘备养子。本姓寇,刘备入蜀后委任为将,但因关羽兵败时不予救援及逼反孟达丧失上庸之责遭赐死。”
    }
    ],
    “desc”: null
    },
    “daughters”: {
    “character”: [
    {
    “name”: “名不详”,
    “desc”: null
    },
    {
    “name”: “名不详”,
    “desc”: null
    }
    ],
    “desc”: “有二女于刘备南逃至长坂时被曹将曹纯所俘。”
    }
    },
    // 历史评价
    “historicalEvaluations”: [
    “刘元起:“吾宗中有此儿,非常人也。”(《三国志·蜀书·先主传第二》)”,
    “陈登:“雄姿杰出,有王霸之略,吾敬刘玄德。”(《三国志·魏书·桓二陈徐卫卢传第二十二》)”,
    “袁绍:“刘玄德弘雅有信义,今徐州乐戴之,诚副所望也。”(《三国志·蜀书·先主传第二》)”
    ]
    }
    已经完成的人物数据
    所有已经完成的人物数据可查看 DEMO。

    刘备
    诸葛亮
    曹操
    孙权
    张让
    张角
    张宝
    张梁
    张飞
    张钧

    张举
    张纯
    张济
    张辽
    张郃
    张邈
    张超
    张杨
    张虎
    张统

    张闿
    张燕
    张昭
    张纮
    张英
    张勋
    张绣
    张鲁
    张道陵
    张衡

    张????
    张南
    张南
    张武
    张温
    张温
    张允
    张横
    张既
    张卫

    张松
    张任
    张肃
    张翼
    张著
    张音
    张爽
    张裔
    张达
    张苞

    张嶷
    张休
    张茂
    张当
    张特
    张约
    张缉

    意见建议
    所有数据整理自网络,且鄙人才疏学浅,一定会有疏忽错误,欢迎指正。
    如果你有好的建议或意见,欢迎提 issue 反馈。
    联系方式
    Email: 851399101@qq.com
    License
    MIT
    Copyright (c) 2018-present, myvin

  • 浅谈easy-mock 最好的备胎没有之一

    引言
    ​  今天我们来聊聊Mock,随着互联网发展,这两年前后端分离的开发模式兴起,Mock也从以住的幕后走上了台面,让更多的人而得知,以前传统的开发方式Mock大多局限在后端人员接触较多一些。
      Mock已经是老生常谈了,网上一搜索就很多,各位前辈们都讲的很到位,但今天我只讲它——easy-mock。
      为什么会突然来聊它,这个就说来话长了,个人简介里就说过,专注于分享工作中遇到的坑,但这一次不是我的坑,来源于QQ群友(# 如果您有想知道的故事,而正好我也会,那么就由我为您讲出来吧,欢迎留言哦 # ),请看下图:
    这里是@IT·平头哥联盟,我是首席填坑官—苏南,用心分享 做有温度的攻城狮。
    什么是Mock
      什么是Mock?? Mock其实就是真实数据存在之前,即调试期间的代替品,是个虚拟的存在,用人话讲它就是个备胎,如女生长的好看,追她的人多,但又不是很满意但也不拒绝,在自己心仪的小哥哥出现之前,一直吊着你????!
    如何Mock数据?

    不要告诉我 new 一个哦,对象可以 new,备胎可new不出来呢????;
    方法一:最low的方式将 Mock 数据写在代码里、json文件里;
    方法二:利用 Charles 、Fiddler等代理工具,将 URL 映射到本地文件;
    方法三:本地起 Mock Server,即mockjs,有点麻烦每次修改了后还要重启服务,nodemon能解决,但开的东西多了,电脑卡出翔,维护也麻烦;
    方法四:规范些的公司自己已经集成了一套mock数据体系;

    重点来了:easy-mock一个在线 Mock 平台,活儿好又性感是你备胎的最佳选择。
    当然优秀的你可能还有很多其他的方式,欢迎补充。

    //mock 基本使用示例
    import Mock from “mockjs”;
    Mock.mock({
    “code”: 0,
    “message”: “请求成功”,
    “data|20”: [{
    “name”: “@cname”,//cname 中文,name 英文
    “userId”: “@id”,
    “lastDate”: “@datetime”
    }]
    })

    什么是easy-mock,能给我们带来什么?

    Easy Mock 是一个可视化,并且能快速生成 模拟数据 的持久化服务,
    Easy Mock 支持基于 Swagger 创建项目,以节省手动创建接口的时间;
    简单点说:Easy Mock就是一个在线创建mock的服务平台,帮你省去你 配置、安装、起服务、维护、多人协作Mock数据不互通等一系列繁琐的操作, 它能在不用1秒钟的时间内给你所要的一切,呼之即来、挥之即去的2018最优秀备胎没有之一,完全不用担心负任何责任哦。
    更多优点它在等你去发现哦……

    深入浅出 – 简介

    就跟人一样长的再好看,了解过后才懂,一样东西也是如何,谁用谁知道;
    Easy Mock支持团队协作,也可以是个人项目,
    以下以个人项目为例,与多人协作没有区别,只是少了成员邀请;

    深入浅出 – Mock语法回顾

    @ip -> 随机输出一个ip;
    @id -> 随机输出长度18的字符,不接受参数;
    “array|1-10” -> 随机输出1-10长度的数组,也可以直接是固定长度;
    “object|2” -> 输出一个两个key值的对象,
    “@image()” 返回一个占位图url,支持size, background, foreground, format, text;
    等等,这里就不再一一介绍。

    深入浅出 – 创建一个接口

    它的写法,跟Mock.js一模一样,上面代码已经展示过,更多示例

    使用Easy Mock创建一个接口,请看下图:

    深入浅出 – 高阶用法 Function

    在线编辑,它也能支持 function ,
    是不是很优秀,能获取到全部请求头,可以让我们像写在js里一样写逻辑,写运算,
    当然它肯定是还有很多局限性的,如并不支持ES6,
    有一点需要注意的是 function 里要写传出Mock对象,不能直接@…,
    来看示例:

    对象
    描述

    Mock
    Mock 对象

    _req.url
    获得请求 url 地址

    _req.method
    获取请求方法

    _req.params
    获取 url 参数对象

    _req.querystring
    获取查询参数字符串(url中?后面的部分),不包含 ?

    _req.query
    将查询参数字符串进行解析并以对象的形式返回,如果没有查询参数字字符串则返回一个空对象

    _req.body
    当 post 请求以 x-www-form-urlencoded 方式提交时,我们可以拿到请求的参数对象


    _req.cookies、ip、host等等,我只是一个代码的搬运,更详细请看这里

    //简单模拟登录,根据用户传入的参数,返回不同逻辑数据
    {
    defaultName:function({_req}){
    return _req.query.name;
    },
    code: function({_req}){
    return this.defaultName ? 0 : -97;
    },
    message: function({_req}) {
    return this.defaultName ? “登录成功” : “参数错误”;
    },
    data: function({_req,Mock}){
    return this.defaultName ? {
    token: Mock.mock(“@guid()”),
    userId: Mock.mock(“@id(5)”),
    cname: Mock.mock(“@cname()”),
    name: Mock.mock(“@name()”),
    avatar: Mock.mock(“@image(200×100, #FF6600)”),
    other:”@IT·平头哥联盟-首席填坑官∙苏南 带你再谈Mock数据之easy-mock”
    }:{}
    }
    }

    深入浅出 – 在线调试

    再优秀的工程师写出的代码也一样要测试,没有人敢说自己的代码无Bug,

    Easy Mock 它是真的懂你的,已经为你准备好了,接口编写好后,立马就能让你测试,
    是不是觉得很棒棒呢??如果是你自己本地写mock数据,你需要重启服务、手动写参数、可能还需要整个测试页,
    知道你已经非常饥渴迫切的想要知道,具体的调试方式了:
    看不清吗??已经为你备好在线调试链接,支持POST、GET、PUT等方式,因gif图加载比较大,就不一一演示了

    结尾:
      天下无不散之宴席,又到说再见的时候了,以上就是今天苏南为大家带来的分享,您GET到了吗?Easy Mock更多强大之处自己去折腾吧,#用心分享 做有温度的攻城狮#,希望今天的分享能给您带来些许成长,如果觉得不错记得点个赞哦,,顺便关注下方公众号就更棒了呢,每周为您推最新分享????????。

    更多文章:
    immutability因React官方出镜之使用总结分享!小程序项目之做完项目老板给我加了6k薪资~小程序项目之填坑小记面试踩过的坑,都在这里了~你应该做的前端性能优化之总结大全!如何给localStorage设置一个过期时间?动画一点点 – 如何用CSS3画出懂你的3D魔方?动画一点点 – 手把手教你如何绘制一辆会跑车SVG Sprites Icon的使用技巧
    作者:苏南 – 首席填坑官链接:https://blog.csdn.net/weixin_…
    交流群:912594095、公众号:honeyBadger8
    本文原创,著作权归作者所有。商业转载请联系@IT·平头哥联盟获得授权,非商业转载请注明原链接及出处。