使用蓝图(Blueprint)组织Flask应用

在一些重量级Web框架中(如Laravel/Django),框架中已经预置了整个框架的目录结构,在没有特殊需求的情况下通常不会改变它。在Flask这类轻量级框架中,理论上你可以把一个项目的全部代码都放进一个.py文件中。然而一旦项目规模稍大,就会变得庞大而难以管理。这时,我们需要蓝图(Blueprint)来帮助我们管理代码结构。

蓝图

什么是蓝图

Flask用蓝图(blueprints) 的概念来在一个应用中或跨应用制作应用组件和支持通用的模式。蓝图很好地简化了大型应用工作的方式,并提供给 Flask 扩展在应用上注册操作的核心方法。一个 Blueprint 对象与 Flask 应用对象的工作方式很像,但它确实不是一个应用,而是一个描述如何构建或扩展应用的蓝图 。

简而言之,我们可以将蓝图理解为Flask应用中的一个模块。

记第六届中国软件杯决赛

经过7月份的初赛,我们成功晋级了中国软件杯的决赛。9月4号,我们启程前往南京参加决赛。

关于比赛:大学生软件设计大赛官方网站—中国软件杯全国大学生软件设计大赛组委会主办
我们的赛题:安全可靠赛题2:分布式爬虫系统 – 六届赛题 – 大学生软件设计大赛官方网站—中国软件杯全国大学生软件设计大赛组委会主办

第一天 签到

9月4号,正好是学校开学第一天,我们一大早出发前往南京。第一周的课就先翘为敬了。

我们的车次是刚上线运行不久的复兴号,有车载WiFi,看了下IP应该是移动的4G,速度很良心。而且小桌板比和谐号更大,简直是为了车上赶代码量身定制的(误。

使用nginx unit部署微服务

前端时间,nginx发布了nginx unit。NGINX Unit 是一个动态的网络应用服务器,它被设计为可运行多种编程语言的。通过API可以轻巧,多面化的动态配置Unit。目前为止nginx unit支持php,python以及go。其他语言如java,javascript等语言尚未提供支持。

有了nginx unit,我们就可以不用配置繁琐的php-fpm和uwsgi,只需要一个json文件即可部署一个微服务。

NGINX Unit 现在是beta版本。你现在虽然可以使用它,但建议仅用于测试环境,不建议用于生产环境。

GitHub:https://github.com/nginx/unit/

中文文档:https://github.com/tuzimoe/unit/blob/master/README_zh-Hans.md

下面我们使用nginx unit部署一个django项目,以hyriamb/nem-downloader: django网易云音乐下载器,适用于新版api为例。

使用Jenkins持续集成Java应用

Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具。用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件持续集成。
在这学期的软件工程课设中,我们使用Jenkins持续构建、测试及部署一个Spring Boot应用。

安装Jenkins

在安装之前请确保已经安装了JDK和maven。
安装Jenkins有两种方法,一种是从官网下载打包好的.war包;或通过apt等包管理器安装。我的系统是Debian8,安装过程参考这篇文章即可,本文不再赘述。
How To Install Jenkins on Ubuntu 16.04 | DigitalOcean

使用Laravel和jwt-auth编写API

JSON Web Token(JWT)是一个轻量级的认证规范,它允许用户和服务器之间传递安全可靠的信息。

在传统的WEB应用中,服务端成功的返回一个响应于两件事。其一是通过一种存储机制保存会话信息(SESSION)。每一个会话都有它独特的信息,常常是一个长的,随机化的字符串,它被用来让未来的请求检索信息。其次,包含在响应头里面的信息使客户端保存了一个COOKIE。服务器自动的在每个子请求里面加上了会话ID,这使得服务器可以通过检索SESSION中的信息来辨别用户。
API应该被设计成无状态的。这意味着没有登录,注销的方法,也没有SESSION和COOKIE。因此,我们引入JWT。

使用jwt进行认证和授权

认证

客户端向服务器发送用于登录的用户名和密码,服务器将用户名和密码和数据库比对成功后,将一条形如eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ的token返回给客户端,客户端应在本地存储这个token。
这条token被.分为三个部分:头部(Header),载荷(Payload)和签名(Signature)。其中头部的algtyp两个字段定义了此token所使用的加密算法和token类型;载荷中包含有认证用户的信息;签名则将头部和载荷以及key用base64编码后计算SHA256。

授权

在同一个会话中,当客户端再次向服务器发送请求时,将token附带在请求头的Authorization字段中。服务器验证此token(是否有效,是否在有效期内等)后,即可对客户端授权。

dingo api是一个为Laravel和Lumen设计的用于更好地实现RESTful API的第三方库,并且兼容jwt-auth。下面我们使用dingo api和jwt-auth来编写一个登录/登录和认证的API。

安装

我们需要安装jwt-auth和dingo api
composer.json中加入如下两行

"require": {
    "dingo/api": "1.0.*@dev",
    "tymon/jwt-auth": "0.5.*"
}

执行安装

composer install

配置

首先配置jwt-auth
编辑config/app.php
providers中加入
'Tymon\JWTAuth\Providers\JWTAuthServiceProvider'

aliases中加入
'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth'

发布配置
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"

生成key
php artisan jwt:generate

接下来配置dingo api
providers中加入
Dingo\Api\Provider\LaravelServiceProvider::class

发布配置
php artisan vendor:publish --provider="Dingo\Api\Provider\LaravelServiceProvider"

创建路由

routes/api.php

$api = app('Dingo\Api\Routing\Router');

$api->version('v1', function ($api) {
    $api->group(['namespace' => 'App\Api\Controllers', function ($api) {
        $api->group(['prefix'=>'auth'], function($api) {
            $api->post('register', 'AuthController@postRegister');
            $api->post('login', 'AuthController@postLogin');
        });

        //在需要认证的路由加入api.auth中间件
        $api->group(['prefix'=>'test', 'middleware' => 'api.auth'], function($api) {
            $api->get('/test', 'TestController@test');
        });
    });
});

注册/登录

app/Api/Controllers/AuthController.php

//新用户注册
public function postRegister(Request $request){
    $this->validate($request, [
        'phone' => 'required|unique:users|max:36',
        'password' => 'required|max:64',
    ]);
    
    $payload = $request->all();
    
    //将新注册用户信息写入数据库
    try {
        $this->authRepo->registerUser($payload);
    }catch(\Exception $e){
        return response()->json(['status' => 0, 'message' => $e->getMessage()]);
    }
    //用已注册的信息登录
    return $this->postLogin($request);
}

//登录
public function postLogin(Request $request){
    $this->validate($request, [
        'phone' => 'required|max:36',
        'password' => 'required|max:64',
    ]);

    $credentials = $request->only('phone', 'password');

    try {
        if (! $token = JWTAuth::attempt($credentials)) {
            //用户名或密码不正确则返回错误信息
            return response()->json(['error' => 1000004, 'message' => '用户名或密码错误'], 401);
        }
    } catch (JWTException $e) {
        return response()->json(['error' => 1000005, 'message' => '创建token失败'], 401);
    }
    //如果一切正常则返回token
    return response()->json(['error' => 0, 'token' => $token], 200);
}

鉴权

在成功登录后,客户端会收到服务器返回的token。在token有效期内(默认是一个小时),客户端在请求需要鉴权的路由时,只需在HTTP请求头中加入名为Authorization,值为bearer +token的字段即可,如果token过期则需要重新登录。
在服务器上,api.auth中间件会自动进行token的鉴定。在Controller中,调用Auth::User()即可获得已认证的用户实例。
app/Api/Controllers/TestController.php

public function test(){
    $user = Auth::User();
}

参考资料

http://www.haomou.net/2014/08/07/2014_session/
https://github.com/dingo/api/wiki
https://github.com/tymondesigns/jwt-auth/wiki

Python异步任务队列

在开发的过程中,有时会需要用到类似下面的这些操作

  • 用户注册时发送认证邮件
  • 带有Web界面的爬虫
  • 定时计划任务

这些任务的共同特点是执行所需的时间较长,但是我们又不希望其阻塞后续的操作。因此我们将这些任务放进任务队列里来运行。

Python常见的异步任务队列实现有功能较丰富的Celery和轻量级的RQ,本文以celery为例。

Celery 是一个简单、灵活且可靠的,处理大量消息的分布式系统。
Celery经常和RabbitMQ同时提起,但实际上,RabbitMQ和celery并不是同一层面的东西。Celery需要存储介质来存储任务(称为broker),可选的broker有RabbitMQ, redis, mysql, mongodb等。

fabric.js裁剪图片对象

最近手上的一个项目使用fabric.js构建一个图片编辑器,裁剪图片是需求之一。虽然fabric.js提供了原生的裁剪功能,但是内置的裁剪功能目的在于将一个图片裁剪成特定的形状而边框不变(如下图),准确的说,这是一个遮罩,而不是裁剪。Google上能搜到的JSFiddle上几乎也都是使用的同样的方法,例如https://fiddle.jshell.net/filiperoberto/wLub3jau/,因此不符合我的需求。

Neuxs 6P的意外掉电故障

去年12月起,大量Nexus6P用户遇到了在电池仍有足够电量(60%-15%)时掉电并关机的情况。从上周开始,我的手机也毫无预兆地出现了同样的问题,而且比网上大多遇到同样问题的用户更严重,意外掉电发生时的电量为50%-80%不等。

Javascript代码同步化

我最近一直在写js。在这期间,我在群里提的最多的问题就是

函数a在函数b执行之前就执行了,我应该怎么办?

我有一个用python写的网易云音乐的下载器,我最近在试图把它用nodejs重写一遍。
它的逻辑非常简单。但是我写了一半就写不下去了,因为一层一层的回调嵌套,代码已经成了>形。

js一个重大的特点就是异步非阻塞,但是在一些情况下,下一步的操作需要依赖上一步的执行结果。这样就会有回调中再回调的情况出现。 当业务逻辑一复杂,回调的嵌套越来越多,可读性就会变差,维护起来也会很困难,这就是回调地狱。
node有很多第三方的模块用来将异步调用同步化,来解决这个问题。