简介

本文出自,原文链接:http://huxinmin.com

谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

禁止非法采集,原文地址,原文链接:http://huxinmin.com

本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
  • 谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
  • 禁止非法采集,原文地址,原文链接:http://huxinmin.com原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

  • 本文出自,原文链接:http://huxinmin.com
  • 未经作者同意,谢绝转载,原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com
    本文出自,原文链接:http://huxinmin.com
  • 未经作者同意,谢绝转载,原文链接:http://huxinmin.com
  • 本文原文地址,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com
    未经作者同意,谢绝转载,原文链接:http://huxinmin.com
  • 禁止非法采集,原文地址,原文链接:http://huxinmin.com
  • 本文出自,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

      本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com
    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

  • 谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
  • 本文原文地址,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com
  • 本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
  • 本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
    本文原文地址,原文链接:http://huxinmin.com
    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
  • 原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
  • 谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com
    未经作者同意,谢绝转载,原文链接:http://huxinmin.com
    本文出自,原文链接:http://huxinmin.com
  • 禁止非法采集,原文地址,原文链接:http://huxinmin.com
  • 本文原文地址,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com
    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com
    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com本文出自,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

  • 本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
  • 本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com
    本文原文地址,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com
    未经作者同意,谢绝转载,原文链接:http://huxinmin.com
      本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
  • 谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
  • 本文原文地址,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com
    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
    本文出自,原文链接:http://huxinmin.com
    未经作者同意,谢绝转载,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
  • 未经作者同意,谢绝转载,原文链接:http://huxinmin.com
  • 本文原文地址,原文链接:http://huxinmin.com
    本文出自,原文链接:http://huxinmin.com
    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com
    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com
      本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
    未经作者同意,谢绝转载,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com

  • 未经作者同意,谢绝转载,原文链接:http://huxinmin.com
  • 谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
  • 禁止非法采集,原文地址,原文链接:http://huxinmin.com
  • 未经作者同意,谢绝转载,原文链接:http://huxinmin.com
    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
    本文原文地址,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

  • 谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
  • 本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

  • 原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
  • 谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
    禁止非法采集,原文地址,原文链接:http://huxinmin.com
    本文原文地址,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com
  • 本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
  • 禁止非法采集,原文地址,原文链接:http://huxinmin.com
  • 禁止非法采集,原文地址,原文链接:http://huxinmin.com
    未经作者同意,谢绝转载,原文链接:http://huxinmin.com
    本文原文地址,原文链接:http://huxinmin.com禁止非法采集,原文地址,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

      本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

  • 本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
  • 未经作者同意,谢绝转载,原文链接:http://huxinmin.com禁止非法采集,原文地址,原文链接:http://huxinmin.com
    未经作者同意,谢绝转载,原文链接:http://huxinmin.com
  • 本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
  • 谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
  • 本文原文地址,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com

  • 本文出自,原文链接:http://huxinmin.com
  • 本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
  • 未经作者同意,谢绝转载,原文链接:http://huxinmin.com

  • 原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
  • 本文原文地址,原文链接:http://huxinmin.com
  • 谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com
    本文原文地址,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
    禁止非法采集,原文地址,原文链接:http://huxinmin.com
    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

  • 原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
  • 本文原文地址,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

  • 原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
  • 本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
  • 本文出自,原文链接:http://huxinmin.com
  • 本文原文地址,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com
    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
    本文出自,原文链接:http://huxinmin.com本文出自,原文链接:http://huxinmin.com谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
  • 禁止非法采集,原文地址,原文链接:http://huxinmin.com
  • 禁止非法采集,原文地址,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
    未经作者同意,谢绝转载,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

  • 未经作者同意,谢绝转载,原文链接:http://huxinmin.com
  • 本文出自,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

  • 谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
  • 本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

  • 禁止非法采集,原文地址,原文链接:http://huxinmin.com
  • 本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
    本文出自,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
  • 未经作者同意,谢绝转载,原文链接:http://huxinmin.com
  • 禁止非法采集,原文地址,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com
  • 谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
  • 原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
  • 未经作者同意,谢绝转载,原文链接:http://huxinmin.com
  • 本文原文地址,原文链接:http://huxinmin.com
    本文原文地址,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

  • 谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
  • 原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

  • 未经作者同意,谢绝转载,原文链接:http://huxinmin.com
  • 谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com
  • 谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
  • 本文原文地址,原文链接:http://huxinmin.com
    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

  • 本文原文地址,原文链接:http://huxinmin.com
  • 本文原文地址,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com
    本文出自,原文链接:http://huxinmin.com
    未经作者同意,谢绝转载,原文链接:http://huxinmin.com
    本文出自,原文链接:http://huxinmin.com

    本文原文地址,原文链接:http://huxinmin.com

    未经作者同意,谢绝转载,原文链接:http://huxinmin.com
    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
      本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com

  • 本文原文地址,原文链接:http://huxinmin.com
  • 原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
  • 本文原文地址,原文链接:http://huxinmin.com
  • 未经作者同意,谢绝转载,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com
    本文出自,原文链接:http://huxinmin.com
    禁止非法采集,原文地址,原文链接:http://huxinmin.com
    原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com

    本文出自,原文链接:http://huxinmin.com
    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    禁止非法采集,原文地址,原文链接:http://huxinmin.com

    谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com禁止非法采集,原文地址,原文链接:http://huxinmin.com
  • 原文出自[胡新敏的个人博客] 转载请保留原文链接:http://huxinmin.com
  • 谢绝转载,原文地址在胡新敏的个人博客,原文链接:http://huxinmin.com
  • 本文出自,原文链接:http://huxinmin.com
  • 本文原文地址,原文链接:http://huxinmin.com
  • 本文原文地址,原文链接:http://huxinmin.com

    本文禁止任何形式的非法采集,原文地址:胡新敏的个人博客,原文链接:http://huxinmin.com

    body-parser是一个HTTP请求体解析中间件,使用这个模块可以解析JSON、Raw、文本、URL-encoded格式的请求体,Express框架中就是使用这个模块做为请求体解析中间件。

    POST报文

    一个HTTP请求报文由请求行(request line)、请求头部(header)、空行和请求数据4个部分组成,一个常见的POST的请求报文格式如下:

    POST /test/ HTTP/1.1  
    Accept-Encoding: gzip  
    Content-Length: 225873  
    Content-Type: text/plain; charset=utf8  
    Host: 127.0.0.1  
    Connection: Keep-Alive
    
    huxinmin
    
    • Content-Type:请求报文主体的类型、编码。常见的类型有text/plainapplication/jsonapplication/x-www-form-urlencodedmultipart/form-data。常见的编码有utf8gbk等。
    • Content-Encoding:声明报文主体的压缩格式,常见的取值有gzipdeflateidentity
    • 报文主体:这里是个普通的文本字符串huxinmin

    原生环境解析

    Node.js 原生HTTP模块中,是将用户请求数据封装到了用于请求对象req中,该对象是一个IncomingMessage,该对象同时也是一个可读流对象。在原生HTTP服务器,或不依赖第三方解析模块时,可以像下面这样接收并解析请求体:

    const http = require('http');
    //用http模块创建一个http服务端 
    http.createServer(function(req, res) {
      if (req.method.toLowerCase() === 'post') {
        var body = '';   
        req.on('data', function(chunk){
          body += chunk;
        });
        req.on('end', function(){
          if(req.headers['content-type'].indexOf('application/json')>=0){
            // JSON 格式请求体解析
            JSON.parse(body);
          } else if(req.headers['content-type'].indexOf('application/octet-stream')>=0){
            // Raw 格式请求体解析
            // ……
          } else if(req.headers['content-type'].indexOf('text/plain')>=0){
            // text 文本格式请求体解析
            // ……
          } else if(req.headers['content-type'].indexOf('application/x-www-form-urlencoded')>=0){
            // URL-encoded 格式请求体解析
            // ……
          } else {
              // 其它格式解析
          }
        })
      } else {
        res.end('其它提交方式');
      }
    }).listen(3000);
    

    body-parser主要做了什么

    • 处理不同类型的请求体:比如textjsonurlencoded等,对应的报文主体的格式不同。
    • 处理不同的编码:比如utf8gbk等。
    • 处理不同的压缩类型:比如gzipdeflare等。
    • 其他边界、异常的处理。

    body-parser使用方法以及API

    注意body-parser不支持multipart/form-data的解析。

    var bodyParser = require('body-parser')
    
    • bodyParser.json(options) - 解析JSON格式

      Options有如下这些选项:
      • inflate - 设置为true时,deflate压缩数据会被解压缩;设置为true时,deflate压缩数据会被拒绝。默认为true。
      • limit - 设置请求的最大数据量。默认为'100kb'
      • reviver - 传递给JSON.parse()方法的第二个参数,详见JSON.parse()
      • strict - 设置为true时,仅会解析Array和Object两种格式;设置为false会解析所有JSON.parse支持的格式。默认为true
      • type - 该选项用于设置为指定MIME类型的数据使用当前解析中间件。这个选项可以是一个函数或是字符串,当是字符串是会使用type-is来查找MIMI类型;当为函数是,中间件会通过fn(req)来获取实际值。默认为application/json
      • verify - 这个选项仅在verify(req, res, buf, encoding)时受支持
    • bodyParser.raw(options) - 解析二进制格式

      Options有如下这些选项:
      • inflate - 设置为true时,deflate压缩数据会被解压缩;设置为true时,deflate压缩数据会被拒绝。默认为true。
      • limit - 设置请求的最大数据量。默认为'100kb'
      • type - 该选项用于设置为指定MIME类型的数据使用当前解析中间件。这个选项可以是一个函数或是字符串,当是字符串是会使用type-is来查找MIMI类型;当为函数是,中间件会通过fn(req)来获取实际值。默认为application/octet-stream
      • verify - 这个选项仅在verify(req, res, buf, encoding)时受支持
    • bodyParser.text(options) - 解析文本格式

      Options有如下选项:
      • defaultCharset - 如果Content-Type后没有指定编码时,使用此编码。默认为'utf-8'
      • inflate - 设置为true时,deflate压缩数据会被解压缩;设置为true时,deflate压缩数据会被拒绝。默认为true。
      • limit - 设置请求的最大数据量。默认为'100kb'
      • type - 该选项用于设置为指定MIME类型的数据使用当前解析中间件。这个选项可以是一个函数或是字符串,当是字符串是会使用type-is来查找MIMI类型;当为函数是,中间件会通过fn(req)来获取实际值。默认为application/octet-stream
      • verify - 这个选项仅在verify(req, res, buf, encoding)时受支持
    • bodyParser.urlencoded(options) - 解析文本格式

      Options有如下这些选项:
      • extended - 当设置为false时,会使用querystring库解析URL编码的数据;当设置为true时,会使用qs库解析URL编码的数据。后没有指定编码时,使用此编码。默认为true
      • inflate - 设置为true时,deflate压缩数据会被解压缩;设置为true时,deflate压缩数据会被拒绝。默认为true。
      • limit - 设置请求的最大数据量。默认为'100kb'
      • parameterLimit - 用于设置URL编码值的最大数据。默认为1000
      • type - 该选项用于设置为指定MIME类型的数据使用当前解析中间件。这个选项可以是一个函数或是字符串,当是字符串是会使用type-is来查找MIMI类型;当为函数是,中间件会通过fn(req)来获取实际值。默认为application/octet-stream
      • verify - 这个选项仅在verify(req, res, buf, encoding)时受支持

    body-parser依赖项简介

    这是body-parser1.18.3的依赖项配置:

      "dependencies": {
        "bytes": "3.0.0",
        "content-type": "~1.0.4",
        "debug": "2.6.9",
        "depd": "~1.1.2",
        "http-errors": "~1.6.3",
        "iconv-lite": "0.4.23",
        "on-finished": "~2.3.0",
        "qs": "6.5.2",
        "raw-body": "2.3.3",
        "type-is": "~1.6.16"
      }
    

    bytes

    一个将不同比特单位进行互转的工具,使用方法如下:

    var bytes = require('bytes');
    bytes(number|string,[options])
    

    可选项有如下这些:

    • decimalPlaces[number|null]输出最大小数位数,默认为2位
    • fixedDecimals[boolean|null]是否显示最大小数位数,默认为false
    • thousandsSeparator[string|null]千位分割符,默认为空
    • unit[string|null]输出的单位((B/KB/MB/GB/TB),默认为空(自动检测)
    • unitSeparator[string|null]输出单位和数字之间分割符,默认为空

    例子:

    bytes(1024);
    // output: '1KB'
    bytes(1000);
    // output: '1000B'
    bytes(1000, {thousandsSeparator: ' '});
    // output: '1 000B'
    bytes(1024 * 1.7, {decimalPlaces: 0});
    // output: '2KB'
    bytes(1024, {unitSeparator: ' '});
    // output: '1 KB'
    bytes('1KB');
    // output: 1024
    bytes('1024');
    // output: 1024
    bytes(1024);
    // output: 1024
    

    content-type

    创建并且解析http content-type头,根据RFC7231,它有如下两个API:

    var contentType = require('content-type')
    
    • contentType.parse(string|req|res) 解析一个content-type并返回一个对象,这个对象包含两个属性,一个是type,一个是parameters
    • contentType.format(obj) 从一个对象中生成一个content-type字符串,obj应包含typeparameters两个属性。

    debug

    一个轻量的调试js代码的库,具体可以参照我的这篇博客:Debug.js学习

    depd

    一个用于声明废弃属性或方法的库,具体可参考我的这篇博客depd学习教程

    http-errors

    http-errors是一个用于创建HTTP错误的工具。使用方法很简单:

    var createError = require('http-errors')
    var err = createError([status], [message], [properties])
    //或者
    var err = new createError[code || name]([msg]))
    //例如
    var err = new createError.NotFound()
    var err = createError(404, 'This video does not exist!')
    

    生成的错误对象具有如下属性:

    • expose - 作为是否应该发送到客户端的信号,默认为false当错误状态码大于500
    • headers - 发送到客户端的header头属性,默认没有定义,若定义必须使用小写名
    • message - 错误消息
    • status - 错误状态码,statusCode的镜像为了兼容性
    • statusCode - 错误状态码,默认500

    iconv-lite

    纯js实现的字符编码解码工具,它具有如下特点:

    • 不需要原生汇编环境,可直接运行在操作系统或者沙箱环境中
    • 在很多火热的项目中被广泛使用,例如expressjsgruntyeoman
    • node-iconv更快
    • 直观的API
    • 支持流Nodejs v0.10+以上
    • 通过browserify可支持浏览器环境
    • 包含typescript的类型定义文件
    • 支持react-native(需要额外安装bufferstream模块)

    基本使用方法:

    var iconv = require('iconv-lite');
    // 将buffer转换为字符串
    str = iconv.decode(Buffer.from([0x68, 0x65, 0x6c, 0x6c, 0x6f]), 'win1251');
    // 将字符串转换为buffer
    buf = iconv.encode("Sample input string", 'win1251');
    // 检测是否支持该编码
    iconv.encodingExists("us-ascii")
    

    流式使用:

    // 将二进制流转换为字符串
    http.createServer(function(req, res) {
        var converterStream = iconv.decodeStream('win1251');
        req.pipe(converterStream);
    
        converterStream.on('data', function(str) {
            console.log(str); // Do something with decoded strings, chunk-by-chunk.
        });
    });
    // 对流进行编码解码
    fs.createReadStream('file-in-win1251.txt')
        .pipe(iconv.decodeStream('win1251'))
        .pipe(iconv.encodeStream('ucs2'))
        .pipe(fs.createWriteStream('file-in-ucs2.txt'));
    //所有的encode/decode 流操作都有一个 .collect(cb)方法来积累数据
    http.createServer(function(req, res) {
        req.pipe(iconv.decodeStream('win1251')).collect(function(err, body) {
            assert(typeof body == 'string');
            console.log(body); // full request body string
        });
    });
    

    支持的编码格式:

    • 所有的Nodejs原生编码:utf8, ucs2 / utf16-le, ascii, binary, base64, hex
    • 额外的unicode编码:utf16, utf16-be, utf-7, utf-7-imap
    • 广泛的单字节编码:Windows 125x, ISO-8859 , IBM/DOS, Macintosh, KOI8,以及所有iconv库支持的编码,例如latin1us-ascii
    • 广泛的多字节编码格式:CP932, CP936, CP949, CP950, GB2312, GBK, GB18030, Big5, Shift_JIS, EUC-JP

    on-finished

    该模块将会在HTTP request事件关闭,完成或者出错的时候执行一次回调函数。使用方法很简单:

    var onFinished = require('on-finished')
    onFinished(res, function (err, res) {}) //在res事件完成后执行一个回调
    onFinished(req, function (err, req) {})  //在req事件完成之后执行一个回调
    onFinished.isFinished(res)  //判断res是否已经完成
    onFinished.isFinished(req)  //判断req是否已经完成
    

    注意,不支持HTTP CONNECT 和HTTP Upgrade请求。

    qs

    具体可参考我的这篇博客qs.js学习教程

    raw-body

    该模块可以用来获取并验证二进制请求体流。使用方法如下:

    var getRawBody = require('raw-body');
    getRawBody(stream, [options], [callback]) //如果没有回调函数或者全局promise时则返回一个promise
    

    它的options有三个参数:

    • length流的长度,如果流的内容总计未达到规定的长度,则返回一个400状态码的错误
    • limit请求体比特大小的限制,使用的是bytes库进行格式化的,例如10050kb等,如果超出大小限制,会返回一个413状态码的错误。
    • encoding编码解码格式,默认不会进行解码,直接返回buffer实例,如果想要以utf-8进行解码,可以将之设为true,你可以使用任何iconv-lite支持的编码格式。

    实例:

    var getRawBody = require('raw-body')
    var http = require('http')
    var server = http.createServer(function (req, res) {
      getRawBody(req)
        .then(function (buf) {
          res.statusCode = 200
          res.end(buf.length + ' bytes submitted')
        })
        .catch(function (err) {
          res.statusCode = 500
          res.end(err.message)
        })
    })
    server.listen(3000)
    

    type-is

    该模块可以用来推断请求的内容类型。使用方法如下:

    typeis(req, ['json'])             // 'json'
    typeis(req, ['html', 'json'])     // 'json'
    typeis(req, ['application/*'])    // 'application/json'
    typeis(req, ['application/json']) // 'application/json'
    typeis(req, ['html']) // false
    typeis.hasBody(req) //是否具有body
    var mediaType = 'application/json'
    typeis.is(mediaType, ['json'])             // 'json'
    typeis.is(mediaType, ['html', 'json'])     // 'json'
    typeis.is(mediaType, ['application/*'])    // 'application/json'
    typeis.is(mediaType, ['application/json']) // 'application/json'
    typeis.is(mediaType, ['html']) // false
    

    源码解析

    body-parser源码的目录结构是这样的:

    index.js代码如下:

    var deprecate = require('depd')('body-parser')
    var parsers = Object.create(null)
    exports = module.exports = deprecate.function(bodyParser,
      'bodyParser: use individual json/urlencoded middlewares')
    /**
     * JSON parser.
     * @public
     */
    Object.defineProperty(exports, 'json', {
      configurable: true,
      enumerable: true,
      get: createParserGetter('json')
    })
    /**
     * Raw parser.
     * @public
     */
    Object.defineProperty(exports, 'raw', {
      configurable: true,
      enumerable: true,
      get: createParserGetter('raw')
    })
    /**
     * Text parser.
     * @public
     */
    Object.defineProperty(exports, 'text', {
      configurable: true,
      enumerable: true,
      get: createParserGetter('text')
    })
    /**
     * URL-encoded parser.
     * @public
     */
    Object.defineProperty(exports, 'urlencoded', {
      configurable: true,
      enumerable: true,
      get: createParserGetter('urlencoded')
    })
    /**
     * Create a middleware to parse json and urlencoded bodies.
     *
     * @param {object} [options]
     * @return {function}
     * @deprecated
     * @public
     */
    function bodyParser (options) {
      var opts = {}
      // exclude type option
      if (options) {
        for (var prop in options) {
          if (prop !== 'type') {
            opts[prop] = options[prop]
          }
        }
      }
      var _urlencoded = exports.urlencoded(opts)
      var _json = exports.json(opts)
      return function bodyParser (req, res, next) {
        _json(req, res, function (err) {
          if (err) return next(err)
          _urlencoded(req, res, next)
        })
      }
    }
    /**
     * Create a getter for loading a parser.
     * @private
     */
    function createParserGetter (name) {
      return function get () {
        return loadParser(name)
      }
    }
    /**
     * Load a parser module.
     * @private
     */
    function loadParser (parserName) {
      var parser = parsers[parserName]
    
      if (parser !== undefined) {
        return parser
      }
    
      // this uses a switch for static require analysis
      switch (parserName) {
        case 'json':
          parser = require('./lib/types/json')
          break
        case 'raw':
          parser = require('./lib/types/raw')
          break
        case 'text':
          parser = require('./lib/types/text')
          break
        case 'urlencoded':
          parser = require('./lib/types/urlencoded')
          break
      }
    
      // store to prevent invoking require()
      return (parsers[parserName] = parser)
    }
    

    这个文件暴露了一个模块,是一个废弃的方法bodyParser,而且暴露了四个属性jsontextrawurlencoded,这四个属性通过Object.defineProperty设置了setter方法,依赖了lib/types目录下的四个文件暴露出来的模块。

    lib目录下还有一个公共文件read.jslib/types目录下的其他四个文件使用,代码如下所示:

    'use strict'
    var createError = require('http-errors')
    var getBody = require('raw-body')
    var iconv = require('iconv-lite')
    var onFinished = require('on-finished')
    var zlib = require('zlib')
    module.exports = read
    function read (req, res, next, parse, debug, options) {
      var length
      var opts = options
      var stream
      // flag as parsed
      req._body = true
      // read options
      var encoding = opts.encoding !== null
        ? opts.encoding
        : null
      var verify = opts.verify
      try {
        // get the content stream
        stream = contentstream(req, debug, opts.inflate)
        length = stream.length
        stream.length = undefined
      } catch (err) {
        return next(err)
      }
      // set raw-body options
      opts.length = length
      opts.encoding = verify
        ? null
        : encoding
      // assert charset is supported
      if (opts.encoding === null && encoding !== null && !iconv.encodingExists(encoding)) {
        return next(createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
          charset: encoding.toLowerCase(),
          type: 'charset.unsupported'
        }))
      }
      // read body
      debug('read body')
      getBody(stream, opts, function (error, body) {
        if (error) {
          var _error
    
          if (error.type === 'encoding.unsupported') {
            // echo back charset
            _error = createError(415, 'unsupported charset "' + encoding.toUpperCase() + '"', {
              charset: encoding.toLowerCase(),
              type: 'charset.unsupported'
            })
          } else {
            // set status code on error
            _error = createError(400, error)
          }
          // read off entire request
          stream.resume()
          onFinished(req, function onfinished () {
            next(createError(400, _error))
          })
          return
        }
        // verify
        if (verify) {
          try {
            debug('verify body')
            verify(req, res, body, encoding)
          } catch (err) {
            next(createError(403, err, {
              body: body,
              type: err.type || 'entity.verify.failed'
            }))
            return
          }
        }
        // parse
        var str = body
        try {
          debug('parse body')
          str = typeof body !== 'string' && encoding !== null
            ? iconv.decode(body, encoding)
            : body
          req.body = parse(str)
        } catch (err) {
          next(createError(400, err, {
            body: str,
            type: err.type || 'entity.parse.failed'
          }))
          return
        }
    
        next()
      })
    }
    function contentstream (req, debug, inflate) {
      var encoding = (req.headers['content-encoding'] || 'identity').toLowerCase()
      var length = req.headers['content-length']
      var stream
      debug('content-encoding "%s"', encoding)
      if (inflate === false && encoding !== 'identity') {
        throw createError(415, 'content encoding unsupported', {
          encoding: encoding,
          type: 'encoding.unsupported'
        })
      }
      switch (encoding) {
        case 'deflate':
          stream = zlib.createInflate()
          debug('inflate body')
          req.pipe(stream)
          break
        case 'gzip':
          stream = zlib.createGunzip()
          debug('gunzip body')
          req.pipe(stream)
          break
        case 'identity':
          stream = req
          stream.length = length
          break
        default:
          throw createError(415, 'unsupported content encoding "' + encoding + '"', {
            encoding: encoding,
            type: 'encoding.unsupported'
          })
      }
      return stream
    }
    

    该文件将主要是将请求流进行处理,并使用raw-body模块解析出req.body。至于其他json.jstext.jsraw.jsurlencoded.js都是针对不同的类型具体的一些异常处理或者边界判断等,源码就不一一分析了。

    原生Nodejs实例

    首先我们写一个客户端发送请求client.js,代码如下:

    var http = require('http');
    var options = {
        hostname: '127.0.0.1',
        port: '8080',
        path: '/test',
        method: 'POST',
        headers: {
            'Content-Type': 'text/plain',
            'Content-Encoding': 'identity'
        }
    };
    var client = http.request(options, (res) => {
        res.pipe(process.stdout);
    });
    client.end('huxinmin');
    

    然后新建一个server.js服务端代码如下:

    var http = require('http');
    var bodyParser = require('./index');
    // 创建服务器
    http.createServer(function(req, res) {
        var textParser = bodyParser.text();
        textParser(req, res, function(err) {
            console.log(req.body)
            res.writeHead(200, { 'Content-Type': 'text/html' });
            var data = { message: 'huxinmin is send complete' }
            // 响应文件内容
            res.write(JSON.stringify(data));
            //  发送响应数据
            res.end();
        })
    }).listen(8080);
    // 控制台会输出以下信息
    console.log('Server running at http://127.0.0.1:8080/');
    

    启动代码,会看到前台返回了:

    {"message":"huxinmin is send complete"}
    

    服务端打印了输出:

    huxinmin
    

    参考资料