REST 程序设计之http method 、http status

HTTP method

在rest程序设计中对资源都是通过URI来体现的,对资源的操作主要是通过Http请求的方法来实现的。API功能是由http method+ URI共同体现的。举例如下

  • GET /tasks - 获取任务列表信息
  • GET /tasks/12 -获取指定的任务信息
  • POST /tasks - 创建新的任务
  • PUT /tasks/12 - 更新指定的任务
  • PATCH /tasks/12 - 更新指定任务的部分信息
  • DELETE /tasks/12 - 删除指定的任务

以上中get 、post、put、delete比较常用。主要实现对资源的CURD。

HTTP status

作为 API 的设计者,正确的将 API 执行结果和失败原因用清晰简洁的方式传达给客户程序是十分关键的一步。 我们确实可以在 HTTP 的相应内容中描述是否成功,如果出错是因为什么, 然而, 这就意味着用户需要进行内容解析,才知道执行结果和错误原因。因此,HTTP 响应代码可以保证客户端在第一时间用最高效的方式获知 API 运行结果,并采取相应动作。 下表列出了比较常用的响应代码。

  • 200 响应成功。主要响应GET, PUT, PATCH 、DELETE.请求,同时也可以响应资源创建成功无返回结果的post请求
  • 201 资源成功创建,同时在location header 里面有新资源的 地址
  • 204 返回请求成功 不反返回内容,即返回结果不包含body,比如delete请求。
  • 301 重定向 , 请求的网页已被永久移动到新位置。服务器返回此响应时,会自动将请求者转到新位置。
  • 302 重定向 , 请求的网页临时移动到新位置,但求者应继续使用原有位置来进行以后的请求。302 会自动将请求者转到不同的临时位置。
  • 304 自从上次请求后,请求的网页未被修改过。服务器返回此响应时,不会返回网页内容。
  • 400 错误请求
  • 401 未授权 , 请求要求进行身份验证
  • 403 已禁止 , 服务器拒绝请求。
  • 404 未找到 , 服务器找不到请求的网页。
  • 405 方法禁用 , 禁用请求中所指定的方法。
  • 410 请求的页面不存在
  • 415 不支持的媒体类型
  • 429 请求次数过多
  • 500 内部服务器错误。

面向对象编程3大特性 7大原则[转]

原文地址

面向对象的特性

三个基本的特性:封装、继承与多态。

1、封装
面向对象编程核心思想这一就是就是将数据和对数据的操作封装在一起.通过抽象,即从具体的实例中抽取共同的性质形成一般的概念,比如类的概念.

2、继承
继承体现了一种先进的编程模式.子类可以继承父类的属性和功能,即子类继承了父类所具有的数据和数据上的操作,同时又可以增添子类独有的数据和数据上的操作.例如,”人类”继承了”哺乳类”的属性和功能,同时又增添了人类独有的属性和功能.

3、多态
多态是面向对象编程的又一重要特征.有两种意义的多态(表现为方法的重写(Overriding)和重载(Overloading))
一种是操作名称的多态,即有多个操作具有相同的名字,但这些操作所接收的消息类型必须不同.所谓操作名称的多态是指可以向操作传递不同消息,以便让对象根据相应的消息来产生一定的行为.

七大基本原则

单一职责原则SRP(Single Responsibility Principle)、开放封闭原则OCP(Open-Close Principle) 、替换原则、依赖原则(the Dependency Inversion Principle DIP) (the Liskov Substitution Principle LSP) 、接口分离原则(the Interface Segregation Principle ISP)、“迪米特”法则 、组合/聚合原则

1、单一职责原则SRP(Single Responsibility Principle)
即一个类只负责一项职责
可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;
提高类的可读性,提高系统的可维护性;
变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。
需要说明的一点是单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则。

2、开放封闭原则OCP(Open-Close Principle)
一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的。比如:一个网络模块,原来只服务端功能,而现在要加入客户端功能,
那么应当在不用修改服务端功能代码的前提下,就能够增加客户端功能的实现代码,这要求在设计之初,就应当将服务端和客户端分开,公共部分抽象出来。

3、里氏替换原则(the Liskov Substitution Principle LSP)
子类应当可以替换父类并出现在父类能够出现的任何地方。

4、依赖倒转原则(the Dependency Inversion Principle DIP)
具体依赖抽象,上层依赖下层。假设B是较A低的模块,但B需要使用到A的功能,
这个时候,B不应当直接使用A中的具体类: 而应当由B定义一抽象接口,并由A来实现这个抽象接口,B只使用这个抽象接口:这样就达到
了 依赖倒置的目的,B也解除了对A的依赖,反过来是A依赖于B定义的抽象接口。通过上层模块难以避免依赖下层模块,假如B也直接依赖A的实现,那么就可能造 成循环依赖。一个常见的问题就是编译A模块时需要直接包含到B模块的cpp文件,而编译B时同样要直接包含到A的cpp文件。

5、接口分离原则(the Interface Segregation Principle ISP)
模块间要通过抽象接口隔离开,而不是通过具体的类强耦合起来。即面向接口编程

6、“迪米特”法则
又叫最少知识原则,就是说,一个对象应当对其他对象有尽可能少的了解
① 在类的划分上,应该创建有弱耦合的类;
② 在类的结构设计上,每一个类都应当尽量降低成员的访问权限;
③ 在类的设计上,只要有可能,一个类应当设计成不变类;
④ 在对其他类的引用上,一个对象对其它对象的引用应当降到最低;
⑤ 尽量降低类的访问权限;
⑥ 谨慎使用序列化功能;
⑦ 不要暴露类成员,而应该提供相应的访问器(属性)。

7、.组合/聚合原则
又叫合成复用原则。原则就是在一个新的对象里面通过关联关系(包括组合关系和聚合关系)使用一些已有的对象,使之成为新对象的一部分,新对象通过委派调用已有对象的方法达到复用其已有功能的目的。也就是,要尽量使用类的合成复用,尽量不要使用继承。
就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分,新的对象通过向这些对象的委派达到复用已有功能的目的。这个原则有一个简短的描述:要尽量使用合成、聚合,尽量不要使用继承。

1) 新对象存取成分对象的唯一方法是通过成分对象的接口

2) 这种复用是黑箱复用,因为成分对象的内部细节是新对象所看不到的

3) 这种复用可以在运行时间内动态进行,新对象可以动态的引用与成分对象类型相同的对象

4) 合成、聚合可以应用到任何环境中去,而继承只能应用到一些有限环境中去

5) 导致错误的使用合成、聚合与继承的一个常见原因是错误的把“Has-a”关系当作“Is-a”关系。如果两个类是“Has-a”关系那么应使用合成、聚合,如果是“Is-a”关系那么可使用继承

在面向对象设计中,可以通过两种基本方法在不同的环境中复用已有的设计和实现,即通过组合/聚合关系或通过继承。

1) 继承复用:实现简单,易于扩展。破坏系统的封装性,从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性;只能在有限的环境中使用。(“白箱”复用 )

2) 组合/聚合复用:耦合度相对较低,选择性地调用成员对象的操作;可以在运行时动态进行。(“黑箱”复用 )

组合/聚合可以使系统更加灵活,类与类之间的耦合度降低,一个类的变化对其他类造成的影响相对较少,因此一般首选使用组合/聚合来实现复用;

其次才考虑继承,在使用继承时,需要严格遵循里氏代换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用。

css inline

今天使用要为元素设置水平居中的时候发现 margin:0 auto 不起作用,查资料发现原来我给div设置了的是disply:inline-block属性,这种情况下 margin:0 auto 是无效的,于是通过给父元素div 设置了 text-aglin:center得以解决.然后搜集了一些资料,加深一下对inline 属性的了解。

测试结果

  1. inline元素, 有效的: background, margin-left, margin-right, padding, border
  2. inline元素, 无效的: width, height, margin-top, margin-bottom (个别除外)
  3. 因width, height无效, 因此下列属性也无效: text-align, text-indent, min|max-width|height, overflow
  4. 原文字相关的依然有效, 如word-spacing, word-wrap, white-space, letter-spacing
  5. 下面两个有效, line-height, vertical-align, box-shadow
  6. float,position及与之相关top,left, 不考虑, 已经不是inline了
  7. img, button, input, label, select, textarea 设置inline与inline-block, 效果是一样的, 没有变化, 都等同于设置了inline-block

思考:

  1. inline元素的宽高是不能定义, 即宽高度是由inline元素inline内容的元素决定的,(ie6,ie7下高度会受inline-block元素的影响)
  2. 即宽高完全是自适应而得出的, 因此也不存在overflow的问题, min|max-width|height也没有影响
  3. margin四个属性, left|righttop|bottom有不同的待遇
  4. margin-top, margin-bottom, 可以用line-height模拟, 但有很大不同, 与inline-block也不兼容, 另 margin:0 auto 的居中效果, 也没有作用

最后:

mdn中说: 默认 inline-level 的元素 inline + inline-block

  • b, big, i, small, tt
  • abbr, acronym, cite, code, dfn, em, kbd, strong, samp, var
  • a, bdo, br, img, map, object, q, script, span, sub, sup
  • 表单元素:button, input, label, select, textarea 与inline-block等同

ECMASCRIPT 5 Object.keys

问题提出 我要获取一个对象包含属性的个数

在我们使用Javascript的过程中、我们经常把Object当做散列或者Map来使用。有时候我想知道一个对象当前存了多少东西,假设我要分析一下 图书馆里图书的数据 这里有个作者与对应图书的集合 ,这个对象包含了几项内容:如下

var bookAuthors = {
    "Farmer Giles of Ham": "J.R.R. Tolkien",
    "Out of the Silent Planet": "C.S. Lewis",
    "The Place of the Lion": "Charles Williams",
    "Poetic Diction": "Owen Barfield"
};

作为分析的一部分,我们像一个API发送图书作者的数据,不幸的是 这个API只能接受一个包含100本左右的图书对象,于是我们需要向一个号的解决方案,在向API发送之前想告诉它一共包含了多少本书。

解决方案,使用for in 变量所有属性,hasOwnProperty判断是不是属于当前对象本身的

于是最初的设计可能如下

function countProperties (obj) {
    var count = 0;

    for (var property in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, property)) {
            count++;
        }
    }

    return count;
}
var bookCount = countProperties(bookAuthors);
// Outputs: 4
console.log(bookCount);

之所以使用hasOwnProperty 是因为 for in 不但会对象本身的属性,同时也会遍历继承而来的属性 判断

Object.keys

当然这种 方案确实是可行的,那么如果能避免手工的计算属性的个数岂不是会更好,Javascript能不能直接获取呢。在ES5 中Object 有了一个 keys 属性是专门获取 对象属性的name并且返回一个数组。这里只返回这个对象本身的属性 不包含继承过来 ,作用等同于

  for (var property in obj) {
     if (Object.prototype.hasOwnProperty.call(obj, property)) {                      
    }
}

如果要获取一个对象属性的个数 获取Keys之后,通过数组的length属性就可以获取了。值得一提的是Object.keys(obj) ,只能通过这种方式,他是Object对象中的方法(有点是静态的意思),并不是在Object.prototype上的 因此我们自己定义的对象时没有这个方法的,所以正确的使用方式如下

var bookCount = Object.keys(bookAuthors).length;
// Outputs: 4
console.log(bookCount);

错误的使用方式

var bookCount = bookAuthors.keys().length; //会报错的,自己声明的对象时不具有这个方法的

其他-Object.getOwnPropertyNames

同时也是在ES5中 有个类似的方法 Object.defineProperties,两者也稍有区别

Object.keys 以及Object.getOwnPropertyNames()用来获取对象中的属性的name,不包括继承来的属性,两者区别是Object.keys 只能获取可遍历的属性的名字的数组。举个例子

 var person = {};
Object.defineProperties(person, 
    {
        "name": {value: 'Cody Lindley',enumerable: true},
        "age": {value: 38,enumerable: false}
    }
);
//只返回可以可遍历的属性 
console.log(Object.keys(person)); //logs ["name"]
//return array of all properties, including ones that are non-enumerable
console.log(Object.getOwnPropertyNames(person)); //returns ["name", "age"]

ployFill 为不支持ES5的浏览器提供支持

以下摘自es5-shim

   var hasDontEnumBug = true,
        hasProtoEnumBug = (function () {}).propertyIsEnumerable('prototype'),
        dontEnums = [
            "toString",
            "toLocaleString",
            "valueOf",
            "hasOwnProperty",
            "isPrototypeOf",
            "propertyIsEnumerable",
            "constructor"
        ],
        dontEnumsLength = dontEnums.length;
    for (var key in {"toString": null}) {
        hasDontEnumBug = false;
    }
    Object.keys = function keys(object) {
        var isFunction = _toString(object) === '[object Function]',
            isObject = object !== null && typeof object === 'object';

        if (!isObject && !isFunction) {
            throw new TypeError("Object.keys called on a non-object");
        }

        var keys = [],
            skipProto = hasProtoEnumBug && isFunction;
        for (var name in object) {
            if (!(skipProto && name === 'prototype') && owns(object, name)) {
                keys.push(name);
            }
        }
        if (hasDontEnumBug) {
            var ctor = object.constructor,
                skipConstructor = ctor && ctor.prototype === object;
            for (var i = 0; i < dontEnumsLength; i++) {
                var dontEnum = dontEnums[i];
                if (!(skipConstructor && dontEnum === 'constructor') && owns(object, dontEnum)) {
                    keys.push(dontEnum);
                }
            }
        }
        return keys;
    };

}

Phonegap创建移动应用

前段时间学习了Phonegap开发,本篇作为记录,希望对初道者有帮助,本篇主要介绍Phonegap以及环境的搭建。下篇结合Angluar 开发小Demo

Phonegap介绍

Phonegap 作为一个开源的框架来帮助开发者使用Javascript/HTML5/CSS 开发跨平台的移动应用。他可以根据指定的移动平台打包为对应的包,支持的平台种类如下。

  • iOS
  • Android
  • Windows 8
  • Windows Phone 7 and 8
  • BlackBerry 5.x+
  • WebOS
  • Symbian
  • Tizen
  • Firefox OS(3.4版本开始支持的)

插件

Phonegap 本质上是在本地原生应用程序中包裹了一个Webview 来支持使用HTML5 进行开发。但是这种方式本质上是不支持访问设备的功能,比如访问文件系统,或者照相机,但是Phonegap通过桥接机制允许Webview中的Javascript 来调用原生的代码,于是Phonegap就拥有了许多插件来支持访问设备API比如

  • 加速度计
  • 相机
  • 联系人
  • 文件系统
  • 媒体文件的播放 以及录音
  • 网络
  • 存储

同时Phonegap 可以允许开发者通过扩展插件来访问更多的设备API比如

  • 二维码扫描
  • 蓝牙
  • 推送通知
  • 语音合成
  • 日历

在之前版本的Phonegap中 包括了一些预编译的插件。随着Phonegap 3的到来,一个新的插件架构取代了原来的插件访问。同时有一个注册中心来创建兼容Phonegap 3的插件

HTML/CSS /Javascript 移动开发基础。

Phonegap本身提供的大部分功能是提供了一些非可视化的功能组件,比如访问文件系统,网络能力,地理定位,等等。Phonegap本身并没有提供构建图形界面的能力,对于此,我们必须使用HTML、CSS来构建,为了更方便地开发中应用,因此推荐结合JS 框架来进行组织程序的开发。这些框架包括

  • jQuery Mobile
  • Ionic
  • Sencha Touch
  • Kendo UI Complete
  • AppGyver’s Steroids
  • Enyo
  • TopCoat

jQuery Mobile 这个就不用说了,简单理解Jquery的移动版本,AngluarJS 是一个比较火的MMV框架,Ionic也是AngluarJS构建的,这里我采用的是AngluarJs。开发之前还要了解一下面的这个问题。

####PhoneGap Or Cordova的关系

在学习Phonegap的过程中,我们经常会碰到Cordova 这个词,事实上,我们使用命令行工具创建的Phonegap工程其实是Cordova,。Cordova是贡献给Apache后的开源项目,是从PhoneGap中抽出的核心代码,是驱动PhoneGap的核心引擎。Cordova提供了一组设备相关的API,通过这组API,移动应用能够以JavaScript访问原生的设备功能,如摄像头、麦克风等。
Cordova还提供了一组统一的JavaScript类库,以及为这些类库所用的设备相关的原生后台代码。你可以把他想象成类似于Webkit和Google Chrome的关系。在大部分情况下这两个名字是可以交换的,随着Phonegap作为Adobe的商标,这个开源的项目需要一个新的名字,因此就有了Cordova,Phonegap继续作为Adobe的商业产品,大部分仍然将这个整体的项目称为Phonegap。

Phonegap 开发环境搭建。

这里我要基于Phonegap开发能够运行在android上的程序。

建环境的基本准备:

  1. java JDK、配置Java环境变量
  2. 下载android ADT Bundle(里面是一个继承了安卓开发环境的Eclipse),下载之后将android SDK下的tools的目录添加到环境变量中,启动下载包里面的eclipse 创建AVD
  3. Ant 打包工具(windows 下载推荐下载 winant)
  4. NodeJS
  5. 安装phonegap

    npm install -g cordova
    

    或者

    npm install -g phonegap
    

    cordova和phonegap这两个命令是通用的。npm install -g phonegap 是3.0之后的新出的一种安装方式,这里我使用是phonegap。

创建Helloword

在命令行下运行

phonegap create my-app
cd my-app
phonegap run android

如果有android 设备连接了电脑的话,这时候会自动将应用部署到设备上,如果没有设备的话,会自动启用刚才创建的模拟器(注意 前面配置过程中一定要把android SDK下的tool 目录添加到环境变量中,并且确保已经创建了模拟器(AVD))到此为止一个Phonegap 版本的Hello World 已经开发完毕。

下面看进入my-app的目录,进入之后我们发现有5个文件夹

  • www : 文件夹 开发的 HTML5 ; CSS ; JS 文件都拷贝到这下面,以后我们开发HTML5的应用主要在这个文件夹下进行
  • plugins : 文件夹 存放的是phonegap插件比如文件,摄像头等插件都下载到这里。
  • merges : (这个我还不了解)
  • platforms : 文件夹 存放的是编译好后的android文件,(如果这个文件夹为空,需要你在命令行编译一次才能生成. 如上面 phonegap run android)
  • .cordova : 存放的是配置文件 。

将这个工程 导入到 Eclipse 下可以查看更多的工程信息。

如何使用Node+express创建Rest API

安装环境

1 首先确保你的机器上已经安装了NodeJS环境,如果没有安装的话 点击此处下载
2 安装 express

npm install express -g

3 创建express 项目

express myapp

API设计

这里只是实现一个基本的计划任务列表,一个计划包含的信息有 名字,描述,截至日期,任务状态,以及一个唯一的标示符,API使用JSON作为数据传输的格式,下面具体描述下API的功能。

  • GET /tasks/ 返回计划任务列表
  • GET /tasks/:id 根据指定的任务id 返回计划任务的信息
  • POST /tasks/ 接受传进来的JSON数据来创建一个新的计划任务
  • PUT /tasks/:id 更新计划任务信息
  • DELETE /tasks/:id 根据指定的计划Id 删除计划任务

这些方法的返回如下

  • 访问get请求之外的api ,如果执行成功返回http 状态符为200
  • 如果访问不存的计划信息 则返回http 状态符为 404

实现

数据

这里仅是为了展示如何创建 Web api,因此尽可能简化每一个步骤,这个例子中计划内容都是一个Javascript数组存储在内存中的,我仅仅是创建了一个TaskRepository 创建了一个简单的javascript数组,TaskRepository的接口描述如下:

function TaskRepository() {}
/**
 * Find a task by id
 * Param: id of the task to find
 * Returns: the task corresponding to the specified id
 */
TaskRepository.prototype.find = function (id) {}
/**
 * Find the index of a task
 * Param: id of the task to find
 * Returns: the index of the task identified by id
 */
TaskRepository.prototype.findIndex = function (id) {}
/**
 * Retrieve all tasks
 * Returns: array of tasks
 */
TaskRepository.prototype.findAll = function () {
    return this.tasks;
}
/**
 * Save a task (create or update)
 * Param: task the task to save
 */
TaskRepository.prototype.save = function (task) {}
/**
 * Remove a task
 * Param: id the of the task to remove
 */
TaskRepository.prototype.remove = function (id) {}

创建express server

var express = require('express');
var app = express();
app.configure(function() {
    app.use(express.bodyParser()); // used to parse JSON object given in the request body
});

获取计划的列表 get

app.get('/tasks', function (request, response) {
    response.json({tasks: taskRepository.findAll()});
});

根据指定的id获取一个计划的信息 get

app.get('/tasks/:id', function (request, response) {
    var taskId = request.params.id;
    try {
        response.json(taskRepository.find(taskId));
    } catch (exeception) {
        response.send(404);
    }
});

创建一个新的计划 post

app.post('/tasks', function (request, response) {
    var task = request.body;
    taskRepository.save({
        title: task.title || 'Default title',
        description: task.description || 'Default description',
        dueDate: task.dueDate,
        status: task.status || 'not completed'
    });
    response.send(200);
});

更新计划信息 put

app.put('/tasks/:id', function (request, response) {
    var task = request.body;
    var taskId = request.params.id;
    try {
        var persistedTask = taskRepository.find(taskId);
        taskRepository.save({
            taskId: persistedTask.taskId,
            title: task.title || persistedTask.title,
            description: task.description || persistedTask.description,
            dueDate: task.dueDate || persistedTask.dueDate,
            status: task.status || persistedTask.status
        });
        response.send(200);
    } catch (exception) {
        response.send(404);
    }
});

删除计划delete

app.delete('/tasks/:id', function (request, response) {
    try {
        taskRepository.remove(request.params.id);
        response.send(200);
    } catch (exeception) {
        response.send(404);
    }
});

启动express server

app.listen(8080)

测试 这里使用 curl 执行http请求访问rest api

首先启动程序

node rest_api.js

最初的情况下访问计划列表应该空的

$ curl -i http://localhost:8080/tasks/
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 17
Date: Sun, 10 Feb 2013 12:37:45 GMT
Connection: keep-alive

{
  "tasks": []
}

创建计划

$ curl -i -X POST http://localhost:8080/tasks --data '{}' -H "Content-Type: application/json"
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/plain
Content-Length: 2
Date: Sun, 10 Feb 2013 12:39:13 GMT
Connection: keep-alive

OK

更新计划

$ curl -i -X PUT http://localhost:8080/tasks/1 --data '{"description":"blabla"}' -H "Content-Type: application/json"
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/plain
Content-Length: 2
Date: Sun, 10 Feb 2013 12:42:39 GMT
Connection: keep-alive
OK

删除计划

$ curl -i -X DELETE http://localhost:8080/tasks/1
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/plain
Content-Length: 2
Date: Sun, 10 Feb 2013 12:43:31 GMT
Connection: keep-alive

OK

这里介绍了用express创建rest api的大致过程,而实际过程中要复杂一些,比如存储数据使用数据库(mongodb 或者传统的关系数据库)来解决,有的也会包括一些复杂的逻辑信息。

在django中使用postgresQL

最近开始做的东西要用到Django,数据库是postgresQL,需要在Django的setting.py中修改相关数据库的配置文件。事先我已经装好了PostgresQL 9.2.6

下载python连接postgresQL数据库的驱动

安装方式也有多种:根据个人喜好可以使用easy_istall或者pip方式安装

  • easy_install psycopg2
  • pip install psycopg2

或者自己下载http://initd.org/psycopg/解压之后,进入目录运行

python setup.py install

会自动安装到Python安装目录的site-packages的目录下

这里我直接在windows安装的exe 下载地址(如果是64位的系统的话也建议下载32位的)http://www.stickpeople.com/projects/python/win-psycopg/ 根据数据库的版本选择合适的进行下载

修改配置文件setting.py,找到DATABASES处

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'mydb',                      
        'USER': 'postgres',
        'PASSWORD': '1',
        'HOST': 'localhost',
        'PORT': '5432'
    }
}

将ENGINE修改为django.db.backends.postgresql_psycopg2,NAME为数据库的名字,USER为数据库的用户名,PASSWORD为数据库用户对应的密码,HOST为数据库所在的主机名,Port为PostgreSQL的端口号。

参考资料https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-DATABASES

跨域解决方案之JSONP,CORS

何为跨域

ajax出现,能够让页面无刷新的同时调用服务,浏览器出于安全方面的考虑,在进行Ajax 访问的时候不允许XMLHttpRequest跨域调用其他页面的对象。这里指的就是不允许XMLHttpRequest跨域调用服务,所谓跨域是指如果服务的URL中协议 hostname、端口号与当前页面的这三个参数中有一个不同的话则视为跨域,如下情况

如上的各种组合还有好多情况,那么我们如何才能绕过浏览器的跨域限制 正常地访问服务呢。

在flash或者silverlight中,只要服务的页面下放入 跨域策略文件就可以允许不同域下flash/silverlight的页面来调用,内容大致如下

<cross-domain-policy> 
<allow-access-from domain="*" to-ports="507" /> 
<allow-access-from domain="*.example.com" to-ports="507,516" /> 
<allow-access-from domain="*.example2.com" to-ports="516-523" /> 
<allow-access-from domain="www.example2.com" to-ports="507,516-523" /> 
<allow-access-from domain="www.example3.com" to-ports="*" /> 
</cross-domain-policy> 

关于Flash 的跨域情况 在这里不说解释,本文重点介绍Javascript的情况。

JSONP

虽然浏览器默认禁止了跨域访问,但并不禁止在页面中引用其他域的JS文件,并可以自由执行引入的JS文件中的function(包括操作cookie、Dom等等)。根据这一点,可以方便地通过创建script节点的方法来实现完全跨域的通信。这种方式叫JSONP解决方案。

利用动态的script来请求服务,服务端返回函数的调用的形式,比如callback(“hello world”);前端如果实现已经定义好了一个callback 的函数既可以获得helloworld;下面开始具体实现。

服务端使用NodeJS+express实现。新建express项目,app.js如下

var express = require('express');
var http = require('http');
var path = require('path');
var app = express();
app.set('port', process.env.PORT || 3000);
app.set('port1', process.env.PORT || 3002);
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')))

        function add(a,b){
            return a+b;
        } 
app.get('/add', function(req,res){
    var a=parseInt(req.query.a);
    var b=parseInt(req.query.b);
     res.send("addresult("+add(a,b)+")");
});

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});
http.createServer(app).listen(app.get('port1'), function(){
  console.log('Express server listening on port ' + app.get('port1'));
});

为了方便演示,这里的跨域具体是指端口号不同,并且在一个文件里面开了两个server,一个3000端口,一个3002端口,这里的服务主要是实现一个加法的功能,读取两个参数,然后进行加法运算,最后返回结果用addresult包起来,比如前端传入过来的参数分别为a=3,b=7,则返回的参数为addresult(7) app.use(express.static(path.join(__dirname, 'public')))是将当前目录下的public 文件件配置为静态目录,接下来在public文件夹下新建crossdomain.html文件,关键代码如下。

var   $=function(id){
               return document.getElementById(id);
   }                
       var a=$('a').value;
       var b=$('b').value;
       var querystring="a="+a+"&b="+b;
       var script=document.createElement('script');
       script.async=true;
       script.src="http://localhost:3000/add?"+querystring;
       document.body.appendChild(script);

以上$为自定义的函数,根据ID 获取元素,然后从文本框中读取值。作为服务的参数传入。接下来要定义一个名字为addresult的函数来接受返回结果:

function addresult (data) {
    // body... 
    $("result").innerHTML=a;
}

启动程序输入 http://localhost:3002/crossdomain.html,这里是在3002上的页面调用的是3000端口上的服务。视为跨域,运行之后在文本框中输入两个值我们发现实现了跨域调用。
以上是实现了一个JSONP的精简版。当然还有些问题要考虑,比如回调函数名字的问题也可以作为参数来传入。第二个问题就是要在服务执行完毕或者请求失败之后 删除动态创建的script标签。

CORS

随着HTML5标准的 不断完善,也推出了一种新的跨域解决方案Cross-Origin Resource Sharing (CORS),现在已经是W3C指定的标准。允许 XMLHttpRequest 进行跨域调用,支持CORS的浏览器有

  • Chrome 3+
  • Firefox 3.5+
  • Opera 12+
  • Safari 4+
  • Internet Explorer 8+ (IE 8下为 XDomainRequest对象)

这里需要服务端和客户端配合 ,要求服务端返回的返回头中必须包含:

Access-Control-Allow-Origin: //发起请求的XMLHttpRequest所在的域
Access-Control-Allow-Methods: //支持的请求的方法
Access-Control-Allow-Credentials:true

同时在请求服务的时候需要将xhr的withCredentials设置为true;如

var xhr = new XMLHttpRequest();
xhr.open("get", "url", true);
xhr.withCredentials = true; //设置这个参数是为了允许cookie发送

这里请求的时候会发两次http请求,第一次是option请求,判断服务端时候只是CORS 以及支持哪些行为(post,get,put等)如果满足条件话则继续进行服务请求,同样这里也以NodeJS为例写个服务端。代码如下

function multiply(a,b){
        return a*b;
}
app.get("/multiply",function(req,res){
    console.log(res.query);
    var a=parseInt(req.query.a);
    var b=parseInt(req.query.b);
    res.set({
              'Access-Control-Allow-Origin': req.headers.origin,
              'Access-Control-Allow-Credentials': true,
              'Access-Control-Allow-Methods':'POST, GET, PUT, DELETE, OPTIONS'
            });
});

客户端的带代码如下

    var a=$('a').value;
    var b=$('b').value;
    var querystring="c="+a+"&d="+b;
    var xhr = new XMLHttpRequest();
        xhr.open("get", "http://localhost:3000/multiply?"+querystring, true);
        xhr.withCredentials = true;//经测试这个貌似没有什么作用
        xhr.onload =function(){
        var data = xhr.responseText;
        $("result").innerHTML="这两个数的乘积为"+data;
    }
xhr.send();  

其他的跨域解决方案。还有比如window.name,iframe,img标签等方式

当然使用代理也是比较给力的,所谓代理就是相当于请求先发到当前页面所在的webserver上,在webserver上的编写一个程序转发请求到另一个本来要打算访问的服务器上,同时也可以将结果返回到浏览器端,这样浏览器就间接地访问了服务。同时代理 又分为 正向和反向两部分。。。。。额,话题扯远了。。

本博文的用到的Demo的源码

HTML5 Geolocation API-获取设备的地理位置

地理位置API介绍

地理位置API能够神奇地定位出你正待在世界的什么地方,并且在你允许的情况下,将你的位置信息分享给你信任的人,关于内部的定位原理,主要通过三种手段:

  1. IP地址
  2. GPS定位
  3. MAC地址
  4. GSM基站网络
  5. 用户定义的地址位置。

地理位置获取流程:

1、用户打开需要获取地理位置的web应用。
2、应用向浏览器请求地理位置,浏览器弹出询问窗口询问用户是否共享地理位置。
3、假设用户允许,浏览器从设别查询相关信息。
4、浏览器将相关信息发送到一个信任的位置服务器,服务器返回具体的地理位置。

监测浏览器支持情况

如果浏览器支持地理位置API话,在全局的 navigator对象上回有一个名字为geolocation的属性,反之,navigator 对象上该属性为undefined 。于是可以编写如下函数

function suport_geolocation(){
    return !!navigator.geolocation
}

或者使用一个专门监测HTML5 特性的库Modernizr,提供的方法来监测浏览器是否支持地理位置API,如下

if(Modernizr.geolocation){

}
else{

}

getCurrentPosition函数

这个函数是通过navigator.geolocation对象来调用的,所以在脚本中需要先取得此对象。这个函数接受一个必选参数和两个可选参数。

void getCurrentPosition(

        in PositionCallback successCallBack,
        in optional PositionErrorCallback errorCallback,
        in optional PositionOptios options
);

. successCallBack:必选参数,为浏览器指明位置数据可用时应调用的函数,即收到实际位置信息并进行处理的地方。
. errorCallback:可选参数,出错处理
. options:可选参数,用来调整HTML5 Geolocation服务的数据收集方式。

示例

navigator.geolocation.getCurrentPosition(updateLocation,handleeLocationError);

这里updateLocation就是接收位置信息并进行重的函数,handleeLocationError是进行错误处理的函数。updateLocation只接受一个参数:位置对象。这个对象包含坐标(coords)和一个获取位置数据时的时间戳。以下是坐标的主要特性:

  • latitude(纬度)
  • longitude(经度)
  • accuracy(准确度)

    function updateLocation(position) {
            var latitude = position.coords.latitude;
            var longitude = position.coords.longitude;
    
            if (!latitude || !longitude) {
                document.getElementById("status").innerHTML = "HTML5 Geolocation is supported in your browser, but location is currently not available.";
                return;
            }
    
            document.getElementById("latitude").innerHTML = latitude;
            document.getElementById("longitude").innerHTML = longitude;
        }
    

    handleeLocationError()函数

HTML5定义了一些错误编号

  • PERMISSION_DENIED(错误编号为1)——用户选择拒绝浏览器获得其位置信息。
  • POSITION_UNAVAILABLE(错误编号为2)——尝试获取用户位置数据失败。
  • TIMEOUT(错误编号为3)——设置了可选的timeout值,获取用户位置超时。

    function handleeLocationError(error)
       {
           switch (error.code) {
               case 0: alert(error.message); break;
               case 1: alert(error.message); break;
               case 2: alert(error.message); break;
               case 3: alert(error.message); break;
           }
       }
    

options:可选的地理定位请求特性

  • enableHighAccuracy:如果启用该参数,则通知浏览器启用HTML5 Geolocation服务的高精度模式,参数的默认值为false.
  • timeout:可选值,单位为ms,告诉浏览器计算当前位置所允许的最长时间。默认值为Infinity,即为无穷大或无限制。
  • maximumAge:这个值表示浏览器重新计算位置的时间间隔。它也是一个以ms为单位的值,默认为零,这意味着浏览器每次请求时必须立即重新计算位置。

watchPosition函数

navigator.geolocation.watchPosition(updateLocation,handleLocationError);

监听位置的变化,如果位置发生变化则调用updateLocation函数,如果程序不再需要接收用户的位置信息,则可以调用navigator.geolocation.clearWatch(watchId),例子如下

var watchId = navigator.geolocation.watchPosition(updateLocation, handleeLocationError);
navigator.geolocation.clearWatch(watchId);

很多情况下,获得的位置在地图上在来展示,可视化地展示出我们所在的位置,参见demo

javascript bind function

记得半年之前在微博上看到一个问题,有人问如何才能证明自己真正理解了Javascript,有大神回答,最起码要能明白并写出bind 函数。当然这里的Javascript 仅仅是指的ECMASCRIPT本身,并不包含浏览器相关的那些。那么这篇博客这里就以bind开始吧。
首先搬出三个概念、

  • 作用域:表示变量或函数起作用的区域,作用域链-Javascript的作用域链也是按有顺序查询的。在访问变量时,先查看本地作用域是否有此变量,如果没有则依次向上一级作用域查找直到全局作用域。如果全局作用域也没有此变量,那么将会在全局作用域中声明这个变量;
  • 上下文:又可以理解为上下文对象,表示当前代码执行时所处的环境。即是this变量所指代的对象;
  • 闭包:表示能访问和操作其他本地作用域的变量的表达式(通常是函数);

在Javascript中this关键字是非常重要的,我之前也花费了不少时间来理解Javascript中的this是如何工作的。每个函数都有一个this变量,简单来说函数this是一个指针,它指向是不固定的,而是动态的,指向调用这个函数的对象,而这个对象就是上下文。我们都知道,Javascript里面的有作用域的概念,就是变量能够访问到的范围,this则是指向当前作用域的。通过this访问变量的时候会在当前作用域上查找。这篇博文是关于bind函数的,这个也是我们经常用的一个东西。

提出问题

说起Javascript,很难避免的提到了闭包,每个闭包拥有自己的作用域以及this 变量闭包里面的this 并不是指向原来的对象。例如

var getUserComments = function(callback) {
    var numOfCommets = 34;
    callback({ comments: numOfCommets });
};
var User = {
    fullName: 'kunkun',
    print: function() {
        getUserComments(function(data) {
            console.log(this.fullName + ' made ' + data.comments + ' comments');
        });
    }
};
User.print();

这里假设getUserComments 是一个异步的函数,通过一个回调函数来接受处理getUserComments返回的结果,在User对象里面我传入了一个匿名函数-闭包(即刚才说的回调的函数),执行完毕的结果如下。

undefined made 34 comments

可以看出这里的在调用print的时候。在刚才的回调函数里面this.fullName取值为undefined,可以看出此时这里面this 不再指向User对象了(其实是指向全局对比如在浏览器环境下的window);但是我期望的返回结果为
kunkun made 34 comments可以如下解决。

var that= this;
getUserComments(function(data) {
    console.log(that.fullName + ' made ' + data.comments + ' comments');
});

在执行print的时候,由于print是由User来调用的因此,print 里面的 this 是指向的User对象的,于是把this 赋值给力that ,所以that 这里保存了User的地址。所以在调用fullname的时候 ,就是User作用域下的fullname。

但是这种方法感觉有点混乱,同时也要定义像that这样新的变量,如果这样的情况出现多次的话,难免造成迷糊的。那么如何提前给这个函数固定好作用域呢。

设置函数的作用域

这里提供了一种方式来设置函数的作用域:使用apply 或者call函数,这两个方法是在Function.prototype里面定义的,因此任何一个函数都继承了这两个方法,apply和call的第一个参数就是要给方法绑定的对象(即该方法调用时this指向的对象)。下面给定义一个getLength方法来获取fullname的length

var getLength = function() {
    console.log(this.fullName.length);
}
getLength.apply(User);

结果是这种方式的输出结果是6,说明我的目的是达到了,如果我们直接调用getLength方法呢,getLength()则会返回爆出异常,这种情况下 getLength是在最外层定义的,因此调用的时候this指向的是全局对象,但是全局对象中没有fullName这个属性,因此此时this.fullName为undfined,那么this.fullName.length则相当于执行了undfined.length,这是必然要爆出异常的。

以上使用apply调用的时候 getLength也自然地调用了,那么如何给getLength指定好上下文,而不让他调用的。等以后调用的时候函数里面的this 则指向我们自己设定的对象。我们不妨把这个功能称之为 bind。

原生的Function.prototype.bind

如上提到的一样apply和call一样,他们都是Function.prototype对象中的方法,这个是ES5里面新增的方法。因此当前主流的浏览器都是支持的包括Chrome, Firefox, Opera,safari 其中IE 要在8以上。

在此将上面的代码修改如下

var User = {
    fullName: 'kunkun',
    print: function() {
        getUserComments(function(data) {
            console.log(this.fullName + ' made ' + data.comments + ' comments');
        }.bind(this));
    }
};
User.print();

在上面绑定的时候调用bind(this)的时候, 在print里面的,此时print是由User调用的,因此的this指向User的。bind的第一个参数就是给该匿名函数绑定作用域。同时呢也可以使用该函数为函数绑定预定义的参数

var User = {
    fullName: 'kunkun',
    print: function() {
        getUserComments(function(msg, data) {
            console.log(this.fullName + ' made ' + data.comments + ' comments' + msg);
        }.bind(this, 'coder'));
    }
};

注意在调用的时候参数的顺序是在bind绑定的参数在前面。

Polyfill-为旧版本的浏览器增加bind函数

同时也是文章开头的那道题要求的答案。

这里的解决方案就是首先判断Function.prototype中的是否具有这个bind函数,如果没有的话在这里为Function.prototype增加一个bind属性,具体实现还是通过apply来搞定的,Function.prototype.bind()返回一个函数,在这个函数执行的时候动态地执行要绑定作用域的函数的apply方法

if (!Function.prototype.bind) {
  Function.prototype.bind = function (oThis) {
    if (typeof this !== "function") {
      throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
    }     
    var aArgs = Array.prototype.slice.call(arguments, 1), 
        fToBind = this, 
        fNOP = function () {},
        fBound = function () {
          return fToBind.apply(
            this instanceof fNOP && oThis ? this : oThis, 
            aArgs.concat(Array.prototype.slice.call(arguments))
          );
        };     
    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();         
    return fBound;
  };
}

每个函数在执行的时候有个arguments 对象,这是一个类数组对象,是一个传入的参数列表,通过Array.prototype.slice.call(arguments, 1)来获取参数列表除第一个元素之外的元素列表,返回结果是一个数组。因为bind 函数第一个参数是要绑定的对象,之后的参数是预先为函数绑定的参数,同事呢这个函数在执行的时候也会传入参数,这个参数列表也在arguments里面,于是将这个两个参数列表进行合并。

aArgs.concat(Array.prototype.slice.call(arguments))

将该数组传入给apply的第二个参数。apply第二个参数是arguments这样类型的,当然也可以是一个数组比如apply(obj,[参数一,参数二,参数三]),而call在这方面的区别就是参数列表是如 call(user,参数一,参数二,参数三)这种形式.apply调用要将参数封装到一个数组里面,这种方式适用用不清楚参数个数的情况下。而call则要求要明确参数的个数。很明显apply更灵活、功能要强一些。

如果要了解更多信息可以参见https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

最后自己瞎编了两道题

第一道
var name="张三"
function Person(name){
this.name=name
};

Person.prototype.getName=function(){
console.log(this.name)
};

var p=new Person("李四");
 p.getName();

 var m=p.getName;

 m();
第二道

根据文章继续文章中使用Demo

var getUserComments = function(callback) {
            var numOfCommets = 34;
            callback({ comments: numOfCommets });
        };
var User = {
            fullName: 'kunkun',
            print: function() {
                getUserComments(function(data) {
                    console.log(this.fullName + ' made ' + data.comments + ' comments');
                }.bind(this));
            }
        };
var p=    User.print;
    p()