琛能IT

琛能IT 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织 www.qdchenneng.com 编辑
编辑

琛能IT www.qdchenneng.com

个人动态

琛能IT 收藏了文章 · 4月12日

超全面的权限系统设计方案!

我的公众号:MarkerHub,Java网站:https://markerhub.com

更多精选文章请点击:Java笔记大全.md

小Hub解读:

没想到小小角色权限系统也这么多内容,不过通常我都是直接找个开源权限系统作为基础开发业务,不过权限系统怎么设计的,这也还是要懂一下好。


  • 作者:iceblow
  • cnblogs.com/iceblow/p/11121362.html

前言

权限管理是所有后台系统的都会涉及的一个重要组成部分,主要目的是对不同的人访问资源进行权限的控制,避免因权限控制缺失或操作不当引发的风险问题,如操作错误,隐私数据泄露等问题。

目前在公司负责权限这块, 所以对权限这块的设计比较熟悉, 公司采用微服务架构, 权限系统自然就独立出来了, 其他业务系统包括商品中心, 订单中心, 用户中心, 仓库系统, 小程序, 多个 APP 等十几个系统和终端

1. 权限模型

迄今为止最为普及的权限设计模型是 RBAC 模型, 基于角色的访问控制(Role-Based Access Control)

1.1 RBAC0 模型

RBAC0 模型如下:

这是权限最基础也是最核心的模型, 它包括用户 / 角色 / 权限, 其中用户和角色是多对多的关系, 角色和权限也是多对多的关系。

用户是发起操作的主体, 按类型分可分为 2B 和 2C 用户, 可以是后台管理系统的用户, 可以是 OA 系统的内部员工, 也可以是面向 C 端的用户, 比如阿里云的用户。

角色起到了桥梁的作用, 连接了用户和权限的关系, 每个角色可以关联多个权限, 同时一个用户关联多个角色, 那么这个用户就有了多个角色的多个权限。

有人会问了为什么用户不直接关联权限呢?在用户基数小的系统, 比如 20 个人的小系统,管理员可以直接把用户和权限关联,工作量并不大,选择一个用户勾选下需要的权限就完事了。

但是在实际企业系统中,用户基数比较大, 其中很多人的权限都是一样的,就是个普通访问权限,如果管理员给 100 人甚至更多授权, 工作量巨大。

这就引入了 "角色 (Role)" 概念, 一个角色可以与多个用户关联, 管理员只需要把该角色赋予用户, 那么用户就有了该角色下的所有权限, 这样设计既提升了效率, 也有很大的拓展性。

  • 权限

是用户可以访问的资源, 包括页面权限, 操作权限, 数据权限:

  • 页面权限:

即用户登录系统可以看到的页面, 由菜单来控制, 菜单包括一级菜单和二级菜单, 只要用户有一级和二级菜单的权限, 那么用户就可以访问页面

  • 操作权限:

即页面的功能按钮,包括查看, 新增, 修改, 删除, 审核等,用户点击删除按钮时,后台会校验用户角色下的所有权限是否包含该删除权限。如果是, 就可以进行下一步操作, 反之提示无权限。

有的系统要求 "可见即可操作", 意思是如果页面上能够看到操作按钮, 那么用户就可以操作, 要实现此需求, 这里就需要前端来配合, 前端开发把用户的权限信息缓存, 在页面判断用户是否包含此权限, 如果有, 就显示该按钮, 如果没有, 就隐藏该按钮。

某种程度上提升了用户体验, 但是在实际场景可自行选择是否需要这样做

  • 数据权限:

数据权限就是用户在同一页面看到的数据是不同的,比如财务部只能看到其部门下的用户数据,采购部只看采购部的数据。

在一些大型的公司,全国有很多城市和分公司,比如杭州用户登录系统只能看到杭州的数据,上海用户只能看到上海的数据,解决方案一般是把数据和具体的组织架构关联起来。

举个例子, 再给用户授权的时候, 用户选择某个角色同时绑定组织如财务部或者合肥分公司, 那么该用户就有了该角色下财务部或合肥分公司下的的数据权限。

以上是 RBAC 的核心设计及模型分析, 此模型也叫做 RBAC0, 而基于核心概念之上, RBAC 还提供了扩展模式。包括 RBAC1,RBAC2,RBAC3 模型。下面介绍这三种类型

1.2 RBAC1 模型

此模型引入了角色继承 (Hierarchical Role) 概念,即角色具有上下级的关系,角色间的继承关系可分为一般继承关系和受限继承关系。

一般继承关系仅要求角色继承关系是一个绝对偏序关系,允许角色间的多继承。

而受限继承关系则进一步要求角色继承关系是一个树结构,实现角色间的单继承。这种设计可以给角色分组和分层,一定程度简化了权限管理工作。

1.3 RBAC2 模型

基于核心模型的基础上,进行了角色的约束控制,RBAC2 模型中添加了责任分离关系。

其规定了权限被赋予角色时,或角色被赋予用户时,以及当用户在某一时刻激活一个角色时所应遵循的强制性规则。

责任分离包括静态责任分离和动态责任分离。主要包括以下约束:

  • 互斥角色:

同一用户只能分配到一组互斥角色集合中至多一个角色,支持责任分离的原则。

互斥角色是指各自权限互相制约的两个角色。比如财务部有会计和审核员两个角色, 他们是互斥角色, 那么用户不能同时拥有这两个角色, 体现了职责分离原则

  • 基数约束:

一个角色被分配的用户数量受限;一个用户可拥有的角色数目受限;同样一个角色对应的访问权限数目也应受限,以控制高级权限在系统中的分配

  • 先决条件角色:

即用户想获得某上级角色, 必须先获得其下一级的角色

1.4 RBAC3 模型

即最全面的权限管理, 它是基于 RBAC0,将 RBAC1 和 RBAC2 进行了整合。

1.5 用户组

当平台用户基数增大,角色类型增多时,而且有一部分人具有相同的属性,比如财务部的所有员工,如果直接给用户分配角色,管理员的工作量就会很大。

如果把相同属性的用户归类到某用户组,那么管理员直接给用户组分配角色,用户组里的每个用户即可拥有该角色,以后其他用户加入用户组后,即可自动获取用户组的所有角色,退出用户组,同时也撤销了用户组下的角色,无须管理员手动管理角色。

根据用户组是否有上下级关系, 可以分为有上下级的用户组和普通用户组:

  • 具有上下级关系的用户组:

最典型的例子就是部门和职位,可能多数人没有把部门职位和用户组关联起来吧。

当然用户组是可以拓展的,部门和职位常用于内部的管理系统,如果是面向 C 端的系统。

比如淘宝网的商家,商家自身也有一套组织架构,比如采购部,销售部,客服部,后勤部等,有些人拥有客服权限,有些人拥有上架权限等等,所以用户组是可以拓展的

  • 普通用户组:

即没有上下级关系,和组织架构,职位都没有关系,也就是说可以跨部门,跨职位。

举个例子,某电商后台管理系统,有拼团活动管理角色,我们可以设置一个拼团用户组,该组可以包括研发部的后台开发人员,运营部的运营人员,采购部的人员等等。

每个公司都会涉及到到组织和职位, 下面就重点介绍这两个。

1.5.1 组织

常见的组织架构如下图:

我们可以把组织与角色进行关联,用户加入组织后,就会自动获得该组织的全部角色,无须管理员手动授予,大大减少工作量,同时用户在调岗时,只需调整组织,角色即可批量调整。

组织的另外一个作用是控制数据权限, 把角色关联到组织, 那么该角色只能看到该组织下的数据权限。

1.5.2 职位

假设财务部的职位如下图:

每个组织部门下都会有多个职位,比如财务部有总监,会计,出纳等职位,虽然都在同一部门,但是每个职位的权限是不同的,职位高的拥有更多的权限。

总监拥有所有权限,会计和出纳拥有部分权限。特殊情况下, 一个人可能身兼多职。

1.6 含有组织 / 职位 / 用户组的模型

根据以上场景, 新的权限模型就可以设计出来了, 如下图:

根据系统的复杂度不同, 其中的多对多关系和一对一关系可能会有变化

1、在单系统且用户类型单一的情况下,用户和组织是一对一关系,组织和职位是一对多关系,用户和职位是一对一关系,组织和角色是一对一关系,职位和角色是一对一关系,用户和用户组是多对对关系,用户组和角色是一对一关系,当然这些关系也可以根据具体业务进行调整。

模型设计并不是死的, 如果小系统不需要用户组, 这块是可以去掉的。

2、分布式系统且用户类型单一的情况下,到这里权限系统就会变得很复杂,这里就要引入了一个 "系统" 概念。

3、此时系统架构是个分布式系统,权限系统独立出来,负责所有的系统的权限控制,其他业务系统比如商品中心,订单中心,用户中心,每个系统都有自己的角色和权限,那么权限系统就可以配置其他系统的角色和权限。

4、分布式系统且用户类型多个的情况下,比如淘宝网,它的用户类型包括内部用户,商家,普通用户,内部用户登录多个后台管理系统,商家登录商家中心,这些做权限控制,如果你作为架构师,该如何来设计呢? 大神可以在评论区留言交流哦!

2. 授权流程

授权即给用户授予角色, 按流程可分为手动授权和审批授权。权限中心可同时配置这两种, 可提高授权的灵活性。

  • 手动授权:

管理员登录权限中心为用户授权,根据在哪个页面授权分为两种方式:给用户添加角色,给角色添加用户。

给用户添加角色就是在用户管理页面,点击某个用户去授予角色,可以一次为用户添加多个角色;给角色添加用户就是在角色管理页面,点击某个角色,选择多个用户,实现了给批量用户授予角色的目的。

  • 审批授权:

即用户申请某个职位角色,那么用户通过 OA 流程申请该角色,然后由上级审批,该用户即可拥有该角色,不需要系统管理员手动授予。

3. 表结构

有了上述的权限模型, 设计表结构就不难了, 下面是多系统下的表结构, 简单设计下, 主要提供思路:

4. 权限框架

  • Apache Shrio
  • Spring Security

在项目中可以采用其中一种框架, 它们的优缺点以及如何使用会在后面的文章中详细介绍。

5. 结语

权限系统可以说是整个系统中最基础,同时也可以很复杂的,在实际项目中,会遇到多个系统,多个用户类型,多个使用场景,这就需要具体问题具体分析,但最核心的 RBAC 模型是不变的, 我们可以在其基础上进行扩展来满足需求。

推荐阅读

Java笔记大全.md

太赞了,这个Java网站,什么项目都有!https://markerhub.com

这个B站的UP主,讲的java真不错!

查看原文

琛能IT 收藏了文章 · 3月3日

JS 文件base64、File、Blob、ArrayBuffer互转

二进制互转

1. file对象转base64

 let reader = new FileReader();
 reader.readAsDataURL(file[0])
 console.log(reader)

2. base64 转成blob 上传

function dataURItoBlob(dataURI) {  
    var byteString = atob(dataURI.split(',')[1]);  
    var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];  
    var ab = new ArrayBuffer(byteString.length);  
    var ia = new Uint8Array(ab);  
    for (var i = 0; i < byteString.length; i++) {  
        ia[i] = byteString.charCodeAt(i);  
    }  
    return new Blob([ab], {type: mimeString});  
}

3. blob 转成ArrayBuffer

let blob = new Blob([1,2,3,4])
let reader = new FileReader();
reader.onload = function(result) {
    console.log(result);
}
reader.readAsArrayBuffer(blob);

4. buffer 转成blob

let blob = new Blob([buffer])

5. base64 转 file

const base64ConvertFile = function (urlData, filename) { // 64转file
  if (typeof urlData != 'string') {
    this.$toast("urlData不是字符串")
    return;
  }
  var arr = urlData.split(',')
  var type = arr[0].match(/:(.*?);/)[1]
  var fileExt = type.split('/')[1]
  var bstr = atob(arr[1])
  var n = bstr.length
  var u8arr = new Uint8Array(n)
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new File([u8arr], 'filename.' + fileExt, {
    type: type
  });
}
查看原文

琛能IT 收藏了文章 · 3月2日

10个HTML文件上传技巧

作者: Tapas Adhikary
译者:前端小智
来源:dev
点赞再看,养成习惯

本文 GitHubhttps://github.com/qq44924588... 上已经收录,更多往期高赞文章的分类,也整理了很多我的文档,和教程资料。欢迎Star和完善,大家面试可以参照考点复习,希望我们一起有点东西。

最近开源了一个 Vue 组件,还不够完善,欢迎大家来一起完善它,也希望大家能给个 star 支持一下,谢谢各位了。

github 地址:https://github.com/qq44924588...

简介

上传文件功能可以说是项目经常出现的需求。从在社交媒体上上传照片到在求职网站上发布简历,文件上传无处不在。在本文中,我们将讨论 HTML文件上传支持的10种用法,希望对你有用。

1. 单文件上传

我们可以将input 类型指定为file,以在Web应用程序中使用文件上传功能。

<input type="file" id="file-uploader">

input filte 提供按钮上传一个或多个文件。 默认情况下,它使用操作系统的本机文件浏览器上传单个文件。成功上传后,File API 使得可以使用简单的 JS 代码读取File对象。 要读取File对象,我们需要监听 change事件。

首先,通过id获取文件上传的实例:

const fileUploader = document.getElementById('file-uploader');

然后添加一个change 事件侦听器,以在上传完成后读取文件对象, 我们从event.target.files属性获取上传的文件信息:

fileUploader.addEventListener('change', (event) => {
  const files = event.target.files;
  console.log('files', files);
});

在控制台中观察输出结果,这里关注一下FileList数组和File对象,该对象具有有关上传文件的所有元数据信息。

clipboard.png

如果大家看到这里,有点激动,想手贱一下,可以 CodePen 玩玩,地址:https://codepen.io/atapas/pen...

2. 多文件上传

如果我们想上传多个文件,需要在标签上添加 multiple 属性:

<input type="file" id="file-uploader" multiple />

现在,我们可以上传多个文件了,以前面事例为基础,选择多个文件上传后,观察一下控制台的变化:

clipboard.png

如果大家看到这里,有点激动,想手贱一下,可以 CodePen 玩玩,地址:https://codepen.io/atapas/pen...

3.了解文件元数据

每当我们上传文件时,File对象都有元数据信息,例如file namesize,last update time,type 等等。这些信息对于进一步的验证和特殊处理很有用。

const fileUploader = document.getElementById('file-uploader');

// 听更 change 件并读取元数据
fileUploader.addEventListener('change', (event) => {
  // 获取文件列表数组
  const files = event.target.files;

  // 遍历并获取元数据
  for (const file of files) {
    const name = file.name;
    const type = file.type ? file.type: 'NA';
    const size = file.size;
    const lastModified = file.lastModified;
    console.log({ file, name, type, size, lastModified });
  }
});

下面是单个文件上传的输出结果:

clipboard.png

如果大家看到这里,有点激动,想手贱一下,可以 CodePen 玩玩,地址:https://codepen.io/atapas/pen...

4.了解 accept 属性

我们可以使用accept属性来限制要上载的文件的类型,如果只想上传的文件格式是 .jpg.png 时,可以这么做:

<input type="file" id="file-uploader" accept=".jpg, .png" multiple>

在上面的代码中,只能选择后缀是.jpg.png的文件。

如果大家看到这里,有点激动,想手贱一下,可以 CodePen 玩玩,地址:https://codepen.io/atapas/pen...

5. 管理文件内容

成功上传文件后显示文件内容,站在用户的角度上,如果上传之后,没有一个预览的,就很奇怪也不体贴。

我们可以使用FileReader对象将文件转换为二进制字符串。 然后添加load 事件侦听器,以在成功上传文件时获取二进制字符串。

// FileReader 实例
const reader = new FileReader();

fileUploader.addEventListener('change', (event) => {
  const files = event.target.files;
  const file = files[0];

  reader.readAsDataURL(file);

  reader.addEventListener('load', (event) => {
    const img = document.createElement('img');
    imageGrid.appendChild(img);
    img.src = event.target.result;
    img.alt = file.name;
  });
});

如果大家看到这里,有点激动,想手贱一下,可以 CodePen 玩玩,地址:https://codepen.io/atapas/pen...

6.验证文件大小

如果用户上传图片过大,为了不让服务器有压力,我们需要限制图片的大小,下面是允许用户上传小于 1M 的图片,如果大于 1M 将上传失败。

fileUploader.addEventListener('change', (event) => {
  // Read the file size
  const file = event.target.files[0];
  const size = file.size;

  let msg = '';

 // 检查文件大小是否大于1MB
  if (size > 1024 * 1024) {
      msg = `<span style="color:red;">The allowed file size is 1MB. The file you are trying to upload is of ${returnFileSize(size)}</span>`;
  } else {
      msg = `<span style="color:green;"> A ${returnFileSize(size)} file has been uploaded successfully. </span>`;
  }
  feedback.innerHTML = msg;
});

如果大家看到这里,有点激动,想手贱一下,可以 CodePen 玩玩,地址:https://codepen.io/atapas/pen...

7. 显示文件上传进度

更好的用户体验是让用户知道文件上传进度,前面我们用过了FileReader以及读取和加载文件的事件。

const reader = new FileReader();

FileReader还有一个progress 事件,表示当前上传进度,配合HTML5的progress标签,我们来模拟一下文件的上传进度。

reader.addEventListener('progress', (event) => {
  if (event.loaded && event.total) {
    // 计算完成百分比
    const percent = (event.loaded / event.total) * 100;
    // 将值绑定到 `progress`标签
    progress.value = percent;
  }
});

clipboard.png

如果大家看到这里,有点激动,想手贱一下,可以 CodePen 玩玩,地址:https://codepen.io/atapas/pen...

8. 怎么上传目录上传?

我们可以上传整个目录吗?嗯,这是可能的,但有一些限制。有一个叫做webkitdirectory的非标准属性(目前只有谷歌浏览器还有Microsoft Edge支持按照文件夹进行上传),它允许我们上传整个目录。

目前只有谷歌浏览器还有Microsoft Edge支持按照文件夹进行上传,具体可以看下百度云盘的网页版的上传按钮,在火狐下就支持按照文件进行上传,而在谷歌和Edge下,就会给用户提供一个下拉,让用户选择是根据文件进行上传还是根据文件夹进行上传。
<input type="file" id="file-uploader" webkitdirectory />

用户必须需要确认才能上传目录

clipboard.png

用户单击“上传”按钮后,就会进行上传。 这里要注意的重要一点。 FileList数组将以平面结构的形式包含有关上载目录中所有文件的信息。 对于每个File对象,webkitRelativePath属性表示目录路径。

例如,上传一个主目录及其下的其他文件夹和文件:

clipboard.png

现在,File 对象将将webkitRelativePath填充为:

clipboard.png

如果大家看到这里,有点激动,想手贱一下,可以 CodePen 玩玩,地址:https://codepen.io/atapas/pen...

9. 拖拽上传

不支持文件上传的拖拽就有点 low 了,不是吗?我们来看看如何通过几个简单的步骤实现这一点。

首先,创建一个拖放区域和一个可选的区域来显示上传的文件内容。

<div id="container">
  <h1>Drag & Drop an Image</h1>
  <div id="drop-zone">
    DROP HERE
  </div>

  <div id="content">
    Your image to appear here..
  </div>

</div>

通过它们各自的ID获取dropzonecontent 区域。

 const dropZone = document.getElementById('drop-zone');
 const content = document.getElementById('content');

添加一个dragover 事件处理程序,以显示将要复制的内容的效果:

dropZone.addEventListener('dragover', event => {
  event.stopPropagation();
  event.preventDefault();
  event.dataTransfer.dropEffect = 'copy';
});

clipboard.png

接下来,我们需要一个drop事件监听器来处理。

dropZone.addEventListener('drop', event => {
  // Get the files
  const files = event.dataTransfer.files;


});

如果大家看到这里,有点激动,想手贱一下,可以 CodePen 玩玩,地址:https://codepen.io/atapas/pen...

10. 使用objectURL处理文件

有一个特殊的方法叫做URL.createobjecturl(),用于从文件中创建唯一的URL。还可以使用URL.revokeObjectURL()方法来释放它。

URL.revokeObjectURL() 静态方法用来释放一个之前已经存在的、通过调用 URL.createObjectURL() 创建的 URL 对象。当你结束使用某个 URL 对象之后,应该通过调用这个方法来让浏览器知道不用在内存中继续保留对这个文件的引用了。

fileUploader.addEventListener('change', (event) => {
  const files = event.target.files;
  const file = files[0];
  
  const img = document.createElement('img');
  imageGrid.appendChild(img);
  img.src = URL.createObjectURL(file);
  img.alt = file.name;
});

如果大家看到这里,有点激动,想手贱一下,可以 CodePen 玩玩,地址:https://codepen.io/atapas/pen...

总结

无论何时,如果你还想学习本文涉及的一些知识,你可以在这里尝试。

https://html-file-upload.netl...


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:https://dev.to/atapas/10-usef...

交流

文章每周持续更新,可以微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了很多我的文档,欢迎Star和完善,大家面试可以参照考点复习,另外关注公众号,后台回复福利,即可看到福利,你懂的。

查看原文

琛能IT 收藏了文章 · 2月22日

学习Python:做数据科学还是网站开发?

译者注:本文的英文原文地址是:Python for Data Science vs Python for Web Development,发布时间是10月29日。译者一开始在Python日报上看到推荐,初步看看了,觉得对于决定学习Python的方向有一定参考价值。不过,在翻译过程中,越来越觉得这其实就是一篇搞Python数据科学培训的公司写的软文,里面写的内容还是比较浅的,只适合像我这样的初学者了解大致情况。当然,文章提到了Python作为网络开发技能的市场需求并不是很高,这点感觉并不是没有根据。作为一篇软文,它成功地激起了我学习数据科学的兴趣,而原因嘛,自然就是做数据科学工作的工资比一般开发工作,高很多(按文章中信息图的数据,比一般岗位的年薪高5万美刀!!!)!

Python编程语言拥有诸多用于网络应用开发、图形用户界面、数据分析、数据可视化等工作的框架和特性。Python可能不是网络应用开发的理想选择,但是正被很多机构广泛用于评估大型数据集(dataset)、数据可视化、进行数据分析或制作原型。在数据科学领域,Python编程语言正不断获得用户的亲睐,而作为网络开发语言,Python显得有点过时了。本篇博文,就是要对这两种截然不同的Python使用方式,进行详细的对比,并且帮助大家明白一点:如果要利用Python做数据科学工作,并没有必要了解它用于网络开发的部分。

python:web development vs data science

面向数据科学的Python

从顶级金融机构到最小的大数据创业公司,各行各业、各种规模的机构都在使用Python编程语言支撑业务运作。Python作为数据科学编程语言,不仅受顶级大数据公司欢迎,还有众多技术创业企业拥泵。它还位列2015推荐学习的前10种编程语言。

世上只有两种编程语言:一种是总是被人骂的,一种是从来没人用的。
-- <cite>Bjarne Stroustrup</cite>

Python属于前一种,而且日益被用于数学计算、机器学习和多种数据科学应用。除了性能依赖性强和底层的业务外,它能够做其他任何事情。利用Python编程语言的最好选择,就是做数据分析和统计计算。学习面向网络开发的Python,需要程序员掌握像Django这样的多种网络框架协助建设网站;但是学习面向数据科学的Python,则要求数据科学家学习如何使用正则表达式和科学计算库,并掌握数据可视化的概念。由于目的、方向不同,那些不了解Python网络开发的程序员,能很轻松地走上利用Python编程语言做数据科学工作的道路。

Python是一个有着23年历史的强大动态编程语言,语言表现力很强。程序员编码完成后,不需要编译器即可运行程序。面向网络开发的Python支持多种编程范式,包括结构化编程(structured programming)、函数式编程(functional programming)和面向对象编程(object-oriented programming, OOP)。Python代码可以很容易地嵌入到许多拥有编程接口的网络应用中。但是,Python更是开发学术研究和科学计算程序的绝佳选择,这些程序要求运行快速、数学计算精确。

而面向网络编程的Python,则要求程序员学习多种网络开发框架,这个学习难度比较大,因为现有Python网络开发框架的文档不太容易理解。当然,不容否认的是,要想利用Python开发一个动态网站或网络应用,学习网络框架是必需的。

Python网络开发框架

目前,Python社区已经有多种免费的网络应用开发框架,比如:

Django

Django是帮助完美主义者按时完成工作的Python网络开发框架(译者注:原文是Django is the python web development framework for perfectionists with deadlines。这也是Django官网上对该框架的描述)。使用Django进行网络开发,最适合的场景是开发那些依靠数据库驱动,同时也具备类似自动化后台管理界面和模板系统等炫酷功能的应用。对于不需要太多功能的网络开发项目来说,Django可能是大材小用,主要是它的文件系统容易让人搞混,而且文件目录结构要求严格。使用Django进行Python网络开发的公司有纽约时报、Instagram和Pinterest(译者注:Pinterest联合创始人Paul Sciarra在Quora上的回答提到了使用Django,Quora地址)。

Flask

Flask是针对初学者的框架,它简单,轻量,初学者很快就可以上手开发单页网络应用。这个框架并不支持验证,没有数据抽象层和其他许多框架所包括的组件。它不是一个全栈开发框架,也只用于小型网站的开发。(译者注:其实Pinterest也使用了Flask,只是没用在整站开发上,而是用来开发API,具体见链接。)

CherryPy

CherryPy框架强调要符合Python语言规范,做到程序员像进行面向对象编程一样开发网络应用。它还是诸如TurboGears和Web2py等流行全栈框架的基础模板引擎。

还有很多其他框架,包括Pyramid、Bottle和Pylons等,但是无论Python开发者使用哪一种框架,他/她都要花精力仔细地研究教程和文档。

为什么使用Python进行网络开发不现实?

Python作为网络开发语言,很可能是一个不太现实的选择:

  • 面向网络开发的Python需要非标准化、昂贵的主机服务,尤其是程序员使用流行的Python网络框架开发网站时。由于利用PHP进行网络编程如此的便捷,大部分的用户没有兴趣在Python上投入太多的精力。

  • 面向网络开发的Python与诸如PHP、Java或Ruby on Rails等语言不同,不是一个经常需要的技能。但是面向数据科学的Python却越来越受欢迎,而且由于它更多地被用于机器学习和其他数据科学程序,Python更是招聘数据科学家的公司所最看重的技能。

  • 面向网络开发的Python已经经历了较长的发展,但是它的学习曲线并没有像PHP这样的网络编程语言那么高。

为什么将Python用于数据科学是最好的选择?

Python编程是驱动大数据、金融、统计和数字运算的核心科技,而它的语法却像英语一样易懂。近来,由于拥有多个针对机器学习、自然语言处理、数据视觉化、数据探索、数据分析和数据挖掘的插件,丰富的Python数据科学生态体系得到了较大的发展,甚至有将数据科学社区Python化的趋势。今天,面向数据科学的Python已经具备了清洗、转换和处理大数据的所有工具。对于数据分析师岗位来说,掌握Python也是最受欢迎的技能。一名具备Python编程能力的数据科学家,可以在纽约挣到平均年薪14万美元的工资。

为什么数据科学家喜欢使用Python语言?

为什么数据科学家喜欢使用Python语言

数据科学家喜欢那些能够快速输出原型,帮助他们轻松地记录下自己的想法和模型的编程环境。他们喜欢通过分析巨量的数据集,得出结论,完成工作。而Python编程语言则是开发数据科学应用的多面手,因为它能帮助数据科学家,以最短最优的时间进行编码、调试、运行并获取结果,从而高效地完成工作。

一名技术娴熟的企业数据科学家的真正价值,在于利用多种数据视觉化手段,向公司的不同利益相关者有效地传递数据模式和预测。否则,数据科学工作就是一场零和游戏。Python以其优良特性,符合高强度科学计算的几乎所有方面要求,这使得它成为在不同的数据科学应用之间进行编程的绝佳选择,原因很简单:开发人员仅用一种语言就可以完成开发和分析工作。面向数据科学的Python将企业业务的不同部分连接在一起,提供了一个数据分享和处理的直接媒介。

  • Python遵循统一的设计哲学,注重可用性、可读性,对于数据科学的学习曲线也较低。

  • Python有很高的可扩展性,且与Matlab、Stata等语言相比,运行更加快速。

  • 另外,Python生态系统中还在涌现出更多的数据视觉化库,以及炫酷的应用编程结构,目的是使用图形更好地展现数据分析的结果。Python社区有着诸如Sci-Kit learn、NumPy、Pandas、Statsmodel和SciPy等许多优秀的数据分析库。这些库的数量还在不断增长。

面向数据科学的Python库

面向数据科学中数字处理与科学计算的Python编程

数据分析与Python编程语言十分契合。如果你决定要通过Python语言学习数据科学,那么你应该考虑的下一个问题,就是Python库中有哪些是可以完成大部分的数据分析工作?接下来,我们给大家介绍全球的企业数据科学家都在使用的Python数据分析库。

NumPy

Numpy是使用Python开发的高级(high level)工具的基础。这个库不能用于高级数据分析,但是深入理解Numpy中面向数组的计算,可以帮助数据科学家有效使用Pandas库。

SciPy

SciPy主要用于科学计算,拥有许多不同的模块,可用于特殊函数、图像处理、插值法(interpolation)、线性代数、常微分方程(ODE)求解器以及其他多种用途。这个库还可以与NumPy数组一起使用,实现许多高效的数学运算。

Pandas

Pandas是用于数据再加工最好的库,因为它使得处理遗失的数据、自动数据对齐(data alignment)变得更加简单,它还支持处理从不同的数据源收集而来的索引数据。

SciKit
这个流行的机器学习库拥有多种回归、分类和聚类算法,还支持gradient boosting、向量机、朴素贝叶斯模型和逻辑回归。这个库还被设计成能够与NumPy和SciPy进行交互。

Matplotlib

这是一个二维绘图库,有着交互性很强的特性,生成的图标可以放大、推移,并且能够用于发行刊物印刷出版。而且,还支持多平台的交互环境。

Matplotlib、NumPy和SciPy是科学计算的基础。还有许多其他的Python库,诸如用于网络挖掘的Pattern,用于自然语言处理的NLTK,用于深度学习的Theano,用于爬取网络的Scrappy,IPython,Statsmodels,Mlpy等。对于初学Python数据科学的人,他们需要很好地掌握上面提到的优秀数据分析库。

查看原文

琛能IT 收藏了问题 · 1月19日

如何在video上显示视频的第一帧

移动端 如何在video上显示视频的第一帧?能写下具体代码吗? 谢谢了

琛能IT 赞了文章 · 2020-12-21

漫画 | 中年架构师的20小时,全靠忍

今天这篇漫画,纯属虚构,虚构中又还有一点点真实,真实的反映大部分人(不仅仅程序员)中年的生活与无奈.

事故主人公:大刘/某伪大厂架构师/35+,东北大汉,身高180,体重180,没有其他180了,下面是中年老刘普通de的一天.

虚构中又还有一点点真实1前端画师-苏南前端漫画师-苏南image5image前端漫画师8imageimage11imageimage14image16image1819imageimage22image24image26image那曾经可爱的,手忙脚乱,一地鸡毛的中年

编后

看着原文的内容,绘画着人物的场景,内心五味杂陈,选择这篇文章来改编时,也曾思考再三,此文内容虽是虚构,但却真实的体现了当下一、二线城市打工人(更包括程序员)的生活:“我抱砖就没手抱你,我放下手中砖就无法养家”.

都说成年人的世界里,没有“容易”二字,人到中年更是如此,生活已不由你自己掌控….

张爱玲说:人到中年,一睁眼周围都是靠自己的人,浑身每一个零件都不是自己的,那是用来换房子,换父母医疗费,换儿女前程的。唯独没有自己的!

仅以此篇,献给那些在迷雾中继续坚定不已前行的中年人,路虽远,行则将至。事虽难,做则必成。我们只管向前走,也许走着走着花就开了。也许在未来的某天,回首曾经的中年,我们会感慨道:“看,那曾经可爱的,手忙脚乱,一地鸡毛的中年”.

参考资料

  • 回龙观社区网 - 人到中年一地鸡毛
  • //mp.weixin.qq.com/s/KUx0qtzgBBJLZVJEOhII-w
文中部分素材来源网络,如有侵权,请联系删除

~ 完 ~

本文系 “ 前端布道师 ” 原创

转载请先微信联系苏南(su-south)授权,并标明出处!

撰稿:苏南、四猿外

插画 / 排版:苏南

往期回顾

前端发展史的江湖恩怨情仇
揭密微信是如何诞生的?

前端布道师-苏南著作

更多精彩,欢迎关注我们

本文首发于公众号:[ 前端布道师 ]

链接:https://mp.weixin.qq.com/s/bRwhMDf784K_9AaxF1rzqQ

用漫画解读前端技术,执笔演绎程序人生,愿吾手中笔,能博君(卿)一笑

查看原文

赞 6 收藏 0 评论 5

琛能IT 收藏了文章 · 2020-11-17

Python学习路线

此视频资源收集于网络,如有侵权请联系删除!!! 此视频资源仅用作个人学习,请勿用于商业获利,造成后果自负!!!

经历长达近一个月的资源筛选过程终于结束,总共1.5T百度网盘的资源经过:去重、筛选、整理、归档之后一份粗略的Python学习曲线资源已经成型,虽然中间经历了很多坎坷,不过最终还是完成,猪哥也是第一时间与大家分享出来。

资料主要分为两部分:一、各个学习网站论坛等,二、百度云资料。网站论坛资源更新快,可以与大家互动;而百度云资源主要为视频,方便大家下载和在线观看,两种资源结合使用,学习和查找知识更方便!

一、网站论坛学习资源

  1. 名称链接说明实验楼:https://www.shiyanlou.com 提供免费的Linux实验环境
  2. Py资源中文大全:http://t.cn/Rq0C0ET 各种python包和管理工具
  3. PEP8 Py编码规范中文版:https://dwz.cn/30uIzs9W 代码规范与基础同样重要
  4. PEP20著名的Py之禅:https://dwz.cn/r2u2zV0c Py编程和设计的指导原则
  5. Google Py编程风格指南:https://dwz.cn/HDTKsCai 实践出真知
  6. Py Code Examples:https://dwz.cn/dawtruYk 精细到方法级别的代码案例
  7. Py Module of the Week:https://pymotw.com 每篇介绍一个 Py标准库的使用
  8. 菜鸟教程:http://t.cn/RLGeMuW 最经典的一个语言教程网站
  9. 廖雪峰Py教程:https://dwz.cn/drygpxjk 大牛带你简单快速入门
  10. Py自动化测试博客:https://dwz.cn/raopItkV 一个自动化测试大佬的博客
  11. Py中文学习大本营:http://www.pythondoc.com 一个不错的Flask学习网站
  12. 刘江的Django:https://dwz.cn/udQayyZY 最适合入门的Django教程
  13. 莫烦Py:https://morvanzhou.github.io 机器学习方面有着不错的造诣
  14. 吴恩达机器学习:https://dwz.cn/F8Aad3DY 网易云课堂(中/英字幕)
  15. 几个Py练习题:https://dwz.cn/ExJnmwOJ 有几个不错的实用练习题
  16. CheckiO:https://py.checkio.org 一个学习Py的有趣网站

二、学习曲线

这个学习曲线是我在某马论坛上看到的,觉得不错就推荐给大家,同时也感谢某马的开源免费精神,猪哥也是受益颇多!

image

三、优质资源

我把这些资源分为了七个不同的阶段,难度是依次递增,其实就是对应上面学习曲线图;

本资源一共800G,永久保存,并且会持续更新,建议大家转发收藏,方便以后查找学习;

这是猪哥推荐的Python学习曲线,基本上是某马的视频教程,这个学习曲线适合零基础的同学:

四、获取方式

  1. 链接:https://pan.baidu.com/s/1SzSW7sexDUNZgsvxi7eF-Q 密码:pig6
  2. 如果链接失效,可在下方留言或者私信猪哥哦,猪哥会及时更新!

五、猪哥有话说

如果你觉得此文章好请分享给你的每一位朋友:也许你一个不经意的善意之举,却是他人人生的一个转折点。

查看原文

琛能IT 收藏了文章 · 2020-10-23

flask route设计思路

引言

本文主要梳理了flask源码中route的设计思路。
首先,从WSGI协议的角度介绍flask route的作用;
其次,详细讲解如何借助werkzeug库的MapRule实现route
最后,梳理了一次完整的http请求中route的完整流程。

flask route 设计思路

源码版本说明

本文参考的是flask 0.5版本的代码。
flask 0.1版本的代码非常短,只有600多行,但是这个版本缺少blueprint机制。
因此,我参考的是0.5版本。

flask route示例

直接使用flask官方文档中的例子

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello World!'
    
@app.route('/post/<int:post_id>')
def show_post(post_id):
    # show the post with the given id, the id is an integer
    return 'Post %d' % post_id

if __name__ == '__main__':
    app.run()

此例中,使用app.route装饰器,完成了以下两个url与处理函数的route:

{ 
    '/': hello_world, 
    '/post/<int:post_id>' : show_post
}

这样做的效果为:
当http请求的url为'/'时,flask会调用hello_world函数;
当http请求的url为'/post/<某整数值>'(例如/post/32)时,flask会调用show_post函数;

flask route的作用

从上面的示例中其实可以明白:flask route的作用就是建立url与处理函数的映射

WSGI协议将处理请求的组件按照功能及调用关系分成了三种:server, middleware, application
其中,server可以调用middleware和application,middleware可以调用application。

符合WSGI的框架对于一次http请求的完整处理过程为:
server读取解析请求,生成environ和start_response,然后调用middleware;
middleware完成自己的处理部分后,可以继续调用下一个middleware或application,形成一个完整的请求链;
application位于请求链的最后一级,其作用就是生成最终的响应。

 http服务器(比如,nginx)--> WSGI server(比如gunicorn,SimpleHttpServer)-->middleware-->
 middleware--> ... -->application

如果接触过Java Web 开发的人可能会立刻发现,这与servlet中的middleware机制是完全一致的。

特别重要的:

在上一小节的示例中app = Flask(__name__)创建了一个middleware
而这个middleware的核心作用是进行请求转发(request dispatch)。

上面这句话非常重要,请在心里重复一百遍。
上面这句话非常重要,请在心里重复一百遍。
上面这句话非常重要,请在心里重复一百遍。

进行请求转发的前提就是能够建立url与处理函数之间的映射关系,即route功能。
因此,在flask中,route是Flask类的一个装饰器。

flask route的实现思路

通过上一小节,我们知道以下两点:

  1. flask route 是url与处理函数的映射关系;

  2. 在http请求时,Flask这个middleware负责完成对url对应的处理函数的调用;

那么,如果是我们自己来实现route,思路也很简单:

  1. 建立一个类Flask,这个类是一个middleware,并且有一个字典型的成员变量url_map

  2. url_map = {url : function}

  3. 当http请求时,进行request dispatch:根据url,从url_map中找到function,然后调用function;

  4. 调用后续的middleware或application,并把function的结果传递下去。

flask的实现思路也是这样的。

class Flask(object):

    def __init__(self):
        self.url_map = {}  # 此处定义保存url与处理函数的映射关系
        
    def __call__(self, environ, start_response):  # 根据WSGI协议,middleware必须是可调用对象
        self.dispatch_request()    # Flask的核心功能 request dispatch
        return application(environ, start_response)  #最后调用下一级的application
    
    def route(self, rule):  # Flask使用装饰器来完成url与处理函数的映射关系建立
        def decorator(f):   # 简单,侵入小,优雅
            self.url_map[rule] = f
            return f
        return decorator
    
    def dispath_request(self):
        url = get_url_from_environ() #解析environ获得url 
        return self.url_map[url]() #从url_map中找到对应的处理函数,并调用

至此, 一个简单的Flaskmiddleware的骨架就完成了。
上面的Flask类主要功能包括:

  1. 符合WSGI协议的middleware:可被调用,并且可以调用application

  2. 能够保存url与处理函数的映射信息

  3. 能够根据url找到处理函数并调用(即,request dispatch)

当然,在实际中,不可能这么简单,但是基本思路是一致的。

werkzeug库中的Map与Rule在Flask中的应用

需要指出,上面实现的最简单的Flask类还是有很多问题的。
比如,HTTP请求中相同的url,不同的请求方法,比如GET,POST如果对应不同的处理函数,该如何处理?

flask使用了werkzeug库中的MapRule来管理url与处理函数映射关系。

首先需要简单了解一下MapRule的作用:
werkzeug中,Rule的主要作用是保存了一组urlendpointmethods关系:
每个(url, endpoint, methods)都有一个对应的Rule对象:
其实现如下:

class Rule(object):
    def __init__(self, url, endpoint, methods):
        self.rule = url
        self.endpoint = endpoint
        self.methods = methods

这里需要解释一下endpoint
前面说过:url与其处理函数可以使用一个字典来实现:{url: function}

flask在实现的时候,在中间加了一个中介endpoint,于是,url与处理函数的映射变成了这样:

url-->endpoint-->function #一个url对应一个endpoint,一个endpoint对应一个function
{url: endpoint} # 保存url与endpoint之间的关系
{endpoint: function} #保存endpoint与function之间的关系

于是,刚才我们实现的简单的flask骨架中{url: function}的字典,就变成了{endpoint: function}
{url: endpoint}这个映射关系就需要借助MapRule这两个类来完成。

可以发现:endpoint就是url和处理函数映射关系中的一个中介,所以,它可以是任何可以用作字典键的值,比如字符串。
但是在实际使用中endpoint,一般endpoint均为字符串,并且默认情况下:

  1. 如果是通过Flask.route装饰器建立的映射关系,那么endpoint就是处理函数的函数名;

  2. 如果是通过blueprint建立的映射关系,那么endpoint是blueprint名.处理函数名;

因为,每建立一个url-->endpoint-->function关系就会创建一个Rule对象,所以,会有很多Rule对象存在。
Map的作用则是保存所有Rule对象。
所以,一般情况下Map的用法如下:


    m = Map([
            Rule('/', endpoint='index'),
            Rule('/downloads/', endpoint='downloads/index'),
            Rule('/downloads/<int:id>', endpoint='downloads/show')
           ])

在flask的源码中

class Flask(object):
    def __init__(self):
        self.url_map = Map()  # url_map为保存所有Rule关系的容器Map
        self.view_functions = {} # view_functions保存endpoint-->function
  1. 成员变量url_map保存所有的(url, endpoint, method)关系

  2. 成员变量view_functions保存所有的{endpoint, function}关系

所以,对于一个url,只要能找到(url,endpoint,method),就能根据endpoint找到对应的function

route的完整流程

首先,建立Flask对象:

app = Flask(__name__)

然后,建立urlfunction之间的映射关系:

@app.route('/')
def hello_world():
    return 'Hello World!'

在装饰器route中,创建(url, endpoint, method){endpoint: function}两组映射关系:

if endpoint is None:
    endpoint = view_func.__name__ # 默认使用响应函数名作为endpoint
self.url_map.add(Rule(url, endpoint, method)) # 保存(url, endpoint, method)映射关系
self.view_functions[endpoint] = view_func  # 保存{endpoint: function}映射关系

这样,就完成了对url和响应函数的映射关系。

下一步,调用WSGI server响应http请求,在文章开始的示例中使用:

app.run()

调用python标准库提供的WSGI server,在实际使用时,可能是gunicornuwsgi

不论server是什么,最终都会调用Flask.__call__函数。这个函数完成request dispatch的任务。

对于request dispatch而言,首先根据请求,解析environ,得到url,
然后调用Map.match函数,这个函数会最终找到预先保存的(url, endpoint, method)映射,
然后返回(endpoint, url请求参数),
由于得到了endpoint,然后,可以从Flask.view_functions中直接取到对应的响应函数,
所以,可以直接进行函数调用

self.view_functions[endpoint](url请求参数)

至此,就完成了完整的route

总结

  1. flaskFlask类是WSGIdispatch middleware

  2. Flaskurl_map保存所有的(url, endpoint, method)映射关系;

  3. Flaskview_functions保存所有的{endpoint: function}映射关系;

  4. dispath request就是根据url找到endpoint,再根据endpoint找到function,最后调用function的过程

查看原文

琛能IT 赞了文章 · 2020-10-23

flask route设计思路

引言

本文主要梳理了flask源码中route的设计思路。
首先,从WSGI协议的角度介绍flask route的作用;
其次,详细讲解如何借助werkzeug库的MapRule实现route
最后,梳理了一次完整的http请求中route的完整流程。

flask route 设计思路

源码版本说明

本文参考的是flask 0.5版本的代码。
flask 0.1版本的代码非常短,只有600多行,但是这个版本缺少blueprint机制。
因此,我参考的是0.5版本。

flask route示例

直接使用flask官方文档中的例子

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello World!'
    
@app.route('/post/<int:post_id>')
def show_post(post_id):
    # show the post with the given id, the id is an integer
    return 'Post %d' % post_id

if __name__ == '__main__':
    app.run()

此例中,使用app.route装饰器,完成了以下两个url与处理函数的route:

{ 
    '/': hello_world, 
    '/post/<int:post_id>' : show_post
}

这样做的效果为:
当http请求的url为'/'时,flask会调用hello_world函数;
当http请求的url为'/post/<某整数值>'(例如/post/32)时,flask会调用show_post函数;

flask route的作用

从上面的示例中其实可以明白:flask route的作用就是建立url与处理函数的映射

WSGI协议将处理请求的组件按照功能及调用关系分成了三种:server, middleware, application
其中,server可以调用middleware和application,middleware可以调用application。

符合WSGI的框架对于一次http请求的完整处理过程为:
server读取解析请求,生成environ和start_response,然后调用middleware;
middleware完成自己的处理部分后,可以继续调用下一个middleware或application,形成一个完整的请求链;
application位于请求链的最后一级,其作用就是生成最终的响应。

 http服务器(比如,nginx)--> WSGI server(比如gunicorn,SimpleHttpServer)-->middleware-->
 middleware--> ... -->application

如果接触过Java Web 开发的人可能会立刻发现,这与servlet中的middleware机制是完全一致的。

特别重要的:

在上一小节的示例中app = Flask(__name__)创建了一个middleware
而这个middleware的核心作用是进行请求转发(request dispatch)。

上面这句话非常重要,请在心里重复一百遍。
上面这句话非常重要,请在心里重复一百遍。
上面这句话非常重要,请在心里重复一百遍。

进行请求转发的前提就是能够建立url与处理函数之间的映射关系,即route功能。
因此,在flask中,route是Flask类的一个装饰器。

flask route的实现思路

通过上一小节,我们知道以下两点:

  1. flask route 是url与处理函数的映射关系;

  2. 在http请求时,Flask这个middleware负责完成对url对应的处理函数的调用;

那么,如果是我们自己来实现route,思路也很简单:

  1. 建立一个类Flask,这个类是一个middleware,并且有一个字典型的成员变量url_map

  2. url_map = {url : function}

  3. 当http请求时,进行request dispatch:根据url,从url_map中找到function,然后调用function;

  4. 调用后续的middleware或application,并把function的结果传递下去。

flask的实现思路也是这样的。

class Flask(object):

    def __init__(self):
        self.url_map = {}  # 此处定义保存url与处理函数的映射关系
        
    def __call__(self, environ, start_response):  # 根据WSGI协议,middleware必须是可调用对象
        self.dispatch_request()    # Flask的核心功能 request dispatch
        return application(environ, start_response)  #最后调用下一级的application
    
    def route(self, rule):  # Flask使用装饰器来完成url与处理函数的映射关系建立
        def decorator(f):   # 简单,侵入小,优雅
            self.url_map[rule] = f
            return f
        return decorator
    
    def dispath_request(self):
        url = get_url_from_environ() #解析environ获得url 
        return self.url_map[url]() #从url_map中找到对应的处理函数,并调用

至此, 一个简单的Flaskmiddleware的骨架就完成了。
上面的Flask类主要功能包括:

  1. 符合WSGI协议的middleware:可被调用,并且可以调用application

  2. 能够保存url与处理函数的映射信息

  3. 能够根据url找到处理函数并调用(即,request dispatch)

当然,在实际中,不可能这么简单,但是基本思路是一致的。

werkzeug库中的Map与Rule在Flask中的应用

需要指出,上面实现的最简单的Flask类还是有很多问题的。
比如,HTTP请求中相同的url,不同的请求方法,比如GET,POST如果对应不同的处理函数,该如何处理?

flask使用了werkzeug库中的MapRule来管理url与处理函数映射关系。

首先需要简单了解一下MapRule的作用:
werkzeug中,Rule的主要作用是保存了一组urlendpointmethods关系:
每个(url, endpoint, methods)都有一个对应的Rule对象:
其实现如下:

class Rule(object):
    def __init__(self, url, endpoint, methods):
        self.rule = url
        self.endpoint = endpoint
        self.methods = methods

这里需要解释一下endpoint
前面说过:url与其处理函数可以使用一个字典来实现:{url: function}

flask在实现的时候,在中间加了一个中介endpoint,于是,url与处理函数的映射变成了这样:

url-->endpoint-->function #一个url对应一个endpoint,一个endpoint对应一个function
{url: endpoint} # 保存url与endpoint之间的关系
{endpoint: function} #保存endpoint与function之间的关系

于是,刚才我们实现的简单的flask骨架中{url: function}的字典,就变成了{endpoint: function}
{url: endpoint}这个映射关系就需要借助MapRule这两个类来完成。

可以发现:endpoint就是url和处理函数映射关系中的一个中介,所以,它可以是任何可以用作字典键的值,比如字符串。
但是在实际使用中endpoint,一般endpoint均为字符串,并且默认情况下:

  1. 如果是通过Flask.route装饰器建立的映射关系,那么endpoint就是处理函数的函数名;

  2. 如果是通过blueprint建立的映射关系,那么endpoint是blueprint名.处理函数名;

因为,每建立一个url-->endpoint-->function关系就会创建一个Rule对象,所以,会有很多Rule对象存在。
Map的作用则是保存所有Rule对象。
所以,一般情况下Map的用法如下:


    m = Map([
            Rule('/', endpoint='index'),
            Rule('/downloads/', endpoint='downloads/index'),
            Rule('/downloads/<int:id>', endpoint='downloads/show')
           ])

在flask的源码中

class Flask(object):
    def __init__(self):
        self.url_map = Map()  # url_map为保存所有Rule关系的容器Map
        self.view_functions = {} # view_functions保存endpoint-->function
  1. 成员变量url_map保存所有的(url, endpoint, method)关系

  2. 成员变量view_functions保存所有的{endpoint, function}关系

所以,对于一个url,只要能找到(url,endpoint,method),就能根据endpoint找到对应的function

route的完整流程

首先,建立Flask对象:

app = Flask(__name__)

然后,建立urlfunction之间的映射关系:

@app.route('/')
def hello_world():
    return 'Hello World!'

在装饰器route中,创建(url, endpoint, method){endpoint: function}两组映射关系:

if endpoint is None:
    endpoint = view_func.__name__ # 默认使用响应函数名作为endpoint
self.url_map.add(Rule(url, endpoint, method)) # 保存(url, endpoint, method)映射关系
self.view_functions[endpoint] = view_func  # 保存{endpoint: function}映射关系

这样,就完成了对url和响应函数的映射关系。

下一步,调用WSGI server响应http请求,在文章开始的示例中使用:

app.run()

调用python标准库提供的WSGI server,在实际使用时,可能是gunicornuwsgi

不论server是什么,最终都会调用Flask.__call__函数。这个函数完成request dispatch的任务。

对于request dispatch而言,首先根据请求,解析environ,得到url,
然后调用Map.match函数,这个函数会最终找到预先保存的(url, endpoint, method)映射,
然后返回(endpoint, url请求参数),
由于得到了endpoint,然后,可以从Flask.view_functions中直接取到对应的响应函数,
所以,可以直接进行函数调用

self.view_functions[endpoint](url请求参数)

至此,就完成了完整的route

总结

  1. flaskFlask类是WSGIdispatch middleware

  2. Flaskurl_map保存所有的(url, endpoint, method)映射关系;

  3. Flaskview_functions保存所有的{endpoint: function}映射关系;

  4. dispath request就是根据url找到endpoint,再根据endpoint找到function,最后调用function的过程

查看原文

赞 20 收藏 56 评论 1

认证与成就

  • 获得 3 次点赞
  • 获得 8 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 8 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-12-20
个人主页被 776 人浏览