可视化中你最常用的软件包有哪些?
简介知乎看到这样一个问题,不请自来回答一下,也算对这段时间可视化推文的一个总结吧。看到很多答主都给出了全面的回答,包括数据处理,统计建模等方面。而在这篇推文中,我将对自己较为擅长的领域(R语言可视化)进行详细的介绍。全文干货十足,给出的拓展链接也十分丰富。可以先收藏,以后慢慢研究。ggplot2包ggplot2包是Harley Wickham在2005年创建的,是包含了一套全面而连贯的语法的绘图系统。弥补了R中创建图形缺乏一致性的缺点,且不会局限于一些已经定义好的统计图形,可以根据需要创造出任何有助于解决所遇到问题的图形。核心理念:将绘图与数据分离,数据相关的绘图与数据无关的绘图分离,按图层作图。也正是因为这个包,直接将R可视化推向了顶端,可以说是所有编程软件绘图最出色的包了吧。哦对了,我把ggplot2基础进行整理,写了一个文稿,推送可见:R分享|自制112页可视化课件。如果对R语言可视化感兴趣,并且想从基础学习的话,可以配着我上的课进行学习(b站链接[1])。好几位师弟师妹回复说,听了受益匪浅噢!😁ggplot2拓展包除此之外,正是因为大佬的ggplot包,延伸出了很多基于ggplot的拓展包,官网一共汇总了ggplot82种拓展包[2]。82种拓展的ggplot包我整理的11个扩展包大家可能会说这么多,我怎么学的完啊!不要怕,我已经把最实用,最热门的包进行整理了。可视化是小编个人业余爱好,纯属感兴趣,于是自愿做了一名大自然搬运工(翻译工),并加上自己的理解,整理了以下11个拓展包。可以直接点击进入,所有源代码和Rmd文档可在我的github[3]中获得。这里就不对这些包做过多介绍了,但各个精品,值得一学!ggvis包---数据可视化交互ggridges包---峰峦图详细介绍esquisse包---不写代码生成ggplot图calendR包---私人定制专属日历corrplot包:相关性矩阵可视化cowplot包:用R添加水印flexdashboard包:用于R的简单交互式仪表盘gghalves包-你五毛我五毛用ggpubr包制图reticulate包--数据科学者的福音igraph包--绘制网络图其他推荐的可视化拓展包(正打算整理整理)ggthemes[4]ggplot的几何图形,尺度和主题的变换。是提升图像美观的拓展包,非常推荐。下面给出一个例子:library("ggplot2")
library("ggthemes")
p2 <- ggplot(mtcars, aes(x = wt, y = mpg, colour = factor(gear))) +
geom_point() +
ggtitle("Cars")
p2 + theme_solarized() +
scale_colour_solarized("blue")当然,还有其他不同选择,官网介绍可见:Introduction to ggthemes[5],或者可以等我出教程啦!欢迎关注我的b站,公众号以及知乎,最新的R学习资料都在这呢。gganimate[6]gganimate扩展了由ggplot2实现的图形语法,以包括动画的描述。为此,它提供了一系列新的语法类,可以将它们添加到plot对象中,以自定义其随时间变化的方式。这里给出一些例子:library(ggplot2)
library(gapminder)
ggplot(gapminder, aes(gdpPercap, lifeExp, size = pop, colour = country)) +
geom_point(alpha = 0.7, show.legend = FALSE) +
scale_colour_manual(values = country_colors) +
scale_size(range = c(2, 12)) +
scale_x_log10() +
facet_wrap(~continent) +
# Here comes the gganimate specific bits
labs(title = 'Year: {frame_time}', x = 'GDP per capita', y = 'life expectancy') +
transition_time(year) +
ease_aes('linear')ggrepel[7]grepel为ggplot2提供了几何图形来解决文本标签重叠的问题。library(ggrepel)
ggplot(mtcars, aes(wt, mpg, label = rownames(mtcars))) +
geom_text_repel() +
geom_point(color = 'red') +
theme_classic(base_size = 16)cowplot[8] ,gridExtra[9],patchwork[10]关于ggplot图片组合,排版可以使用上面三个包的任意一个进行实现。我也给出了非常详细介绍。可见这几篇推文:patchwork包;R可视乎|合并多幅图形;R可视乎|混合多个图形5.和esquisse包类似的包:ggthemeassist[11]和ggedit[12]可以通过点击按钮实现ggplot的绘图,并获得相应代码和轻松导出图形。
云安全管理中的DevOps职责
通常,安全属于信息安全团队的工作范畴。这样的网络安全实现方式曾一度运作良好,直至最近几年才发生变化。向云计算基础设施的转变,造就了分散化的软件开发环境,进而推动了软件开发在速度和规模上的增长。DevOps 为软件开发全生命周期提供了更丰富的服务,有助于开发团队更快速地实现敏捷的软件创建、测试和实施。但 DevOps 同时引入了新的网络安全漏洞,这是传统的信息安全孤岛所无法管理的。为保护 DevOps 环境,以敏感信息(Secret)管理为主责的 DevSecOps 部门应运而生。网络安全是必须的毫无疑问,云安全已成为 DevOps 团队不可分割的职责。团队必须在云安全管理(Cloud Security Management,CSM)上积极作为。云安全:敏感信息及防泄露方法现代开发人员在创建软件之外,还必须保护组织的敏感信息免受未经授权或身份验证的访问,并将其落实在开发过程中。那什么是“敏感信息”?敏感信息指支持访问权限的数字凭证,其中访问包括用户访问应用,以及应用间的相互访问。对于后一种访问,“敏感信息”包括密码、加密密钥、证书和 API 密钥等。为避免代码出现泄露敏感信息而导致数据泄露,DevOps 团队必须首先了解在 DevOps 环境中存在的多种敏感信息扩散方式。导致敏感信息滚雪球般扩散的因素,可概况为如下七种方式:基于云原生的开发、多云基础设施、微服务架构、从用户到机器的身份转变、主动学习/机器学习/数据分析、物联网/嵌入式设备,当然还包括 DevOps 本身。这些致因引发了更多的出错机会,因此会产生漏洞。其中包括:为加速测试而对敏感信息做了硬编码、使用了不安全的开源库,以及未考虑云上安全和云本身安全等。当前存在着多种商业的和开源的敏感信息管理技术,用户需要考虑的因素包括:所在组织的预算和要求、当前部署的技术、DevOps 团队在敏感信息管理上的经验,以及目前实施这些技术并维持最新的可能性。支持云架构安全,DevOps 团队的 8 项做法识别必要需求:前期应做好预警的准备,越早越好。大部分公司已至少部分上云,有时很难后退一步看到整体局面。谨记,云服务并不仅仅是 AWS、Azure 和 Google 这些厂商,而是技术栈涵盖了从记账到 Zoom 的所有 SaaS 应用。需特别提出一点,团队是否正在使用 Slack 等基于 SaaS 的文档管理系统(DM)?如果开发团队或 DevOps 团队正使用自己的 Slack Channel 分享企业的敏感信息,那么攻击一旦通过暴力获取了 Slack 的访问权限,就能置整个组织于危险境地。CI/CD 访问的界定和管理,需明确落实为管制措施。鉴于数据泄露主要由人为错误引发,表现为配置不正确、敏感信息泄露和数字卫生堪忧等问题,因此鉴权的责任需由 DevOps 和 DevSecOps 团队承担,这是每个联网的安全系统的基本要求。有别于开发团队,DBA 团队需要完全不同的数据访问权限。为降低风险,应在正确配置的技术栈上遵循“最小访问权限策略”(least access privilege policy)。事实上,任何一种“某某即服务”,无论是 IaaS、PaaS 或是其它,都需要做到在凭证这一最基本的级别上的保护。Solarwinds数据泄露案例就始于凭证的泄露。Google 会定期向其商业用户发送警报,告知用户潜在的凭据泄露。无论如何,都不要遗漏"影子 IT"(ShadowIT)。确保组织中每位人员都知道,自身凭据的泄露可能是皇冠明珠中最薄弱的环节。影子 IT 增大了凭证泄露的风险,因为 IT 团队并未意识到一些外部平台能突然连接到受控的内部系统。最后一点,举一反三,博采众长。推荐英特尔提供的速查安全清单,可作为确定云安全要求的一个基础。定义架构:一旦识别了组织的云安全要求,就会更加全面地掌握组织在用的云服务类型,以及需添加的其它服务。始终将云上安全和云自身安全置于首位。谨记,用户需对自己应用、数据、操作系统、用户访问和虚拟网络流量的安全负责。此外,还需提升用户对配置的基础认知。有超过 5%的 AWS S3 Bucket 被错误地配置为公开可读。近期Kafdrop的一个低级错误配置,就泄露了一些世界上最大型企业的 Apache Kafka 技术栈。尽管三大云厂商在自身技术栈安全加固上的投资数以百万计,但 PaaS 公司并没有此类预算。所以重要的事情说三遍,检查、检查,仔细检查。称其为“零信任”是有原因的。对于 SaaS 和 Web 安全,凭证保护同样关键。每种类型的架构,都需要有各自的安全类型,在这点上不能偷懒。例如,混合云基础架构需要达到一种“三重击”类型的安全。一是本地部署需实现高度安全,包括关闭所有的端口、追踪表面区域,以及具备一个高度活跃的安全运营中心 (SOC);二是在公有云的安全防护上,需使用其技术栈中可用的最新和最强大的安全技术;三是二者间的连接也需加以保护,以免受攻击。分析现有管控措施,找出存在的漏洞:零敲碎打的安全技术栈,效果是不会好的。显然,更彻底、更合理的做法是从零开始构建。下面列出了一些可行的技术措施:云访问安全代理 (Cloud access security broker,CASB)云工作负载保护平台 (Cloud workload protection platforms,CWPP)云安全态势管理 (Cloud security posture management,CSPM)静态应用安全测试 (Static application security testing,SAST)数据丢失防护 (Data loss prevention,DLP)CASB 担当了组织和云厂商间的中介,提供配置审计、DLP、治理和监控等服务。主要的 CASB 产品厂商包括 Broadcom、Palo Alto 和 Forcepoint 等企业。CWPP 用于防止系统过载,比如 DDoS 和潜在导致内存溢出的错误代码。CWPP 监控云基础设施上部署的云计算资源。CheckPoint、Trend Micro 和 Carbon Black 等企业都提供 CWPP 产品。CSPM 通过持续审计方式检测错误的配置,帮助发现人为错误,这是导致出现安全漏洞的主要原因之一。CSPM 厂商包括 Spectral 等。SAST扫描源代码中的潜在漏洞,防止由于人为错误和遗漏而导致敏感信息泄露。例如,测试后遗忘了数据库访问凭证的硬编码。DLP 可以是 CASB 产品的组成部分,也可以是单独的产品。DLP 提供保护敏感数据的工具和策略,降低或消除由不良行为者或内部资源导致的数据泄露风险。上述工具可以单独使用,也可以作为更广泛的安全技术栈的组成部分;可用于整个组织,也可用于特定的领域内,例如在开发中使用 SAST。聚焦于云上敏感信息保护:在理想情况下,只要相关教育、以安全为中心的文化和各项工具到位,敏感信息是永远不会泄露的。但人为错误是难免会出现的。老话常说“更快、更好、更便宜”。但在新说法中,会再添加上一条,“更安全”。当然,最初的收益是从“更快”和“更便宜”中获得的,但忽视“更安全”的后果是对会业务产生更持久的影响。开发人员面对着发布代码的巨大压力。为简化跨工具的访问,他们有时会走捷径,试图使用更易于记忆的密码,或是使用易于猜出模式的轮转密码。鉴于此,我们应聚焦于凭证保护。密钥和密码应尽可能在无需人工交互的情况下自动轮转,也必须强大到足以承受蛮力攻击。不要忘记培训人员了解一些“典型”威胁,例如钓鱼式攻击、短信息钓鱼(smishing)、链接投毒等。然而,无论团队如何审慎而为,人总是会犯错误的。搜索错误配置:如前所述,在更快、更高效、更好的过程中,开发人员一直专注于如何将代码发布出来。一种加速做法就是在配置中硬编码数据库访问等敏感信息。偶尔还会为了省事,在 QA 和测试中直接将“读取访问权限”设置为“公共”。问题在于,开发人员面对太多需要关注的事情,以至于有时会忘记删除这些访问权限,从而导致整个系统易受攻击。自动配置扫描是发现此类错误的关键,因为没有人会真正有时间特地去逐行地检查配置代码。聚焦于最少访问原则:理想情况下,每个人都是百分百适岗和诚实的,从不会犯错误,或是故意做错事。现实中,为实现更好的访问控制,需执行将访问权限严控于必需人员的“最少访问权限”原则,由此降低发生错误的风险、限制损害的范围,并加强安全性。例如,在Sage公司数据泄露案例中,即便某位会计岗员工图谋不轨,该策略也能大大地降低企业的损失。当然,最小访问权限需要辅以持续的监控,它并非一种完备的解决方案,但可通过以下方式得到强化:去除终端用户计算机上的管理员权限;更好地保护帐户凭据;监控特权会话,确保正确的使用;除非开发人员有特定的要求,否则应限制访问权限;限制对生产系统的访问。这一原则同样具有技术解决方案。权限访问管理技术提供监控、审计和强制合规性。好的权限访问解决方案,可支持权限的动态分配和拒绝,确保基于实际需要访问。对 CI/CD 管道的全面持续安全防护:关键在于“测试关口前移”(Shifting Left)。安全性应始于首行代码,而不是遗留到 QA 或测试阶段。主动安全涵盖了从防止不合规的和错误的配置,到限制敏感信息泄露和凭证漏洞,从而降低了整个软件生命周期出现问题的可能性。在开发中的每一步,都需要主动安全和被动安全齐头并进。在加快响应速度的同时,保持一切过程的敏捷性。安全性是每位开发人员都需要考虑的问题,并检查新旧代码是否存在潜在的漏洞。简而言之,从一开始就要在新代码编写中落实安全,在旧代码审查中寻找问题。一切从简:为确保整个软件生命周期的安全,实现自动化是最快捷、最简单的方式。重要的解决方案包括配置检查、敏感信息扫描、身份访问管理、治理、合规性、掩码和人工数据等。解决问题的关键在于找出一种组合,能最大限度地提高云安全性、减少误报,并快速且低代价地发布高质量的代码。最好的解决方案,应是最简单的。即使用尽可能少的工具构建安全技术栈,却能提供最高等级的安全性和最低等级的误报。不幸的是,事情总是相当复杂的。虽然一些企业提供了多合一工具或兼容套件,用于简化流程。但作为用户,并不能完全依赖于此。和所有的大型项目一样,最好的做法是择机去迈出这一步。永远不要忽视云上安全与云本身安全,云厂商很少会去分担用户的责任。对照企业自身的 SLA,去发现云厂商提供服务中所有遗留下的坑,并逐项加以弥补。作者简介:Dotan Nahum 是Spectral公司的 CEO 和联合创始人。作为一名技术大拿和代码忍者,Nahum 具有数十年的动手编码经验,力图通过深思熟虑的设计实现快速构建,融合性能、正确性和开发人员经验。作为一名重要的开源贡献者、作家、演讲者和播客,Nahum 在 React、Node.js、Go、React Native、分布式系统和基础设施(包括 Hadoop、Spark、Docker、AWS 等)方面体现出专业高水平。可访问他的Github和博客。原文链接:The Role of DevOps in Cloud Security Management
《MySQL》系列 - select 语句是怎么执行的?
mysql 作为一个关系型数据库,在国内使用应该是最广泛的。也许你司使用 Oracle、Pg 等等,但是大多数互联网公司,比如我司使用得最多的还是 Mysql,重要性不言而喻。事情是这样的,某天我司小胖问我执行 select * from table,数据库底层到底发生了啥?从而我们得到数据呢?以下把我给问住了,为此我查阅了大量的书籍、博客。于是就有了这篇文章。假设现在我有张 user 表,只有两列,一列 id 自增的,一列 name 是 varchar 类型。建表语句是这样的:CREATE TABLE IF NOT EXISTS `user`(
`id` INT UNSIGNED AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
PRIMARY KEY ( `id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;小胖的问题就是下面这个语句的执行过程。select * from user where id = 1;01 mysql 架构概览要想理解这个问题就必须要知道 mysql 的内部架构。为此,我画了张 mysql 的架构图(你也可以理解为 sql 查询语句的执行过程),如下所示:首先 msql 分为 server 层和存储引擎层两个部分。server 层包括四个功能模块,分别是:连接器、查询缓存、优化器、执行器。这一层负责了 mysql 的所有核心工作,比如:内置函数、存储过程、触发器以及视图等。而存储引擎层则是负责数据的存取。注意,存储引擎在 mysql 是可选的,常见的还有: InnoDB、MyISAM 以及 Memory 等,最常用的就是 InnoDB。现在默认的存储引擎也是它(从 mysql 5.5.5 版本开始),大家可以看到我上面的建表语句就是指定了 InnoDB 引擎。当然,你不指定的话默认也是它。由于存储引擎是可选的,所以 mysql 中,所有的存储引擎其实是共用一个 server 层的。回到正题,我们就以这张图的流程来解决一下小胖的问题。1.1 连接器首先,数据库要执行 sql,肯定要先连接数据库吧。这部分工作就是由连接器完成。它负责校验账户密码、获取权限、管理连接数,最终与客户端建立连接等工作。mysql 链接数据库是这样写的:mysql -h 127.0.0.1 -P 3306 -u root -p
# 127.0.0.1 : ip 3306 : 端口 root : 用户名运行命令之后需要输入密码,当然也可以跟在 -p 后面。不过不建议这么做,会有密码泄露的风险。输入命令后,连接器根据你的账户名密码验证身份。这是会出现两种情况:账号或密码不对,服务端会返回一个 "ERROR 1045 (28000): Access denied for user 'root'@'127.0.0.1' (using password: YES)" 的错误,退出连接。验证通过,连接器就会到权限表查出你的权限。之后你有啥权限都要通过这时读到的权限进行判断。注意,我说的是此时查到的权限。就算你用管理员账号修改了当前用户的权限,此时已连接上的当前用户不受影响,必须要重启 mysql 新的权限才会生效。1.1.1 查看连接状态连接完成,如果后续没有做任何事情,这个连接就处于空闲状态。你可以用 show processlist; 命令查看 mysql 的连接信息,如下图,我的数据库连接都是 Sleep 状态的,除了执行 show processlist 操作的连接。1.1.2 控制连接如果客户端太长时间没有操作,此连接将会自动断开。这个时间默认是 8 小时,由参数 wait_timeout 控制。如果断开以后继续操作就会收到 "Lost connection to MySQL server during query" 的错误。这时就必须重连才能执行请求。数据库里面有长短连接之分,长连接:连接成功后不断有请求,就会一直使用同一连接。短连接:每次执行完几次请求就断开连接,下次需要再建立。由于建立连接是比较耗时的操作,所以建议使用长连接。但这会有个问题长连接一直连着就会导致内存占用过大,被系统强行沙雕。从而导致 MySQL 异常重启。如何解决呢?两个方法:定期断开长连接。使用特定时间,或者程序判断执行一个占用内存大的操作后,断开连接。之后需要操作就重连。mySQL 5.7 或以上版本,可以在每次执行一个占用内存大的操作后,执行 mysql_reset_connection 来重新连接资源,此时不需重连或重新做权限认证,但会把连接状态恢复到刚创建完时。1.2 查询缓存连接建立以后可以执行 select 语句了。这就会来到第二步:查询缓存。查询缓存中存储的数据是 key-value 的形式,key 是查询语句,value 是查询的结果。逻辑是这样的:先看看查询缓存有没该语句对应的 value?有则直接取出返回客户端,无则继续到数据库执行语句。查出结果后会放一份到缓存中,再返回客户端。你可能发现缓存真的香,但是并不建议使用查询缓存,因为有弊端。查询缓存的失效非常频繁,只有某个表有更新。它马上失效了,对于经常更新的表来说,命中缓存的概率极低。它仅仅适用于那些不经常更新的表。而 MySQL 似乎也考虑到这点了。提供了 query_cache_type 参数,把它设置为 DEMAND 就不再使用缓存。而对于要使用缓存的语句则可用 SQL_CACHE 显示指定,像这样:select SQL_CACHE * from user where id = 1;PS:MySQL 8.0 及以上版本把查询缓存删掉了,之后再也没有这块功能了。1.3 分析器如果没有命中缓存就进入分析器,这里就是对 sql 进行分析。分析器会做词法分析。你输入的 sql 是啥,由啥组成,MySQL 都需要知道它们代表什么。首先根据 "select" 识别出这是查询语句。字符串 "user" 识别成 "表名 user"、字符串 "id" 识别成 "列名 id"。之后进行语法分析,它会根据输入的语句分析是不是符合 MySQL 的语法。具体表现就是 select、where、from 等关键字少了个字母,明显不符合 MySQL 语法,这次就会报个语法错误的异常:它一般会提示错误行数,关注 "use near" 后面即可。1.4 优化器过了分析器,就来到了优化器。MySQL 是个聪明的仔,再执行之前会自己优化下客户端传过来的语句,看看那种执行起来不那么占内存、快一点。比如下面的 sql 语句:select * from user u inner join role r on u.id = r.user_id where u.name = "狗哥" and r.id = 666它可以先从 user 表拿出 name = "狗哥" 记录的 ID 值再跟 role 表内连接查询,再判断 role 表里面 id 的值是否 = 666也可以反过来:先从 role 表拿出 id = 666 记录的 ID 值再跟 user 表内连接查询,在判断 user 表里面的 name 值是否 = "狗哥"。两种方案的执行结果是一样的,但是效率不一样、占用的资源也就不一样。优化器就是在选择执行的方案。它优化的是索引应该用哪个?多表联查应该先查哪个表?怎么连接等等。1.5 执行器分析器知道了做啥、优化器知道了应该怎么做。接下来就交给执行器去执行了。开始执行,判断是否有相应的权限。比如该账户对 user 表没权限就返回无权限的错误,如下所示:select * from user where id = 1;
ERROR 1142 (42000): SELECT command denied to user 'nasus'@'localhost' for table 'user'PS:如果命中缓存没走到执行器这里,那么在返回查询结果时做权限验证。回到正题,如果有权限,继续打开表执行。执行器会根据表定义的引擎去使用对应接口。比如我们上面的 sql 语句执行流程是这样的:走 id 索引、调用 InnoDB 引擎取 "满足条件的第一行" 接口,再循环调用 "满足条件的下一行" 接口(这些接口都是存储引擎定义好的),直到表中不再有满足条件的行。执行器就将上述遍历得到的行组成结果集返回给客户端。对于 id 不是索引的表,执行器只能调用 "取表记录的第一行" 接口,再判断 id 是否 = 1。如果不是则跳过,是则存在结果集中;再调存储引擎接口取 "下一行",重复判断逻辑,直到表的最后一行。至此,整个 SQL 的执行流程完毕,小胖懂了吗?巨人的肩膀https://time.geekbang.org/column/article/68319总结本文通过一条简单的 SQL 查询语句,引出 MySQL 的结构以及这条 sql 查询语句的执行流程。相信你看完会对 SQL 有更深的理解。
源码分享-基于vue+elementUI后台管理系统
今天分享的源码是我很早之前gitee上开源的一个管理后台模版,是基于vue和elementUI的一个基础版,技术栈使用:vue2 + vuex + vue-router + webpack + ES6/7 + axios + elementUI + 阿里图标iconfont,可以说基本上使用了vue全家桶,对新手来说,是个不错的上手项目。体验地址https://nmgwap.gitee.io/vueproject/#/login这是一个纯前台的(不包括接口API)的界面,项目中的数据皆为假数据,没有使用插件mock,但是增删改查功能已开发完成,只需按照假数据格式返回数据编写API即可。1 完成功能√登录 -- 完成√路由拦截 -- 完成√商品管理(增加、编辑、搜索、删除) -- 完成√角色管理(增加、编辑、搜索、删除、权限管理) -- 完成√交易订单(增加、编辑、搜索、删除) -- 完成√用户管理(增加、编辑、搜索、删除、数据权限、刷新缓存) -- 完成√支付配置(增加、编辑、搜索、删除) -- 完成√系统环境变量(增加、编辑、搜索、删除) -- 完成√权限管理(增加、编辑、搜索、删除、配置权限) -- 完成√菜单管理(增加、编辑、搜索、删除) -- 完成√公司管理(增加、编辑、搜索、删除) -- 完成2 目录结构首先,我们熟悉下目录结构。├── /build/ # 项目构建(webpack)相关配置
├── /config/ # 项目开发环境配置
├── /src/ # 源码目录
│ ├── /api/ # 请求
│ ├── /assets/ # 组件静态资源(图片)
│ ├── /components/ # 公共组件
| ├── /api/ # 请求接口
│ ├── /router/ # 路由配置
│ ├── /vuex/ # vuex状态管理
│ ├── /views/ # 路由组件(页面维度)
│ ├── /config/ # 接口配置文件(请求地址)
│ ├── App.vue # 组件入口
│ └── main.js # 程序入口
├── /static/ # 非组件静态资源
├── .babelrc # ES6语法编译配置
├── .editorconfig # 定义代码格式
├── .eslintignore # ES6规范忽略文件
├── .eslintrc.js # ES6语法规范配置
├── .gitignore # git忽略文件
├── index.html # 页面入口
├── package.json # 项目依赖
└── README.md # 项目文档复制一个标准的项目结构,目录及项目中的作用一一对应。3 运行项目# install dependencies
npm install
# serve with hot reload at localhost:8080
npm run dev
# build for production with minification
npm run build
# build for production and view the bundle analyzer report
npm run build --report
# run unit tests
npm run unit
# run e2e tests
npm run e2e
# run all tests
npm test复制4 对接后台在config/index.js配置后端接口代理(只适用开发环境)dev: {
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
'/api': {
target: 'http://xxx.xxx.xxx.xxx:xxx', // 你请求的第三方接口
changeOrigin: true, // 在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
pathRewrite: { // 路径重写,
'^/api': '/api' // 替换target中的请求地址,也就是说以后你在请求http://api.jisuapi.com/XXXXX这个地址的时候直接写成/api即可。
}
}
},
},复制在src/api下,新建文件或者修改已有文件,配置接口请求地址import axios from 'axios';
import { req } from './axiosFun';
/**
* 商品管理
**/
// 商品管理-获取商品管理列表
export const GoodsList = (params) => { return req("post", "/api/Goods/list", params) };
// 商品管理-保存商品管理
export const GoodsSave = (params) => { return req("post", "/api/Goods/save", params) };复制在views/模块(如goods)/.vue文件里,getdata方法去掉模拟数据,取消注释的请求方法。5 源码码云(gitee)https://gitee.com/nmgwap/vueproject如果没有码云账号的话,提供网盘下载,获取回复关键字【管理平台源码】
Flask 入门系列教程(四)
HTML 表单在 HTML 表单中,可以通过 <form> 标签来创建,通过 <input> 来定义字段。<form method="post"> <!-- 指定提交方法为 POST -->
<label for="name">用户名</label>
<input type="text" name="name" id="name"><br> <!-- 文本输入框 -->
<label for="occupation">手机号</label>
<input type="text" name="occupation" id="occupation"><br> <!-- 文本输入框 -->
<input type="submit" name="submit" value="登录"> <!-- 提交按钮 -->
</form>编写表单的 HTML 代码有下面几点需要注意:在form标签里使用method属性将提交表单数据的 HTTP 请求方法指定为 POST。如果不指定,则会默认使用 GET 方法,这会将表单数据通过 URL 提交,容易导致数据泄露,而且不适用于包含大量数据的情况。对于input元素必须要指定name属性,否则无法提交数据,在服务器端,我们也需要通过这个name属性值来获取对应字段的数据。当然,编写 HTML 代码并不是我们的主要工作,所以我们可以通过 Flask 的相关插件来自动生成这部分 HTML 代码。WTFormsWTForms 支持在 Python 中使用类定义表单,然后直接通过类定义生成对应的 HTML 代码,这种方式更加方便,而且也更易于重用。因此,在一般的情况下,我们都不会直接使用 HTML 编写表单,使用 WTForms 是我们的第一选择。 使用Flask-WTF 处理表单扩展 Flask-WTF 集成了 WTForms,使用它可以在 Flask 中方便的使用 WTForms。Flask-WTF 将帮助我们更加方便的处理表单,包括表单的生成、解析、CSRF等等。安装 Flask-WTF 还是一样的,直接通过 pip 安装pip install flask-wtf因为 Flask-WTF 默认会为每一个表单启用 CSRF 保护,Flask-WTF 默认情况下使用程序密钥来对 CSRF 令牌进行签名,所以我们需要进行如下设置app.secret = 'my hard secret'定义WTForms表单类一个表单由若干个输入字段组成,这些字段分别用表单的类属性来表示。下面我们来编写一个登录类from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField, SelectMultipleField, SelectField
from wtforms.validators import DataRequired, EqualTo, ValidationError
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
password = PasswordField('Password', validators=[DataRequired()])
submit = SubmitField('Login')我们定义了一个 LoginForm 类,该类中又定义了三个字段,就是后面我们在 web 页面上会看到的表单字段。如下是一些常用的 WTForms 类字段字段类说明对应的 HTMLStringField文本字段<input type="text">SubmitField提交按钮<input type="submit">PasswordField密码文本字段<input type="password">FileField文件上传字段<input type="file">SelectField下拉列表<select></select>在 WTForms 中,验证器(validator)是一系列用于验证字段数据的类,我们在实例化字段类时使用 validators 关键字来指定附加验证器列表。如下是常用的验证器验证器说明DataRequired验证数据是否存在Email验证 email 地址EqualTo验证两个字段是否一致在模板中渲染表单为了能够在模板中渲染表单,我们需要把表单实例传入模板。首先实例化表单类 LoginForm,然后在 render_template() 函数中传入模板,于是我们修改 login 试图函数如下@app.route('/login/')
def login():
form = LoginForm()
return render_template('login.html', form=form)接着我们再创建一个 login.html 文件,写入如下{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}My Web - Login{% endblock %}
{% block page_content %}
{{ wtf.quick_form(form) }}
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">&times;</button>
{{ message }}
</div>
{% endfor %}
{% endblock %}这样,我们再刷新我们的项目页面,可以看到如下效果处理表单数据一般来说,从获取表单数据到保存表单数据大致需要以下几步:解析请求,获取表单数据对数据进行转换,验证表单数据是否符合要求如果验证错误,那么提示相关的错误信息如果验证通过,则保存数据提交表单在 HTML 中,当表单类型为 submit 的字段被点击时,就会创建一个提交表单的 HTTP 请求,请求中会包含表单中的各个字段。由于 Flask 为路由默认设置的监听的 HTTP 请求为 GET,而表单往往都是 POST 请求,所以我们需要手动给试图函数绑定 POST 请求@app.route('/login/', methods=['GET', 'POST'])
def login():
form = LoginForm()
return render_template('login.html', form=form)在试图函数中处理表单对于数据的验证,我们可以使用函数 validate_on_submit(),如果返回 True,则代表验证通过。@app.route('/login/', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
username = form.username.data
session['username'] = username
flash("登录成功,%s!" % username)
return redirect(url_for('index'))
return render_template('login.html', form=form)在这里,我们通过 form.username.data 来获取表单中的用户名,并通过 session 来保存,然后再重定向到 index 视图函数下面我们再来看看 index 视视图函数@app.route('/')
def index():
user = session.get('username')
return render_template('index.html', user=user)这个就相对简单了,从 session 中拿到用户名,然后传递给 index.html 模板,而 index.html 模板则与前面我们做的类似,就不再赘述了。进阶应用在模板中渲染错误如果函数 validate_on_submit() 返回 false,那么说明表单提交的数据验证不通过,WTForms 会把错误消息添加到表单类的 error 属性中,我们可以在模板中轻松的取出。在 loging.html 中添加如下代码{% for message in form.username.errors %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">&times;</button>
{{ message }}
</div>
{% endfor %}
{% for message in form.password.errors %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">&times;</button>
{{ message }}
</div>
{% endfor %}效果如下:文件上传对于文件上传,其实我们有许多安全的问题需要考虑:验证文件大小过滤文件名称验证文件类型下面我们来看一看 WTForms 能帮助我们做些什么首先定义一个文件上传的表单类,一个图片上传的表单class UploadForm(FlaskForm):
photo = FileField('Upload Image', validators=[file_required(), file_allowed(upload_set='.jpg')])
submit = SubmitField('Upload')在这里,我们定义了用于上传文件的表单,并且限制了只能上传 jpg 格式的文件类型下面我们编写上传图片的视图函数 upload@app.route('/upload', methods=['GET', 'POST'])
def upload():
form = UploadForm()
return render_template('upload.html', form=form)这里其实与登录的视图函数是类似的写法接下来就是 upload.html 文件的编写{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}My Web - Upload{% endblock %}
{% block page_content %}
{{ wtf.quick_form(form) }}
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">&times;</button>
{{ message }}
</div>
{% endfor %}
{% endblock %}我们重新刷新页面,得到如下效果处理上传文件对于上传的文件,我们在服务器端需要做一定的处理,例如保存、校验等等。下面我们继续编写 upload 视图函数app.config['UPLOAD_PATH'] = os.path.join(app.root_path, 'uploads')
@app.route('/upload', methods=['GET', 'POST'])
def upload():
form = UploadForm()
if form.validate_on_submit():
f = form.photo.data
filename = f.filename
f.save(os.path.join(app.config['ULOAD_PATH'], filename))
flash('上传图片文件成功!')
session['filename'] = filename
return redirect(url_for('show_images'))
return render_template('upload.html', form=form)我们通过 f.filename 来获取文件的名称,并保存上传的文件到指定目录下面就是编写展示图片的视图函数了@app.route('/uploads/<path:filename>')
def get_file(filename):
return send_from_directory(app.config['UPLOAD_PATH'], filename)
@app.route('/uploaded-images')
def show_images():
return render_template('uploaded.html')在上传好图片后,我们的程序会跳转至另外的页面,用于展示当前的图片{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}My Web - Upload{% endblock %}
{% block page_content %}
{% for message in get_flashed_messages() %}
<div class="alert alert-warning">
<button type="button" class="close" data-dismiss="alert">&times;</button>
{{ message }}
</div>
{% endfor %}
{% if session.filename %}
<a href="{{ url_for('get_file', filename=session.filename) }}" target="_blank">
<img src="{{ url_for('get_file', filename=session.filename) }}">
</a>
{% endif %}
{% endblock %}当然对于表单,还有很多其他的高级应用,比如富文本编辑器等,这些我们留到后面再进行讨论!这部分的完整代码,可以检出4a总结本节我们一起学习了 WEB 表单相关的知识,在后面的学习当中,我们还会多次使用,一定要好好消化这部分哦!
原来一条select语句在MySQL是这样执行的《死磕MySQL系列 一》
一、从宏观的角度分析MySQL首先看一张经典图片这幅图估计很多人都看到过,也是经典之作高性能MySQL里边的,如果有兴趣可以先看一下电子版的(如需要电子版可联系咔咔),感觉自己能看进去了,再去买书也来的急。闲话少说,进入正题。上图的客户端可以直接理解为PHP、Java等。接下来,你会看到连接、线程处理。这一部分并不是MySQL所特有的,而且大多数客户端、服务器都具有类似的结构。因此,一般而言,MySQL可以分为两层:Server层和存储引擎层。Server层主要包括连接层、查询缓存、分析器、优化器、执行器等重要模块组成,这一层还包含了MySQL核心Api部分,比如常用的格式化时间、加密等。存储引擎大家都很熟悉,因为在面试中不止一次的问过大家Innodb、Myisam存储引擎的不同。所以想过没有,MySQL为什么会有这么多的存储引擎呢?一切技术起源于当下问题,同样在MySQL中也不例外。MySQL在存储引擎这一方面的架构是插件式的,即可以随意切换不固定,而且MySQL5.5版本存储引擎已经默认为Innodb。二、一条SQL执行要经过多少困难?下图是咔咔之前培训时给发的资料图中还有一个熟悉的陌生人查询缓存模块,该模块在MySQL8.0中已不存在。关于该模块为何要被删除,后续的文章也将于大家交流。首先,我们将大致了解当我们执行一条SQL语句时,如何在这个架构图中运行。2-1 连接器mysql -u root -p连接数据库命令,在执行之后,你将需要输入密码。当完成经典的TCP握手之后,连接器就开始发挥作用了。如果码错误时,则返回Access denied for user ‘root‘@‘localhost‘ (using password: YES,错误编码1045。如果连接信息均正确,则此时将根据你输入的用户访问权限表来获取该用户的权限,此处必须清楚,当你登录成功后,即使其他人修改了你的权限,在这个连接未断开之前你的权限是不会发生改变的。当你连接完成之后,如果你一直不做任何事情,执行show processlist将会看到一个sleep,表示空连接。那么你知道在MySQL中,如果连接成功后没有进行任何操作,多久会被自动中断?可以执行show variables like 'wait_timeout';用于查看时间。在MySQL中如果没有特别说明,那么所有的时间都是以秒为单位的,根据时间转换可以得知空连接持续8小时。2-2 查询缓存你需要注意的是,MySQL8.0已经被取消了,这个问题不止说了一次了,特别是那些正在使用MySQL8.0以下版本的小伙伴要注意哈!当你切换到8.0时候,遇到这个问题不知道怎么解决。MySQL8.0为何取消查询缓存模块这个模块的设计,把查询语句作为key ,将结果作为value 进行缓存,一旦这个表有更新,之前所有的缓存都会被清除掉。这就像你辛辛苦苦写的代码提交之后被别人覆盖一样难受。MySQL8.0以下的版本提供了一个参数query_cache_type = enmand来控制是否要使用查询缓存,在设置完成后,默认的select语句将不会被缓存。如果确实可以使用部分场景,那么你可以将sql_cache添加到select关键字之后。如果一条select语句之前被缓存过,那么结果集在这里就会直接返回,而没有缓存过的select语句就比较辛苦了,还要继续自己的漫漫长路。2-3 分析器MySQL8.0之前,它会在进入分析器之前判断是否缓存,在MySQL8.0之后,连接器验证成功后就直接进入分析器。分析器,根据字面意思来理解就是分析要执行的SQL语句是什么,要做什么。比如执行select * from user where id = 1MySQL首先根据select判断这是一个查询语句,然后将user识别为表名,id识别为字段名,这个过程被称为词法分析。下一步,需要知道该SQL的语法是否正确,进行语法分析,如果语法不对你就会看到You have an error in your SQL syntax错误。通常,将在use near中找到该错误。2-4 优化器到了这一步,MySQL知道你要做什么,但是要选择最佳执行方案。优化器都优化些什么?举例来说:多个索引时选择那个索引、多表关联时连接顺序。现在你是否想知道,优化器将优化多表关联的连接顺序,那在写SQL语句时是否就不必考虑连接顺序呢?当然不是,能让MySQL少做事情就少做,还是一个准则用小表驱动大表。2-5 执行器通过要做什么、怎么做后这条SQL语句才会真正的被执行,先进行权限验证,若没有权限则直接返回权限错误,否则根据表定义的存储引擎,去使用对应引擎提供的接口。三、总结上图包含了正文的所有知识点,也是整个MySQL的大体执行流程图,后期文章都将围绕这几个要点展开。
Flask 入门系列教程(二)
请求响应循环其实大家对于 HTTP 协议应该是再熟悉不过了,它是超文本传输协议,定义了服务器和客户端之间信息交流的格式和传递方式。那么对于上面的问题,我们其实也可以大致的说出一个简易流程:按下 Enter 之后,浏览器会向 URL 地址发送一个 HTTP 请求在浏览器的背后,有一个后台程序,用于接收相关请求,并返回处理的结果浏览器接收结果,并渲染给终端用户查看事实上,每一个 Web 应用都包含这种处理模式,即“请求-响应循环(Request-Response Cycle)”:客户端(浏览器等)发出请求,服务端处理请求并响应。我们再把上面的流程扩展到 Flask 服务器上,就是由浏览器生成的 HTTP 请求发送至 Web 服务器。Web 服务器接收到请求后,经由 WSGI 协议把数据转换成 Flask 程序能够识别的数据后,传递给 Flask 程序。然后 Flask 程序再根据视图函数等处理相关请求,最后再返回响应给 Web 服务器。最终交由浏览器来渲染结果,比如加载 CSS,执行 JavaScript 代码等等操作。我们可以看下下面的图片这里有两个概念我们要先明确下Web 服务器:Web 服务器是一类特殊的服务器,其作用是主要是接收 HTTP 请求并返回响应。我们常用的 Web 服务器有 Nginx,tomcat 等,相信大家都非常熟悉或多少听说些。WSGI:它确切来说应该是一种协议,或者接口规范。定义了 web 服务器和 web 应用(Flask 等)之间的接口规范。只有 Web 服务器和 Web 应用都遵守了 WSGI 协议,那么他们才能正常通信。比如说在上一节我们使用 app.run() 启动测试服务器时,就是使用了 Flask 自带的 Web 服务器,当然这种服务器只能用来开发测试时使用,在生成环境,我们需要部署到 Nginx 等 Web 服务器上。在了解了 Web 程序的整体运行流程之后,我们再来深入的探究下 Flask 的工作原理。Flask上下文HTTP 请求当 Flask 接收到客户端的请求后(后面的章节中我们都会直接省略 Web 服务器和 WSGI 的转换步骤),就会产生一些视图函数可以访问的对象,通过这些对象来处理请求,这就是请求对象--request。request 对象包含了 HTTP 请求中的 URL 信息和相关的报文信息URL 信息例如请求 URL 为:http://www.luobodazahui.top/hello?name=zhouluobo属性值path'/hello'full_path'/hello?name=zhouluobo'host'www.luobodazahui.top'host_url'http://www.luobodazahui.top'base_url'http://www.luobodazahui.top/hello'url'http://www.luobodazahui.top/hello?name=zhouluobo'报文信息属性或方法说明args查询字符串信息cookiescookies 信息字典data字符串形式的请求数据form表单数据get_json()获取 json 类型的请求数据method请求的 HTTP 方法下面我们通过一个简单的例子来具体查看下@app.route('/test/')
def test_view():
query = 'Flask'
if request.args:
query = request.args.get('name', 'Flask')
host = request.host
path = request.full_path
cookie = request.cookies
method = request.method
return """
<h1>
<p>query string: %s</p>
<p>host: %s</p>
<p>path: %s</p>
<p>cookies: %s</p>
<p>method: %s</p>
</h1>
""" % (query, host, path, cookie, method)当我们在浏览器输入:http://127.0.0.1:5000/test/,可以得到当我们在浏览器输入:http://127.0.0.1:5000/test/?name=luobo,可以得到在这里,request 是一个全局的变量,我们可以在任何的视图函数中去使用它。当然,这仅仅局限在当前线程中,对于多线程服务器中,不同线程服务器的请求对象是不同的。两种上下文在 Flask 中,有两种上下文:程序上下文和请求上下文。主要包括下面四种变量名上下文类型说明request请求上下文请求对象,封装了 HTTP 请求中的内容session请求上下文请求上下文用户会话,存储请求之间需要保留的值g程序上下文处理请求时的临时存储对象,仅在当前请求有效current_app程序上下文当前的程序实例对于 request,我们已经了解了,下面再来看看 session。sessionsession 最常用的就是确认用户状态了,比如检查用户是否登陆等。下面我们就简单实现一个基于浏览器的用户认证功能,来理解下 session 的强大功效。普通的认证系统,用户在页面表单中输入用户名和密码后,后台程序进行确认,如果认证通过,则返回响应,并在浏览器的 Cookie 中设入标记,例如“loginID:User1”。但是因为浏览器 Cookie 是很容易被修改的,所以如果使用名称存储这些信息就会非常不安全,此时就需要 session 登场了。在 Flask 中 session 通过密钥对数据进行签名从而加密数据,所以我们需要先设置一个密钥。app.secret_key = 'Very Hard Secret'当然,更加安全的做法是把该密钥写到部署服务器的环境变量中,对于这种写法,我们在后面部署程序时再详细讲解。接下来我们做模拟用户认证的情况,写两个视图函数,分别模拟登陆和登出场景。@app.route('/login/')
def login():
session['loginID'] = 'admin'
return redirect(url_for('welcome'))@app.route('/logout/')
def logout():
if 'loginID' in session:
session.pop('loginID')
return redirect(url_for('welcome'))再修改 welcome 视图函数,用于展示是否登陆@app.route('/user/', defaults={'name': '陌生人'})
@app.route('/user/<name>')
def welcome(name):
res = '<h1>Hello, %s!</h1>' % name
if 'loginID' in session:
res += 'Authenticated'
else:
res += 'UnAuthenticated'
return res这里我们使用了 redirect 函数,是一个重定向方法。只需要传入目标的 URL 地址,就可以在视图函数处理结束后跳转至目标的页面。当我在浏览器输入:http://127.0.0.1:5000/login/的时候,就会在浏览器中插入一个加密的 cookie 并跳转至 welcome 页面可以看到,插入的 cookie 是加密的,这样就加大了攻击者的攻击难度,从而在一定程度上保护了我们系统的安全。g 和 current_app其实你应该会有个疑惑,我们已经有了一个 app 程序实例了,为什么还需要定义一个 current_app 变量呢?在不同的视图函数中,request 对象都表示和视图函数对应的请求,也就是当前请求(current request)。而程序会有多个程序实例的情况,为了能获取对应的程序实例,而不是固定的某一个程序实例,我们就需要使用 current_app 变量。当然对于多个程序实例的情况,我们留待后面的章节详细介绍。g 存储在程序上下文中,而程序上下文会随着每一个请求的进入而激活,随着每一个请求的处理完毕而销毁,所以每次请求都会重设这个值。比如说如果对于某个请求,我们几个视图函数都需要用到一个前端传递过来的变量,那么就可以把它保存到 g 变量当中g.name = request.args.get('name')这样,其他的视图函数就可以在同一个请求中直接使用 g.name 来访问,而不用每次都调用 request 了。对于 current_app 和 g 的更多使用方式,在后面的学习中我们会慢慢接触的更多。请求钩子在处理请求之前或之后执行的代码,就称为请求钩子。比如在请求之前,我们需要初始化数据库,创建 admin 用户等等,就需要在请求之前调用请求钩子来做这件事情。在 Flask 中提供了四种请求钩子,以装饰器的形式注册到函数,使得我们可以方便的应用该功能钩子名称作用before_first_request在处理第一个请求之前运行before_request在每次请求之前运行after_request如果没有未处理的异常抛出,则在每次请求之后运行teardown_request即使有未处理的异常抛出,也在每次请求之后运行在请求钩子函数和视图函数之间共享数据一般使用上下文全局变量 g,比如上面的例子我们就可以写成from flask import g
@app.before_request def get_name():
g.name = request.args.get('name')重定向回上一个页面功能实现重定向回上一个页面,这应该是一个非常常见的应用场景,那么该如何通过 Flask 来实现呢。首先我们修改下 login 视图函数,在请求参数中查找 next 参数,如果存在则重定向到 next 参数对应的地址,否则重定向到 hello 视图函数对应的地址@app.route('/login/')
def login():
session['loginID'] = 'admin'
return redirect(request.args.get('next') or url_for('hello'))这里所谓的 next 参数,其实只是一种约定俗成的命名方式再修改 needpage1 视图函数,如果用户未登陆则展示登陆链接,并保存 next 参数@app.route('/needlogin1/')
def needLogin1():
if 'loginID' in session:
return '<h1>Hello, needLogin1!</h1>'
else:
return """
<h1>Login</h1><a href="%s">Go To Login</a>
""" % url_for('login', next=request.url)这样,当用户处于未登录状态时,就可以点击 Go To Login 链接进行登陆,登陆成功之后会自动跳转回当前页面了。安全处理现在我们虽然完成了功能,但是却还遗留了相关的安全问题。因为我们的 next 参数是以查询字符串的方式写在 URL 里的,所以如果有人拦截了我们的请求,就可以随便修改 next 的指向,此时我们就需要验证 next 变量是否属于我们的应用,否则很容易被指向外部链接,从而造成安全隐患。我们先创建一个检查 URL 正确性的函数from urllib.parse import urlparse
def check_next(target):
ref_url = urlparse(request.host_url)
test_url = urlparse(target)
return ref_url.netloc == test_url.netloc该函数接收目标地址为参数,并比较本应用的 host_url 和目标地址的 host_url 是否相同改写 login 视图函数@app.route('/login/')
def login():
session['loginID'] = 'admin'
target = request.args.get('next')
if check_next(target):
return redirect(target)
return redirect(url_for('hello'))只有当 check_next 函数返回 True 时才重定向到 next 变量对应的地址,否则重定向到 hello 对应的地址。本节所以代码可以查看本教程的 GitHub 代码仓库的 2a tag 版本代码总结本章着重介绍了 Flask 中的 HTTP 相关知识,包括 Web 服务器的运行方式,Flask 上下文的使用,请求钩子,重定向等知识点。
Mac 在阿里云服务器上搭建最新的 SVN
1、登录自己的服务器ssh -p root@公网IP2、创建一个用户(如果你不懂组和用户请看我的这篇博客)useradd -m -g 组名 新用户名 # 我创建的用户名叫 JKSvn,名字你可以随便起
passwd 新用户名 # 设置密码3、安装 svn(我采用的方式一,因为我方式二我的不支持)方式一: yum install subversion
方式二:apt-get install subversion提示:查看svn版本 svnserve --version4、创建版本库3.1、进入服务器的用户(我在home目录下创建了一个用户JKSvn)
cd /home/JKSvn
3.2、创建svn文件夹(目录)
mkdir svn
3.3、创建版本库
svnadmin create /home/JKSvn/svn/mycodesubversion目录说明:db目录:就是所有版本控制的数据存放文件hooks目录:放置hook脚本文件的目录locks目录:用来放置subversion锁定数据的目录,用来追踪存取文件库的客户端format文件:是一个文本文件,里面只放了一个整数,表示当前文件库配置的版本号conf目录:是这个仓库的配置文件(仓库的用户访问账号、权限等)4、cd进入conf目录(该svn版本库的配置文件)4.1、修改authz文件是权限控制文件 (不会vi命令的要自己学习了)vim authz (进入到authz)i(进入编辑状态)输入账号和权限(在此只写两个用户,你可以写多个用户)[groups]
ios_peoples = user1,user2
android_peoples = user3,user4
php_peoples = user5
[/]
admini = rw
[mycode:/iOS]
@ios_peoples = rw
* =
[mycode:/Android]
@android_peoples = rw
* =
[mycode:/PHP]
@php_peoples = rw
* =esc 退出编辑shift + ;输入 wq 保存提示: 上面的 iOS、Android、PHP 是在mycode(版本库)下的文件夹下,创建这些文件夹的方式后面我会阐述4.2、修改passwd是帐号密码文件也是 vim passwd 进入到passwd文件,接下来就是修改了,用下图展示,步骤和上面一样4.3、svnserve.conf SVN服务配置文件,打开下面五项anon-access = none ( read 改为 none)
auth-access = write
password-db = passwd
authz-db = authz
realm = My First Repository说明一下:如果在 svn 下再建一个版本库,那么realm = My First Repository
realm = My Second Repository
realm = My Third Repository
.....................................5、启动svn版本库svnserve -d -r /home/JKSvn/svnps:停止SVN命令killall svnserveps:查看服务是否开启ps -ef |grep svn6、在SVN客户端进行测试6.1、登录mycode的 管理员 admini 进行创建版本库mycode下创建子文件夹(iOS、Android、PHP)6.2、账号分类:(下面的要和上面4.1的配置保持一致)iOS: user1、user2Android: user3、user4PHP: user56.3、iOS开发者登录客户端(Android,PHP都一样,在此用iOS来做展示)admini(管理员) 只需要给iOS开发者 服务器IP、账户名、账户名的密码、仓库的路径 mycode/iOS(安卓的路径是:mycode/Android、PHP的路径是:mycode/PHP)6.4、在mycode(版本库)下分iOS、Android、PHP 的好处管理员admini可以看到 所有人的开发代码,iOS只能看到mycode/iOS 下的代码,Android只能看到mycode/Android 下的代码,PHP只能看到mycode/PHP 下的代码,这样分类更加清晰,如果来了新的开发者,直接在authz里面添加人员就好,记得在passwd设置密码,不需要再重启SVN6.5、查看版本库下的子目录svn checkout svn://47.92.215.236/mycode47.92.215.236是服务器的id , mycode是版本库的名字7、到此在阿里云服务器搭建的SVN完成了,如果您在服务器搭建SVN有任何问题都可以联系我。如果本篇文章对您有帮助记得给个喜欢,谢谢
飞天加速计划·高校学生的实践体验
自我介绍首先介绍一下自己,我是一名在读的大三学生,专业是软件工程专业,课程方面上老师的要求,需要一台云服务器来完成服务器测试环境的搭建。云服务器有很多个不同的平台,比较出名的就有阿里云,然后就去阿里云的官网搜索ECS云服务器,有月付的和年付的,需要多久就买多久,算力也能按需购买,接着深入了解就在知乎上的一篇文章上看到了有个叫做“飞天加速计划·高校学生在家实践”的活动,跳转到页面需要完成学生认证和做题测试,完成后,很快就领取到了资格。使用攻略服务器可以选择地域,华南或者华北的,系统有Windows,Ubuntu和Linux等,而我选择了Ubuntu,一开始还不是很懂操作,不断的试错,才逐渐熟悉起来,进入了管理控制台,可以修改root密码和服务器名称,通过远程连接来连到服务器中,然后通过命令行来操作,也可以通过软件比如说xshell来操控,xshell也是一个挺好用的软件,值得一试。在服务器上在阿里云服务器下安装启动jdk,并配置环境变量,通过xshell或者filezilla等软件将调试好的压测脚本上传到云服务器上,安装jmeter,在云服务器上启动被测项目,通过外网的ip地址能正常访问,设置安全组来开放8080端口,在云服务器上通过非GUI模式执行压测脚本,这就是近些日来在云服务器上的使用体验。收获总结我相信云服务器能做的事情并不止这些,需要在以后更加的深入学习,比如说web项目,网站的搭建,博客系统,不过这个需要域名,Java的框架技术,docker,nginx等等好多方面都需要用到。服务器是开发中必不可少的一环最后,感谢阿里云能提供这个活动供大家能体验学习到ECS云服务器的相关知识。希望审核能通过。谢谢阿里云
ECS使用体验
我是一名在读的计算机科学与技术的研究生,因为疫情一直在家学习。由于学校网络是校园网,使用实验室电脑部署项目需要内网穿透,而且实验室电脑资料比较多,不方便部署项目。听说阿里云的服务器有学生优惠,一个月不到十块钱,便想着申请一个。上了阿里云官网一看,有一个“飞天加速计划·高校学生在家实践”活动,学生认证成功后可以申请免费使用两个月15天,只需要动动手做一下任务,其中一个任务还是在试用平台体验1个小时,这对小白来说太好了。 很快就申请成功了15天,使用起来非常方便,分配的服务器是2核的,云盘也有40G,这足够使用了。一开始可以更改一下服务器实例密码,操作简单。在云服务器上更换操作系统特别方便,速度也很快,如果用自己电脑安装系统或者安装虚拟机配置系统都需要好久,而且有丰富的操作系统可供选择,使用镜像实现一键部署。把项目部署在云服务器上也很快,很方便,在线安装应该软件网络也比较快。访问服务器端口一定要配置安全组规则,对端口号进行放行,不然不能访问相关端口。云服务器都有一个主网IP,不需要再使用内网穿透,直接使用Xftp上传文件到云服务器,使用Xshell操作云服务器。 最后说一下这个飞天加速计划,这个计划对高校学生真的非常友好,学校实验室资源有限,不能满足一人一台服务器,阿里云平台免费为我们学生提供这个资源,对我们学习提供支持,希望可以收获更多服务器实践经验。在这里要特别感谢阿里云,感谢飞天加速计划,感谢阿里云团队给我们提供了这么好的平台。