CORS||跨域资源共享 (Cross-origin resource sharing)

CORS是一个W3C标准,全称是"跨域资源共享" (Cross-origin resource sharing)
以往的解决方案
以前要实现跨域访问,可以通过JSONP、Flash或者服务器中转的方式来实现,但是现在我们有了CORS。
CORS与JSONP相比,无疑更为先进、方便和可靠。
1、 JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
2、 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。
3、 JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS(这部分会在后文浏览器支持部分介绍)。
详细内容
要使用CORS,我们需要了解前端和服务器端的使用方法。
1、 前端
以前我们使用Ajax,代码类似于如下的方式:
- var xhr = new XMLHttpRequest();
- xhr.open("GET", "/hfahe", true);
- xhr.send();
这里的“/hfahe”是本域的相对路径。
如果我们要使用CORS,相关Ajax代码可能如下所示:
- var xhr = new XMLHttpRequest();
- xhr.open("GET", "http://www.liuxinxiu.com/hfahe", true);
- xhr.send();
请注意,代码与之前的区别就在于相对路径换成了其他域的绝对路径,也就是你要跨域访问的接口地址。
我们还必须提供浏览器回退功能检测和支持,避免浏览器不支持的情况。
- function createCORSRequest(method, url) {
- var xhr = new XMLHttpRequest();
- if ("withCredentials" in xhr) {
- // 此时即支持CORS的情况
- // 检查XMLHttpRequest对象是否有“withCredentials”属性
- // “withCredentials”仅存在于XMLHTTPRequest2对象里
- xhr.open(method, url, true);
- } else if (typeof!= "undefined") {
- // 否则检查是否支持XDomainRequest,IE8和IE9支持
- // XDomainRequest仅存在于IE中,是IE用于支持CORS请求的方式
- xhr = new XDomainRequest();
- xhr.open(method, url);
- } else {
- // 否则,浏览器不支持CORS
- xhr = null;
- }
- return xhr;
- }
- var xhr = createCORSRequest('GET', url);
- if (!xhr) {
- throw new Error('CORS not supported');
- }
现在如果直接使用上面的脚本进行请求,会看到浏览器里控制台的报错如下:
错误显示的很明显,这是因为我们还未设置Access-Control-Allow-Origin头。
2、 服务器
服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。
HTTP 头的设置方法有很多,http://enable-cors.org/这篇文章里对各种服务器和语言的设置都有详细的介绍,下面我们主要介绍Apache和PHP里的设置方法。
Apache:Apache需要使用mod_headers模块来激活HTTP头的设置,它默认是激活的。你只需要在Apache配置文件的<Directory>, <Location>, <Files>或<VirtualHost>的配置里加入以下内容即可:
- Header set Access-Control-Allow-Origin *
PHP:只需要使用如下的代码设置即可。
- <?php
- header("Access-Control-Allow-Origin:*");
以上的配置的含义是允许任何域发起的请求都可以获取当前服务器的数据。当然,这样有很大的危险性,恶意站点可能通过XSS攻击我们的服务器。所以我们应该尽量有针对性的对限制安全的来源,例如下面的设置使得只有http://www.liuxinxiu.com 这个域才能跨域访问服务器的API。
- Access-Control-Allow-Origin: http://www.liuxinxiu.com
浏览器支持情况
HTTP协议之multipart/form-data请求分析

首先来了解什么是multipart/form-data请求:
根据http/1.1 rfc 2616的协议规定,我们的请求方式只有OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE等,那为为何我们还会有multipart/form-data请求之说呢?这就要从头来说了。
http协议大家都知道是规定了以ASCII码传输,建立在tcp、ip协议之上的应用层规范,规范内容把http请求分为3个部门:状态行,请求头,请求体。所有的方法、实现都是围绕如何运用和组织这三部分来完成的。换句话来说就是万变不离其中,只要我们了解了http请求的组成部分后,自然就可以应变任何实际工作中的需求和问题了。
关于状态行,请求头,请求体等三部分的具体内容,大家可以参考官方的协议文档http://www.faqs.org/rfcs/rfc2616.html,这里主要分析multipart/form-data请求具体是怎么一回事。
既然http协议本身的原始方法不支持multipart/form-data请求,那这个请求自然就是由这些原始的方法演变而来的,具体如何演变且看下文:
1、multipart/form-data的基础方法是post,也就是说是由post方法来组合实现的
2、multipart/form-data与post方法的不同之处:请求头,请求体。
3、multipart/form-data的请求头必须包含一个特殊的头信息:Content-Type,且其值也必须规定为multipart/form-data,同时还需要规定一个内容分割符用于分割请求体中的多个post的内容,如文件内容和文本内容自然需要分割开来,不然接收方就无法正常解析和还原这个文件了。具体的头信息如下:
- Content-Type: multipart/form-data; boundary=${bound}
//其中${bound} 是一个占位符,代表我们规定的分割符,可以自己任意规定,但为了避免和正常文本重复了,尽量要使用复杂一点的内容。如:--------------------56423498738365
4、multipart/form-data的请求体也是一个字符串,不过和post的请求体不同的是它的构造方式,post是简单的name=value值连接,而multipart/form-data则是添加了分隔符等内容的构造体。具体格式如下:
- --${bound}
- Content-Disposition: form-data; name="Filename"
- HTTP.pdf
- --${bound}
- Content-Disposition: form-data; name="file000"; filename="HTTP协议详解.pdf"
- Content-Type: application/octet-stream
- %PDF-1.5
- file content
- %%EOF
- --${bound}
- Content-Disposition: form-data; name="Upload"
- Submit Query
- --${bound}--
其中${bound}为之前头信息中的分割符,如果头信息中规定为123,那么这里也要为123,;可以很容易看出,这个请求体是多个相同的部分组成的:每一个部分都是以--加分隔符开始的,然后是该部分内容的描述信息,然后一个回车,然后是描述信息的具体内容;如果传送的内容是一个文件的话,那么还会包含文件名信息,以及文件内容的类型。上面的第二个小部分其实是一个文件体的结构,最后会以--分割符--结尾,表示请求体结束。
综上,可以知道要发送一个multipart/form-data的请求,其实任何支持post请求的工具或语言都可以支持,只是自己要稍微包装一下便可。
mysql 常用SQL汇总

- 1.用管理员登陆
- mysql -u root -p;
- 2.创建数据库
- create database nodeDB;
- 3.删除数据库
- drop database nodeDB;
- 4.查看数据库
- show databases;
- 5.选择目标库
- use nodeDB;
- 6.查看目标表所有字段
- select * from user;
- 7.创建用户
- //user01只能本地访问
- CREATE USER user01@'localhost' IDENTIFIED BY 'user01';
- //user02可以远程访问
- CREATE USER user02@'%' IDENTIFIED BY 'password1';
- 8.修改user01密码
- SET PASSWORD FOR 'user01'@'localhost'=PASSWORD('password2');
- 9.授权
- //user01管理db01全部权限
- GRANT ALL PRIVILEGES ON nodeDB.* TO user01;
- //user02查看权限,并修改密码
- GRANT SELECT ON *.* TO 'user02'@'%' IDENTIFIED by 'password2';
- 10. 刷新数据库
- flush privileges;
创建测试表:
- //判断有库就删除
- drop database if exists school;
- create database school;
- use school;
- create table teacher(
- id int(3) auto_increment not null primary key,
- name char(10) not null,
- address varchar(50) default '深圳',
- year date
- );
- //以下为插入字段
- insert into teacher values(null,'allen','大连一中','1976-10-10');
- insert into teacher values(null,'jack','黄冈中学','1975-12-23');
- insert into teacher values(null,'jesse','北京四中','1980-6-23');
- insert into teacher values(null,'tony','衡水中学','1982-2-12');
NodeJs JSONP数据接口定义

- var http = require('http');
- var urllib = require('url');
- var port = 10011;
- var data = {'name': 'jifeng', 'company': 'taobao'};
- http.createServer(function(req, res){
- var params = urllib.parse(req.url, true);
- console.log(params);
- if (params.query && params.query.callback) {
- //console.log(params.query.callback);
- var str = params.query.callback + '(' + JSON.stringify(data) + ')';//jsonp
- res.end(str);
- } else {
- res.end(JSON.stringify(data));//普通的json
- }
- }).listen(port, function(){
- console.log('server is listening on port ' + port);
- })
自建Node路由Jsonp接口访问地址:http://liuxinxiu.com:3000/jsonp?Jsoncallback=2016&&name=liuxinxiu
- function jsonp(){
- this.exec=function(route,req,res){
- var data1={key:'value',hello:'world',name:'刘新修'};
- var data2={'name':'wangjiang','company':'taobao'};
- console.log(req+'---'+route)
- res.statusCode=200;
- //res.setHeader('content-type','application/json;charset=utf-8');
- //res.setHeader('Content-Type','text/html');
- //req.write(data);
- //req.end();
- var params=url.parse(req.url,true);
- console.log(params);
- if(params.query&¶ms.query.Jsoncallback){
- console.log(params.query.Jsoncallback);
- var str=params.query.Jsoncallback+'('+JSON.stringify(data1)+')';//jsonp
- res.end(str);
- }
- else{
- res.writeHead(200,{'Content-Type':'application/json'});
- res.end(JSON.stringify(data1)); //普通的json
- }
- }
- }
- module.exports=new jsonp();
自建Node路由Jsonp接口访问地址:http://liuxinxiu.com:3000/Jsoncallback?Jsoncallback=1&name=Jack
- function jsoncallback(){
- this.exec=function(route,req,res){
- /******** 处理请求判断用作JSONP ********/
- var params=url.parse(req.url,true);
- /********** 造数据要Obj不要String **********/
- var str2={dataList:[{name:"Jack",age:28,sex:1},{name:'Tony',age:30,sex:0}]};
- /********** 映射数据源无为undefined **********/
- var output=2016+'('+JSON.stringify(str2)+')';
- var result=params.query.Jsoncallback+'('+JSON.stringify(str2)+')';
- /************* 定义头部信息及编码规范 *******************/
- res.writeHead(200,{'content-Type':'application/json;charset=utf-8'});
- /************* 判断出来相应返回的结果 ********************/
- if(params.query&¶ms.query.Jsoncallback){
- res.write(result);
- res.end();
- }
- else{
- res.write(output);
- res.end();
- }
- }
- };
- module.exports=new jsoncallback();
渲染html到页面中,在koa中可以这么干:
- app.get('/',function*(){
- this.body = "<p>'+title+'</p>";
- })
但问题很大,模板混入到js逻辑了,非常不利于维护,基于 mvc 模式,我们需要将html模板抽离到 view 中方便维护,这时候我们就需要一款模板渲染引擎。
为什么选择xtemplate
适用于 koa 的模板引擎选择非常多,比如 jade、ejs、nunjucks、xtemplate 等。
为什么选择 xtemplate 呢?
上手难度
jade 无疑是最独特,上手难度最高,特别是将 html 转成 jade 风格的代码,需要一些成本和适应,特别是空格敏感,经常引起模块渲染报错:
- body
- h1 Jade - node template engine
- #container.col
- if youAreUsingJade
- p You are amazing
- else
- p Get on it!
但 jade 在 express 中拥有广泛的群众基础,所以从 express 转到 koa,jade 是个不错的选择,但我一直认为 jade 不是最佳的模板选择。
ejs、nunjucks、xtemplate 的上手难度差不多,但ejs的扩展写法有些诡异,特别是 filter 的写法:
- <p><%=: users | first | capitalize %></p>
xtemplate 相对于 nunjucks 的优势是,模板的逻辑写法体验更接近js(ejs的逻辑表达也很接近js),上手更为简单,来看下同样一个 if 判断的写法差异。
xtemplate:
- {{#if(variable===0)}}
- It is true
- {{/if}}
nunjucks:
- {% if variable == 0 %}
- It is true
- {% endif %}
所以上手难度:xtemplate < nunjucks < ejs < jade 。
功能的强大度
四款模板引擎必备几样功能都具备:变量、逻辑表达式、循环、layout、include、宏、扩展等。
nunjucks 无疑是功能最全面的模板引擎,xtemplate 拥有 nunjucks 大部分的特性,除了filter,但 xtemplate 拥有非常强悍的拓展性:
- xtpl.addCommand('money',function(scope, option){
- var money = option.params[0];
- if(typeof money !== 'number') return '';
- //金额除以100(接口返回的金额都是以分为单位,转成元)
- var s = Number(money)/100;
- return s;
- })
模板中使用:
- {{money(10000)}}
nunjucks 与 xtemplate 都拥有实用的宏定义功能:
- {{#macro("test","param", default=1)}}
- param is {{param}} {{default}}
- {{/macro}}
模板中使用:
- {{macro("test","2")}}
输出内容:
- param is 2 1
jade 中的 Mixins,ejs 中的 function 也可以实现类似的抽取公用html代码块的目的 。
如果真要分个高下:nunjucks > xtemplate > jade > ejs 。
是否支持前后端混用
jade 直接淘汰,相信在前端js领域一般不会选择 jade 来渲染。
nunjucks 也使用的比较少(ejs其实也少),更多人会选择使用 handlebars 。
xtemplate 目前在阿里的系统中前后端混用中已经得到论证,节约了模板前后端转换的时间。
事实上是否支持前后端混用不是决定性因素,特别是在 angularjs 盛行的年代。
性能考量
它们都很快,其实性能都不是问题,真要较真的话,xtemplate 会更优秀些,可以看 xtemplate benchmark。
个人觉得性能不是决定性因素。
基于上述几点考虑,推荐使用 xtemplate 。
PHP处理HTTP请求的几种方式

GET,一般是明文的,比如XXX.php?a=1&b=2,这里的a,b就必须用GET方式接收,接收代码如下:
$a = $_GET['a'];
$b = $_GET['b'];
//接收a,b两个变量
POST,一般是隐藏的非明文的,一般表单设置成POST的,接收方式如下:
//比如有个表单,表单中有两个文本框,name 分别是 a,b,那么代码如下:
$a = $_POST['a'];
$b = $_POST['b'];
另外:$_REQUEST,可以同时接收GET、POST的变量,用法如:
$_REQUEST['a'];//接收变量a,a可以是GET的也可以是POST的
另外说说PHP获取POST请求的几种方式:
方法1、最常见的方法是:$_POST['fieldname'];
说明:只能接收Content-Type: application/x-www-form-urlencoded提交的数据
解释:也就是表单POST过来的数据
方法2、file_get_contents(“php://input”);
说明:
允许读取 POST 的原始数据。
和 $HTTP_RAW_POST_DATA 比起来,它给内存带来的压力较小,并且不需要任何特殊的 php.ini 设置。
php://input 不能用于 enctype=”multipart/form-data”。
解释:
对于未指定 Content-Type 的POST数据,则可以使用file_get_contents(“php://input”);来获取原始数据。
事实上,用PHP接收POST的任何数据都可以使用本方法。而不用考虑Content-Type,包括二进制文件流也可以。
所以用方法二是最保险的方法。
方法3、$GLOBALS['HTTP_RAW_POST_DATA'];
说明:
总是产生 $HTTP_RAW_POST_DATA 变量包含有原始的 POST 数据。
此变量仅在碰到未识别 MIME 类型的数据时产生。
$HTTP_RAW_POST_DATA 对于 enctype=”multipart/form-data” 表单数据不可用
如果post过来的数据不是PHP能够识别的,可以用 $GLOBALS['HTTP_RAW_POST_DATA']来接收,
比如 text/xml 或者 soap 等等
解释:
$GLOBALS['HTTP_RAW_POST_DATA']存放的是POST过来的原始数据。
$_POST或$_REQUEST存放的是 PHP以key=>value的形式格式化以后的数据。
但$GLOBALS['HTTP_RAW_POST_DATA']中是否保存POST过来的数据取决于centent-Type的设置,即POST数据时 必须显式示指明Content-Type: application/x-www-form-urlencoded,POST的数据才会存放到 $GLOBALS['HTTP_RAW_POST_DATA']中。
node.js 获取http url路径中的各个参数

假设URL为:http://localhost:8888/select?name=a&id=5
- http.createServer(function(request,response){
- var pathname = url.parse(request.url).pathname; //pathname => select
- var arg = url.parse(request.url).query; //arg => name=a&id=5
- console.log("Request for " + arg );
- var str = querystring.parse(arg); //str=> {name:'a',id:'5'}
- var arg1 = url.parse(request.url, true).query; //arg1 => {name:'a',id:'5'}
- console.log("Request for " + arg1 );
- var name = querystring.parse(arg).name; //name => a
- console.log("name = "+name);
- console.log("Request for " + pathname + " received.");
- }).listen(8888);
//querystring.parse(arg) => { name: 'a', id: '5' }
var url = require('url');
var a = url.parse('http://example.com:8080/one?a=index&t=article&m=default');
console.log(a);
//输出结果:
{
protocol : 'http' ,
auth : null ,
host : 'example.com:8080' ,
port : '8080' ,
hostname : 'example.com' ,
hash : null ,
search : '?a=index&t=article&m=default',
query : 'a=index&t=article&m=default',
pathname : '/one',
path : '/one?a=index&t=article&m=default',
href : 'http://example.com:8080/one?a=index&t=article&m=default'
}
node.js post json格式数据到服务器的几种方法

json格式被越来越多的开发者说青睐,我们常常在接口定义时使用这种格式参数进行数据交换.
今天主要给大家从繁到简的几个node.js下使用的提交 json个数参数的方式方法.
下面示例中出现的参数都做了处理,拷贝代码后需要更改这些参数同时自己写一个接收json格式的api,请求成功后再返回json格式
(一) node.js 原生自带http模块,可以解决基于http协议下的请求及回发,执行效率高,但是好多东西需要开发人员自己动手来实现,看下面代码
- var http=require('http');
- var body = {
- "data":{
- "channel" : "aaa",
- "appkey" : "bbb"
- },
- "sign" : "22334455",
- "token" : "bb667788"
- };
- var bodyString = JSON.stringify(body);
- var headers = {
- 'Content-Type': 'application/json',
- 'Content-Length': bodyString.length
- };
- var options = {
- host: '127.0.0.1',
- port: 3005,
- path: '/Config',
- method: 'POST',
- headers: headers
- };
- var req=http.request(options,function(res){
- res.setEncoding('utf-8');
- var responseString = '';
- res.on('data', function(data) {
- responseString += data;
- });
- res.on('end', function() {
- //这里接收的参数是字符串形式,需要格式化成json格式使用
- var resultObject = JSON.parse(responseString);
- console.log('-----resBody-----',resultObject);
- });
- req.on('error', function(e) {
- // TODO: handle error.
- console.log('-----error-------',e);
- });
- });
- req.write(bodyString);
- req.end();
http模块比较原始,请求参数里我们要手动指定请求头类型,头长度,请求方式,主机头和端口等.
当我们将json格式参数post上去以后,得到的response 对象需要我们在 data 事件上手动处理获取到的数据, end 事件表示接收数据流已结束, error 事件是当回发数据发生错误时将自动触发此事件响应函数
(二)
大名鼎鼎的 request 模块上场了,这个模块帮我们做了很多我们不关心的东西,比如请求后得到的 response 对象,我们将不会非常繁琐的去手动捕获 data ,end ,error 事件. 取而代之的是回调函数里直接将我们关心的回发数据放到了一个变量里,看下面的代码:
此模块并发node.js 原生自带模块,首先需要 npm install request ,下面代码的参数同样做了处理
- var request=require('request');
- var options = {
- headers: {"Connection": "close"},
- url: 'http://127.0.0.1:3005/Config',
- method: 'POST',
- json:true,
- body: {data:{channel : "aaa",appkey : "bbb"},sign : "ccc",token : "ddd"}
- };
- function callback(error, response, data) {
- if (!error && response.statusCode == 200) {
- console.log('----info------',data);
- }
- }
- request(options, callback);
代码明显比第一种方式少了很多,callback 里面的 data 参数就是我们请求路径后得到的内容,而且我们在 options 中指定了 json:true ,所以请求和回发的数据自动转变成了 json 对象.我们看下这次的运行结果:
node request.js
下面还有一种更简单的方法和大家分享
(三)
也是一个第三方模块,叫做 request-json
看名字就非常容易理解,有关json 格式参数的请求模块,使用也非常方便.
首先 npm install request-json 安装此模块,定义好 json请求参数对象,见下面代码:
- request = require('request-json');
- var client = request.newClient('http://127.0.0.1:3005/');
- var data = {data:{channel : "aaa",appkey : "bbb"},sign : "4444",token : "555"};
- client.post('Config', data, function(err, res, body) {
- console.log(res.statusCode,body);
- });
返回的body 也是自动序列化成json对象,见运行结果
前面的 200 是 res.statusCode 回发状态值,接着输出的是我们得到的json对象,无论从使用还是理解上,都是最简单的一种方法.
nodeJs构建一个HttpServer

将下面代码放入server.js中
- var http = require("http");
- http.createServer(function(request, response) {
- console.log('request received');
- response.writeHead(200, {"Content-Type": "text/plain"});
- response.write("Hello World");
- response.end();
- }).listen(8888);
- console.log('server started');
执行node server.js
打开http://localhost:8888/,页面显示Hello World
- var http=require('http');
- var data={key:'value', hello:'world'};
- var srv=http.createServer(function (req, res) {
- res.writeHead(200,{'Content-Type': 'application/json'});
- res.end(JSON.stringify(data));
- });
- srv.listen(8080,function() {
- console.log('listening on localhost:8080');
- });
nodejs 提供了一个 node-uuid 模块用于生成 uuid:

首先执行:npm install node-uuid -g
- var uuid = require('node-uuid');
- //var uuid = require('/usr/local/nodejs/lib/node_modules/node-uuid');
- console.log(uuid.v1())
- console.log(uuid.v4())
注明:如果在linux下编译安装node可以使用软连接ls 或这直接写绝对路径引入需要的nmp模块!
v1 是基于时间戳生成uuid
v4是随机生成uuid
结果:
- 57af5b10-3a76-11e5-922a-75f42afeee38
- f3917fb9-9bde-4ec1-a7cf-966251b3d22a