Day 5

编程入门 行业动态 更新时间:2024-10-19 15:38:48

<a href=https://www.elefans.com/category/jswz/34/1768188.html style=Day 5"/>

Day 5

别人的理解

/


自己的理解

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 先看懂整体框架,再看详细实现 1.coroweb .py在client请求开始返回func( * args,  * * kw),然后编写func( * args, * * kw)处理 #比如get('/index')(func(*args, **kw)) 2.middlewares = [logger_factory, response_factory] init_jinja2(app, filters = dict (datetime = datetime_filter)) 3. 先看懂add_routes(app,  'handlers' ),然后是add_static(app),最后await handler(request)  add_route(app, handles.create_comment) 变成 # 自动把handler模块的所有符合条件的函数注册了: add_routes(app,  'handlers' ) add_routes,handler - >是否有index,blog等属性 / / 不理解的 fn  =  getattr (mod, attr) / / 到了add_route,变成了app.router.add_route(method, path, RequestHandler(app, fn)) 4. 最后的细节 func( * args,  * * kw) if  全部都会执行 5.middlewares  拦截器 / / await handler(request) 06.30  更新 上面一团乱糟糟的,重新梳理 1.coroweb .py 主要是app.router.add_route(method, path, RequestHandler(app, fn)) 理解为url和对应的函数绑定 2.add_route (app,fn) fn变成协程 3. 然后handlers.py写具体实现方法,比如index() 请求一过来,先找到add_route对应的函数,因为app.py已经批量绑定了handlers.py中方法到url上(模糊匹配,类似的感觉,通用) 找到对应的函数,执行handlers中的函数,执行的过程调用coroweb.py中的get和post偏函数,就是装饰器 4. 再添加一些拦截器 app  =  web.Application(loop = loop, middlewares = [logger_factory, response_factory]) 其中 return  (await  handler(request)),handler(request)应该是在框架里面写好了。 5.handlers .py就是写全部逻辑的地方。


代码地址

.py



然后要搞清楚一下这些东西,多

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 文档 http: / / aiohttp.readthedocs.io / en / stable / web.html 先看懂整体框架,再看详细实现 参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。 去熟悉这些写法 数据集合并 http: / / pandas.pydata / pandas - docs / stable / merging.html from  jinja2  import  Environment python inspect模块解析  -  郭猛的个人空间 Python 的内置函数 __import__ inspect.signature https: / / docs.python / 3 / library / inspect.html 为啥进程池封装在装饰器中不能生效,而多进程可以? http: / / pythontutor / visualize.html #mode=edit 1000 + w 的数据去重也可以用 bloom  filter  啊,就用 Redis 的 bitmap 存 bit 数组就可以了。 aiomysql.DictCursor会将结果返回为字典 __all__ = [ "echo" , "surround" , "reverse" ]。 这就意味着当 from  sound.effects  import  * 语句执行时,会导入那三个模块 相当详细的解释 https: / / github / icemilk00 / Python_L_Webapp / blob / master / www / app.py https: / / github / icemilk00 / Python_L_Webapp https: / / www.v2ex / t / 347788 https: / / www.v2ex / t / 347421 还要不断的去翻python3的原始文档,看asyncio,httpio的说明和源代码,最终还是明白了 优先看 https: / / github / moling3650 / mblog / blob / master / www / app / frame / __init__.py http: / / blog.csdn / qq_38801354 / article / details / 73008111 https: / / segmentfault / a / 1190000008400059 http: / / www.w2bc / article / 218471 https: / / zhuanlan.zhihu / p / 22494483 http: / / lovenight.github.io / 2016 / 09 / 25 / Python - 3 - % E5 % AD % A6 % E4 % B9 % A0 % E7 % AC % 94 % E8 % AE % B0 / http: / / blog.csdn / yueguanghaidao / article / details / 11708417 init_jinja2(app, filters = dict (datetime = datetime_filter)) def  init_jinja2(app,  * * kw):      logging.info( 'init jinja2...' )      options  =  dict (          autoescape  =  kw.get( 'autoescape' True ),          block_start_string  =  kw.get( 'block_start_string' '{%' ),          block_end_string  =  kw.get( 'block_end_string' '%}' ),          variable_start_string  =  kw.get( 'variable_start_string' '{{' ),          variable_end_string  =  kw.get( 'variable_end_string' '}}' ),          auto_reload  =  kw.get( 'auto_reload' True )      )      path  =  kw.get( 'path' None )      if  path  is  None :          path  =  os.path.join(os.path.dirname(os.path.abspath(__file__)),  'templates' )      logging.info( 'set jinja2 template path: %s'  %  path)      env  =  Environment(loader = FileSystemLoader(path),  * * options)      filters  =  kw.get( 'filters' None )      if  filters  is  not  None :          for  name, f  in  filters.items():              env.filters[name]  =  f      app[ '__templating__' =  env env上添加属性datetime_filter,变成了方法



最后看能不能看懂别人的问题和回复

问题

看到好多函数都有request做参数,但这个request是什么时候传进去的呢,没看到从哪里得到request的,新手求轻喷


哥们,你源码里的所有url处理函数的参数都是来源于request。可我查询了aiohttp,发现request是一个对象,好像没有那些属性呀。aiohttp。迷惑中,盼回。


RequestHandler

我遇到的第二个难点就是RequestHandler,因为RequestHandler看起来是一个类,但又不是一个类,从本质上来说,它是一个函数,那问题来了,这个函数的作用到底是什么呢?

如果大家有仔细看day2的hello world的例子的话,就会发现在那个index函数里是包含了一个request参数的,但我们新定义的很多函数中,request参数都是可以被省略掉的,那是因为新定义的函数最终都是被RequestHandler处理,自动加上一个request参数,从而符合app.router.add_route第三个参数的要求,所以说RequestHandler起到统一标准化接口的作用。

接口是统一了,但每个函数要求的参数都是不一样的,那又要如何解决呢?得益于factory的理念,我们很容易找一种解决方案,就如同response_factory一样把任何类型的返回值最后都统一封装成一个web.Response对象。RequestHandler也可以把任何参数都变成self._func(**kw)的形式。那问题来了,那kw的参数到底要去哪里去获取呢?

request.match_info的参数: match_info主要是保存像@get('/blog/{id}')里面的id,就是路由路径里的参数

GET的参数: 像例如/?page=2

POST的参数: api的json或者是网页中from

request参数: 有时需要验证用户信息就需要获取request里面的数据

说到这里应该很清楚了吧,RequestHandler的主要作用就是构成标准的app.router.add_route第三个参数,还有就是获取不同的函数的对应的参数,就这两个主要作用。只要你实现了这个作用基本上是随你怎么写都行的,当然最好加上参数验证的功能,否则出错了却找不到出错的消息是一件很头痛的是事情。在这个难点的我没少参考同学的注释,但觉得还是把这部分的代码太过复杂化了,所以我用自己的方式重写了RequestHandler,从老师的先检验再获取转换成先获取再统一验证,从逻辑上应该是没有问题,但大幅度简化了程序。


你可以参考我的data_factory的实现。


如果method == 'GET'时,参数就是查询字符串,也就是request.query_string

如果method == 'POST'时,有两种可能,Ajax的json和html的form(表单),分别对应request.json()和request.post()。 data_factory的主要作用就是把这些参数统一绑定在request.__data__上。


在RequestHandler里,init是初始化用的,在生成实例的时候执行,而call是模拟()的调用,需要在实例上应用,在下面这句代码里:


  app.router.add_route(method, path, RequestHandler(func))


  RequestHandler这个类并没有创建实例,是不是意味着call并没有执行,在我的代码里貌似是这样的。

  小白一只,卡在这里好几天了,希望能解决我的疑惑。。。


你理解错了,RequestHandler(func)就是一个实例,只不过没有给它命名,最终会在factorys的response_factory调用。


r = await handler(request)


这里的request也就是__call__(request)的参数。


你说的意思是 r = await handler(request) 里的handler就代表RequestHandler(func),这样call就被执行了,可是我还是不太明白handler怎么跟RequestHandler(func)联系起来的


response_factory的r = await handler(request)实际上是调用r = await RequestHandler(request),然后内部又调用了await self._func(**kw),这里才是调用我们自己定义的函数比如await hello(**kw),最后把函数处理后的数据传回到response_factory,response_factory根据hello(**kw)的返回值封装成一个响应对象发送给浏览器。


app.router.add_route只不过是一个注册器,把我们写的某的函数和URL绑定,形成一个映射关系而已


不赖@流留66 ,我也糊涂了。

难道r = await handler(request)不是调用r = await RequestHandler(app,fn)(request)的意思吗?

@灰_手 你手滑了?


pymysql.err.InternalError: (1054, "Unknown column 'password' in 'field list'")


我的git的ORM测试,不过要将# 测试count rows语句下面两句注释,我重写了findNum方法了。


day06,day07是水课,后边除了还有个day13 watchdog , day15 fabric之外都是体力活


day12 日志列表分页,如果之前没有做过分页,这一天的课程也很有趣


前端确实是个大坑,前端届太闹腾.今天这个框架火了,明天那个库黄了.

要学的东西又多又杂,看得我两股战战,几欲先走.


最基本的老三样还是得掌握的:

html掌握常用标签,会简单布个局.


css 掌握选择符和浮动清除的概念就能毕业,日常使用查手册就差不多了.

css这东西掌握了没卵用,我用起来和别人用起来是两回事,没有美感做出东西来照样丑.

像UIKit,bootstrap这些UI包真的是业界良心,一下子把没有美感不懂设计的人们救活了.


原生javascript,它的很多概念我学过又忘记了.

只剩下document.getElementById,XMLHttpRequest,setInterval是我的三宝.

有了这三样,DOM,ajax,动画我都能捅咕一下.

以前javascript对象的继承方式有很多种,我一样也记不住.

prototype引用来引用去的根本闹不清什么状况.

廖大的教程讲ES6,我要学一下.


学习资料

石川(blue) 2012年录制的一套javascript教程32集.

第1集 初探javascript魅力

.html


我学习实战的时候day04,day05卡了一个星期.

前端的部分我是直接抄廖大的代码.我对廖大实现的ORM特别感兴趣.

day04,day05以后我就判定自己毕业了,博客的其他功能我根本没完成.

我现在复习了一遍整个教程,目前看到sqlalchemy部分,快到实战填坑了,假如在实战部分我学习起前端来,大概就绕不回来了.到时候咱们就只好在node.js教程里见了.


前端是大坑!

html,css,js的耦合度太高了,随便一个改动也是非常困难的,比如你想把vue.js从0.12版升级到1.02版,就会发现在语法有N多不兼容的地方,不但要改js,就连html也要改,如果你想把uikit换成bootstrap的话,你会发现html要改,javascipt也是要改的,css是改成最少的地,前提是你不用它,只要换了ui框架,命名问题必要存在,除非你自己在js把id和class写成可变的... 总之就是牵一发而动全身。


有问题多找文档,没有精力学就是复制廖大的代码好了。


 UK中文网,虽然容易看懂,但也有些deom不适用了。能看英文最好看英文的

Vue.js中文官网,这里的文档还是挺好的,易实现,效果好。


import(module_name, fromlist=['get_submodule'])里的get_submodule是什么意思?

在其他代码里也没发现get_submodule模块


get submodule没有任何意思。这里是个黑魔法。当fromlist不为空时,__import__可以直接导入子模块


user.id 是users表中每一行记录业务无关的唯一标识.

它的值由定义在models.py中的next_id()生成.


@post('/api/authenticate') #登录鉴定

sha1.update(user.id.encode('utf-8'))

sha1.update(b':')

sha1.update(passwd.encode('utf-8'))


这么验证的原因是为了配合新建用户的时候设置的密码的格式


@post('/api/users')#新建用户

sha1_passwd = '%s:%s' % (uid, passwd) #密码的生成格式

user = User(id=uid, name=name.strip(), email=email,

     passwd=hashlib.sha1(sha1_passwd.encode('utf-8')).hexdigest(),#看密码那里

     image='/%s?d=mm&s=120' % hashlib.md5(email.encode('utf-8')).hexdigest())

    await user.save()


 这里看到的password已经是进行过一次摘要的,防止密码明文在中途被截获.

 注册用户页面和用户登录页面上使用javascript 提前将email和password明文揉在一起 

 CryptoJS.SHA1(email + ':' + this.password1).toString()

 最后将摘要作为password,和email一起发送给服务端.服务端将user.id:password再次进行摘要操作.作为用户的密码密文存储在数据库表中.

带cookie的登录流程:

前提条件:

创建一个管理员账户,注册新用户,admin字段手动修改为1.我记得我改过.

1.访问/manage开头的url时,auth_factory middleware检查客户端带来的cookie,鉴定是否可以免登录.如果客户端没有带来一个叫COOKIE_NAME的cookie或者cookie内容是伪造的,将客户端浏览器重定向到登录页面.


2.在登陆页面填写email和passwrod,点击登录按钮.

前端代码将email:password进行一次摘要作为password,然后通过廖大的postJSON将email和password发送给服务端 /api/authenticate.

3.服务端鉴定,authenticate方法中,首先通过email查找到对应的user,得到user.id.

然后将user.id:password进行一次摘要,并与创建用户时生成的user.password进行对比.

若两者相同,则创建一个免登录cookie,随response一起响应给客户端.


cookie的内容,查看user2cookie()方法.


为什么 user.passwd = '******'

user.passwd = '********'

r.content_type = 'application/json'

r.body = json.dumps(user, ensure_ascii=False).encode('utf-8')

return r

因为这个response的body部分是将代表当前登录用户的user对象转化为了一个json返回给客户端.

如果不抹掉user.passwd,客户端就能拿到用户登录密码的密文.


免登录cookie的制作配方里用到了这个密文


def user2cookie(user, max_age):

    '''

    Generate cookie str by user.

    '''

    # build cookie string by: id-expires-sha1

    expires = str(int(time.time() + max_age))

    s = '%s-%s-%s-%s' % (user.id, user.passwd, expires, _COOKIE_KEY)

    L = [user.id, expires, hashlib.sha1(s.encode('utf-8')).hexdigest()]

    return '-'.join(L)

 客户端现在拿到密文虽然没什么卵用,但本站在未来被黑的风险增加.


 在未来的日子里,不排除有科学家用户首先日翻了消息摘要算法,然后截获本站管理员的登录密码密文,伪造出合法的免登录cookie,用管理员的身份来日翻本站.

 这种情况属于天有不测风云,不可抗力,真有那一天,反正大家都被日翻了,谁也不笑话谁.


这是我代码存放的地点:


登陆按钮的动作

templates/signin.html


这段js大概是vue与jquery混合双打的写法.我对vue没有研究,大概能猜出这段代码的意思.

data里email和passwd的值由vue来维护.

methods里边的submit对应的就是提交处理代码.

首先一个event.preventDefault()阻止默认事件,由客户端决定行为.

然后下边把email:password摘要一次,然后用廖大的postJSON向 /api/authenticate发送post请求,如果返回没有错误,就给浏览器地址栏定位到站点根目录.

 <script>

$(function() {

    var vmAuth = new Vue({

        el: '#vm',

        data: {

            email: '',

            passwd: ''

        },

        methods: {

            submit: function(event) {

                event.preventDefault();

                var

                    $form = $('#vm'),

                    email = this.email.trim().toLowerCase(),

                    data = {

                        email: email,

                        passwd: this.passwd==='' ? '' : CryptoJS.SHA1(email + ':' + this.passwd).toString()

                    };

                $form.postJSON('/api/authenticate', data, function(err, result) {

                    if (! err) {

                        location.assign('/');

                    }

                });

            }

        }

    });

});

    </script>

你说的注册按钮绑定的 在

templates/register.html里修改


 $(function () {

        var vm = new Vue({

            el: '#vm',

            data: {

                name: '',

                email: '',

                password1: '',

                password2: ''

            },

            methods: {

                submit: function (event) {

                    event.preventDefault();

                    var $form = $('#vm');

                    if (!this.name.trim()) {

                        return $form.showFormError('请输入名字');

                    }

                    if (!validateEmail(this.email.trim().toLowerCase())) {

                        return $form.showFormError('请输入正确的Email地址');

                    }

                    if (this.password1.length < 6) {

                        return $form.showFormError('口令长度至少为6个字符');

                    }

                    if (this.password1 !== this.password2) {

                        return $form.showFormError('两次输入的口令不一致');

                    }

                    var email = this.email.trim().toLowerCase();

                    $form.postJSON('/api/users', { #这里postJSON('/api/users')

                        name: this.name.trim(),

                        email: email,

                        passwd: CryptoJS.SHA1(email + ':' + this.password1).toString()

                    }, function (err, r) {

                        if (err) {

                            return $form.showFormError(err);

                        }

                        return location.assign('/');

                    });

                }

            }

        });

        $('#vm').show();

    });

@get('/register')

这个url是用来显示注册用户页面的.你github里没写错呀.


这个问题我自己找到了,问题就在那段js脚本中el,data,methods中的methods,我写成了method,这样就导致了没有调用到这个方法,还是自己太粗心了,哎呀,好气呀!


在昨天是终于把所有功能都实现了,在自己的电脑上也都跑通了,接下来还不打算部署,而是想好好总结一下,总结一下前段后端的所有流程、功能,还想画一个所有函数调用的流程图给后来人作为参考,到时可能还要请教你看我的想法是否是正确的;其次,把前段界面按照自己的想法风格改一下,现在的界面还都是和廖大教程里的一样,虽然是简洁,但是我丑起来我自己都害怕...最后,就是想重构一下,按照自己的想法把项目去耦合,再用flask实现一遍,这样这个东西就可以结束了。


在这短时间内,真是多谢谢你和@灰_手,经常会麻烦你们帮忙解答我的各种问题,多谢多谢!你最近复习教程后在做什么?接下来准备干嘛?(ps:你有新浪微博或是知乎的账号么?我关注你 。。。)


我复习到python实战第一天的时候,忽然心血来潮学起了廖大的javascript教程.看到DOM部分了.后边我想试试node.js. python的实战不知道什么时候才能绕回去.


哈哈,等你重构完实战项目,咱们node.js教程里见.


我的git有现成的flask框架搭建的后端,需要有些地方可以改进,不过还是可以用的。


flask和aiohttp都是web框架吧,我看到网上很多关于flask和Django的资料,aiohttp很少,这些框架有什么优劣吗?


在        #联调时一直出现如下错误    

        #self._encoding = charset_by_name(self._charset).encoding

        #AttributeError: 'NoneType' object has no attribute 'encoding'

        #原因竟然是把这里的utf8 写成了utf-8,卧槽!!

        charset=kw.get('charset', 'utf8'),

        autocommit=kw.get('autocommit', True),

        #默认最大连接数

        maxsize=kw.get('maxsize', 10),

        minsize=kw.get('minsize', 1),

        loop=loop

        此插入代码



day05难点,06,07简单,跑起来了。



本文转自 liqius 51CTO博客,原文链接:,如需转载请自行联系原作者

更多推荐

Day 5

本文发布于:2024-02-26 22:06:48,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1704113.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:Day

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!