Monthly Archive 十一月 2019

By柏小白

apicloud实现短信验证功能

短信验证码SDK,为开发者提供全球通用的短信验证码工具,开发者可以用其在App植入短信验证码SDK、简单设置即可实现短信验证功能,集成快速便捷,且后期易于管理。

配置集成 开发者使用本模块之前需要先到Mob官网申请开发者账号,并在账号内填写相应信息创建自己的 APP,从而获取AppKeyAppSecret,然后添加SMSSDK功能,获取模板id。 详情参考:快速集成获取apppkey和appSecret

一. 准备工作

下载并安装开发工具:APICloud Studio 2

二. 创建应用

APICloud提供了两种创建应用的方式,方便开发者在云端或APICloud Studio中创建应用。 云端创建应用:

1) 注册并登录APICloud系统:https://www.apicloud.com/console 点击左上角“创建应用”, 如图:选择“Native”,填写“名称”及“说明”,应用创建完成。

1

APICloud Studio中创建应用:

1) 登录APICloud Studio,没有账号点击“注册账号”,已经注册,用之前注册的APICloud账号登录APICloud Studio

2

这2端可以相互同步信息:

同步本地应用到云端资源库,开发者在APICloud Studio创建的应用会和云端资源库建立连接。项目代码改动后,可以使用APICloud Studio的代码提交功能提交代码到云端资源库。

三. 添加模块

打开APICloud云端,同时添加SMSSDK模块、mobtools模块:

4

四. 模块使用攻略

Android: 使用此模块之前android需先配置config.xml文件,方法如下

<meta-data name="Mob-AppKey" value="moba6b6c6d6"/>
<meta-data name="Mob-AppSecret" value="b89d2427a3bc7ad1aea1e1e8c1d36bf3"/>

IOS: ios 需要将Info.plist 文件放入res目录下,文件内容内容:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>MOBAppKey</key>
<string>moba6b6c6d6</string>
<key>MOBAppSecret</key>
<string>b89d2427a3bc7ad1aea1e1e8c1d36bf3</string>
</dict>
</plist>

字段描述:

  • Mob-AppKey:(必须配置)从Mob官网获取的 AppKey。
  • Mob-AppSecret:(必须配置)从Mob官网获取的 AppSecret。 申请方法参考快速集成获取apppkey和appSecret

编译app时iOS 请配置访问联系人的权限

5

Demo实例widget下载地址

五. 模块接口说明

需要引入模块: var moduleSMSSDK = api.require('smssdk'); (1)获取文本验证码 getTextCode({params}, callback(ret,err)) params:

phoneNumber:

类型:字符串

默认值:无

描述:手机号

zone:

类型:字符串

默认值:无

描述:区域号,不要加"+"号

tempCode:

类型:字符串

默认值:无

描述:模板id

callback(ret,err) ret:

  • 类型:JSON 对象
  • 内部字段: { smart:0 //是否为智能验证 bool类型, ios 忽略此字段 }

err:

  • 类型:JSON 对象
  • 内部字段:
{
    code:0    //错误码(详见错误码常量)
    msg:""    //错误描述
};

示例代码

var param = {zone:'86', phoneNumber:'18500000000',tempCode:'1319972'};
moduleSMSSDK.getTextCode(param, function(ret, err){
    if (err !== null && err !== undefined && err !== '') {
     // 错误消息示例:{"msg":"Template not exist.","code":484}
    alert("Error:\n" + JSON.stringify(err));
    } else {
        // 正常消息示例:{"smart":false}
       alert("Success:\n" + JSON.stringify(ret));
    }
    });

(2)获取语音验证码 getVoiceCode({params}, callback(ret, err)) params:

phoneNumber:

类型:字符串

默认值:无

描述:手机号

zone:

类型:字符串

默认值:无

描述:区域号,不要加"+"号

callback(ret, err) ret:

  • 类型:JSON 对象 内部字段: { }

err:

  • 类型:JSON 对象
  • 内部字段:
{
    code:0    //错误码(详见错误码常量)
    msg:""    //错误描述
};

示例代码:

// param中的key命名不能改变
var param = {zone:'86', phoneNumber:'18500000000'};
moduleSMSSDK.getVoiceCode(param, function(ret, err){
  if (err !== null && err !== undefined && err !== '') {
   // 错误消息示例:{"msg":"Template not exist.","code":484}
   alert("Error:\n" + JSON.stringify(err));
  } else {
     // 正常消息示例:{}
    alert("Success:\n" + JSON.stringify(ret));
    }
   });

(3)提交验证码 commitCode({params}, callback(ret, err)) params:

phoneNumber:

类型:字符串

默认值:无

描述:手机号

zone:

类型:字符串

默认值:无

描述:区域号,不要加"+"号

code:

类型:字符串

默认值:无

描述:验证码

callback(ret, err) ret:

  • 类型:JSON 对象 内部字段: { }

err:

  • 类型:JSON 对象
  • 内部字段:
{
    code:0    //错误码(详见错误码常量)
    msg:""    //错误描述
};

示例代码:

// param中的key命名不能改变
var param = {zone:'86', phoneNumber:'18500000000', code:'4847'};
moduleSMSSDK.commitCode(param, function(ret, err){
if (err !== null && err !== undefined && err !== '') {
  // 错误消息示例:{"msg":"Template not exist.","code":484}
  alert("Error:\n" + JSON.stringify(err));
  } else {

  }
  });

(4)获取区号 getSupportedCountries(callback(ret, err)) callback(ret, err) ret:

  • 类型:JSON 对象
  • 内部字段:
{
    countries =
    (
        {
        rule = "^\\d+";
        zone = 1868;
        }
    )
}

err:

  • 类型:JSON 对象
  • 内部字段:
{
    code:0    //错误码(详见错误码常量)
    msg:""    //错误描述
};

示例代码:

// param中的key命名不能改变
moduleSMSSDK.getSupportedCountries(function(ret, err){
    if (err !== null && err !== undefined && err !== '') {
     // 错误消息示例:{"msg":"Template not exist.","code":484}
      alert("Error:\n" + JSON.stringify(err));
     } else {
      // 正常消息示例:{"countries":[{zone=590, rule=^\d+},{zone=680, rule=^\d+}]}
        alert("Success:\n" + JSON.stringify(ret));
    }
  });

(5)获取通讯里好友信息 getFriends(callback(ret, err)) callback(ret, err) ret:

  • 类型:JSON 对象
  • 内部字段:
{
    friends =
    (
     {
        lastname : "你好";
        others =     (
                {
                desc = "董浩";
                phone = 10101155;
                type = 1;
            }
        );
        phones =     (
                    {
                desc = "董浩";
                phone = 10101155;
                type = 1;
            }
        );
        recordid = 411;
        specialdate =     (
                    {
                desc = "生日";
                date = "1988-07-08";
                type = 1;
            }
        );
    }
    )
}

err:

  • 类型:JSON 对象
  • 内部字段:
{
    code:0    //错误码(详见错误码常量)
    msg:""    //错误描述
};

示例代码:

// param中的key命名不能改变
moduleSMSSDK.getFriends(function(ret, err){
   if (err !== null && err !== undefined && err !== '') {
    // 错误消息示例:{"msg":"Template not exist.","code":484}
    alert("Error:\n" + JSON.stringify(err));
   } else {
    // 正常消息示例:{"countries":[{zone=590, rule=^\d+}, {zone=680, rule=^\d+}]}
     alert("Success:\n" + JSON.stringify(ret));
    }
 });

(6)提交用户资料 submitUserInfo({params}, callback(ret, err)) params:

uid:

类型:字符串

默认值:无

描述:用户id

nickname:

类型:字符串

默认值:无

描述:用户昵称

avatar:

类型:字符串

默认值:无

描述:头像地址

phoneNumber:

类型:字符串

默认值:无

描述:手机号

zone:

类型:字符串

默认值:无

描述:区域号,不要加"+"号

callback(ret, err) ret:

  • 类型:JSON 对象
  • 内部字段: { }

err:

  • 类型:JSON 对象
  • 内部字段:
{
    code:0    //错误码(详见错误码常量)
    msg:""    //错误描述
};`

示例代码:

// param中的key命名不能改变
var uid = "3241241";
var nickname = "SmsSDK_Api_Cloud_User_" + uid;
var avatar = "http://download.sdk.mob.com/510/deb/0c0731ac543eb71311c482a2e2.png";
// param中的key命名不能改变
var param = {uid:uid, nickname:nickname, avatar:avatar, phoneNumber:'18500000000', zone:'86'};

moduleSMSSDK.submitUserInfo(param, function(ret, err){
if (err !== null && err !== undefined && err !== '') {
// 错误消息示例:{"msg":"Template not exist.","code":484}
alert("Error:\n" + JSON.stringify(err));
} else {
}
});

(7)获取版本号 getVersion(callback(ret, err)) callback(ret,err) ret

  • 类型:JSON 对象
  • 内部字段: { version: "1.0.0" }

err

  • 类型:JSON 对象
  • 内部字段: { }

示例代码:

// param中的key命名不能改变
moduleSMSSDK.getVersion(function(ret, err){
if (err !== null && err !== undefined && err !== '') {
// 错误消息示例:{"msg":"Template not exist.","code":484}
alert("Error:\n" + JSON.stringify(err));
} else {
// 正常消息示例:{"version":'3.2.2'}
}
});

(8)是否允许访问通讯录好友 enableWarn({params}) params:

isWarn:

类型:布尔

默认值:true

描述:YES 代表启用 NO 代表不启用 默认为启用

示例代码:

// param中的key命名不能改变
var isWarn = true;
// param中的key命名不能改变
var param = {isWarn:isWarn};
moduleSMSSDK.enableWarn(param);
By柏小白

Nginx配置一键生成

关于Nginx部署、配置的文章网上已经发布过很多,包括我自己也私藏了不少还发布过两篇:

整理出来为的就是需要的时候,复制、粘贴就能使用。

然而千奇百怪的实际开发中,你肯定需要增删Nginx配置。你就得上网搜一下,复制粘贴出bug了又得调一下…

搞定还得保存下来以备后患。多了不好找还得整理…就搞得很麻烦

后果

今天我给大家推荐一款”Nginx配置利器”,配配变量就能一键生成常用配置。和繁琐低效配置说再见👋


网站链接:

nginxconfig 目前支持:

  • Angular、React、Vue、Node.js
  • PHP、Python
  • wordpress、Magento、Drupal
  • 缓存、Https、日志等各种配置…

使用

实现用户访问*.myweb.com域名自动跳转到myweb.com配置,并且开启http强制跳转到https的配置。

图片描述
图片描述

配置完之后,下方还有安装步骤指导你配置生效。交互体验相当好

图片描述

生成配置 /etc/nginx/sites-available/myweb.com.conf 如下:

server {
	listen 443 ssl http2;
	listen [::]:443 ssl http2;

	server_name myweb.com;
	root /var/www/myweb.com/public;

	# SSL
	ssl_certificate /etc/letsencrypt/live/myweb.com/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/myweb.com/privkey.pem;
	ssl_trusted_certificate /etc/letsencrypt/live/myweb.com/chain.pem;

	# security
	include nginxconfig.io/security.conf;

	# index.html fallback
	location / {
		try_files $uri $uri/ /index.html;
	}

	# additional config
	include nginxconfig.io/general.conf;
}

# subdomains redirect
server {
	listen 443 ssl http2;
	listen [::]:443 ssl http2;

	server_name *.myweb.com;

	# SSL
	ssl_certificate /etc/letsencrypt/live/myweb.com/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/myweb.com/privkey.pem;
	ssl_trusted_certificate /etc/letsencrypt/live/myweb.com/chain.pem;

	return 301 https://myweb.com$request_uri;
}

# HTTP redirect
server {
	listen 80;
	listen [::]:80;

	server_name .myweb.com;

	include nginxconfig.io/letsencrypt.conf;

	location / {
		return 301 https://myweb.com$request_uri;
	}
}
复制代码

网站下方还罗列了推荐的nginx配置、安全配置…以作参考/etc/nginx/nginx.conf

# Generated by nginxconfig.io
# https://nginxconfig.io/?0.domain=myweb.com&0.php=false&0.index=index.html&0.fallback_html

user www-data;
pid /run/nginx.pid;
worker_processes auto;
worker_rlimit_nofile 65535;

events {
	multi_accept on;
	worker_connections 65535;
}

http {
	charset utf-8;
	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	server_tokens off;
	log_not_found off;
	types_hash_max_size 2048;
	client_max_body_size 16M;

	# MIME
	include mime.types;
	default_type application/octet-stream;

	# logging
	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log warn;

	# SSL
	ssl_session_timeout 1d;
	ssl_session_cache shared:SSL:10m;
	ssl_session_tickets off;

	# Diffie-Hellman parameter for DHE ciphersuites
	ssl_dhparam /etc/nginx/dhparam.pem;

	# Mozilla Intermediate configuration
	ssl_protocols TLSv1.2 TLSv1.3;
	ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;

	# OCSP Stapling
	ssl_stapling on;
	ssl_stapling_verify on;
	resolver 1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=60s;
	resolver_timeout 2s;

	# load configs
	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/sites-enabled/*;
}
复制代码

/etc/nginx/nginxconfig.io/security.conf

# security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

# . files
location ~ /\.(?!well-known) {
	deny all;
}
复制代码

拓展

以上就满足日常开发需求啦。如果你压抑不住,想要展示你的高端操作。
你可以加入到项目本身开发中;nginxconfig项目本身是MIT开源协议,你也可以在此基础上迭代出自己的版本

作者:锐玩道
链接:https://juejin.im/post/5dbb88e56fb9a0208055c5fa
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

By柏小白

前端Nginx学习笔记

 前端Nginx学习笔记

nginx简介

1.nginx是一个高性能的Web服务器和反向代理服务器,也可作为电子邮件代理服务器。
2.在连接高并发的情况下,nginx是Apache服务不错的替代品,能够支持高达 50,000 个并发连接数的响应。

正向代理与反向代理

1.反向代理是指以代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回到Internet上请求连接的客户端,此时代理服务器对外表现为一个反向代理服务器。
2.正向代理是指用户无法访问某网站,但是可以访问某代理服务器,而代理服务器可以访问该网站。于是用户通过访问代理服务器去访问该网站,代理服务器把响应信息返回给用户。
3.反向代理中用户访问的IP和端口是nginx服务器的,不知道提供服务的底层服务器,底层服务器在内网中,对外公开的是nginx服务器,nginx充当中间层。而正向代理中用户是知道目标服务器的域名信息的,知只是迫于网络的限制无法访问,如最常见的访问外网。

nginx安装与启动

nginx有windows版本和linux版本,一般针对项目需求是在Linux部署nginx服务器。

下载地址

nginx.org/en/download…

windows版本下载一个压缩包,解压即可
1.文件路径必须为英文,否则启动不成功。
2.查看端口占用,nginx服务器默认启动80端口。
cmd中

  • netstat -an  显示出电脑中所有被打开的端口列表
  • netstat -ano  显示出所有占用端口的列表
  • netstat -ano | findstr “80”  显示出80端口占用的详细情况
  • tasklist | findstr “80”    查询端口具体哪个应用占用

Netstat是在内核中访问网络连接状态及其相关信息的程序,它能提供TCP连接,TCP和UDP监听,进程内存管理的相关报告。

启动成功

  • 启动方式1:双击nginx.exe
  • 启动方式2:start nginx
  • 关闭方式1: 结束进程(两个进程)
  • 关闭方式2:nginx -s stop

nginx配置文件

nginx的核心配置文件nginx.conf主要由3个部分组成

  • 基本配置

#user  nobody; #配置worker进程运行用户
worker_processes  1; #配置工作进程数目,根据硬件调整,通常等于CPU数量或者2倍于CPU数量

#error_log  logs/error.log; #配置全局错误日志及类型 [debug|info|notice|warn|error|crit(致命错误)]默认error
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid; #配置进程pid文件

复制代码
  • events配置
#配置工作模式和连接数
events {
    # use epoll; #事件处理模型优化
    worker_connections  1024; #配置每个worker进程连接数上限,nginx支持的总连接数等于worker_connections*worker_processes
    # 我的本双核四线程,多任务运行弱,CPU主频2.4GHz,运行缓慢
}
复制代码
  • http配置,基本配置和多个server配置
http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';
    
    #配置access_log日志及存放路径,并使用上面定义的main日志格式
    #access_log  logs/access.log  main;

    sendfile        on; #开启高效文件传输模式
    #tcp_nopush     on; #防止网络阻塞

    #keepalive_timeout  0;
    keepalive_timeout  65; #长连接超时时间,单位是秒
    #gzip  on; #开启gzip压缩输出

    #可以配置多个server
    server {
        listen       80; #配置监听端口
        server_name  localhost; #配置服务名

        #charset koi8-r; #配置字符集(俄罗斯字符集)      
        #access_log  logs/host.access.log  main;

        #默认的匹配斜杠/的请求,当访问路径中有/,会被该localtion匹配到并进行处理
        #nginx根目录 /html/index.html
        location / {
            root   html; #root根目录,nginx安装主目录下的html目录
            index  index.html index.htm; #配置首页文件的名称
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html; #配置50x错误页面 html/50x.html
        #精确匹配
        location = /50x.html {
            root   html;
        }

        #PHP脚本请求全部转发道到Apache处理
        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        #PHP脚本请求全部转发道到FastCGI处理
        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}
        
        # 禁止访问.htaccess文件-通常是禁止外网访问的文件
        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }

    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

    #配置https服务,安全的网络传输协议,加密传输,端口443
    # HTTPS server
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;

    #    ssl_certificate      cert.pem; #证书
    #    ssl_certificate_key  cert.key; #秘钥

    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;

    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;

    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}

}
复制代码

nginx的主要应用

1.静态网站

nginx是一个http的web服务器,可以将服务器上的静态文件(html、图片等)通过http协议返回给浏览器客户端;若不配置location,nginx默认查找根目录html中的index.html文件。

    server {
        listen       8080;
        server_name  localhost;
        location / {
	    root   wangyi/static; #root= ip+端口 
            index  index.html index.htm;
        }
        #访问:localhost:8080,或如下配置
         location /static {  
	    root   wangyi;  #注意分号
            index  index.html index.htm;
        }
        #访问:localhost:8080或localhost:8080/static
        .......
    }
复制代码

2.负载均衡

网站服务器由多台服务器组成一个集群对外提供服务,当用户输入域名进行访问的时候,负载均衡负责将用户请求分发到集群中的不同服务器,从而提高并发处理能力。nginx作为访问的统一入口,分发请求,实现负载均衡。
负载均衡实现方式有硬件负载均衡和软件负载均衡。

nginx负载均衡

nginx通过在nginx.conf配置文件中配置实现负载均衡
1.首先在http模块配置

upstream network1{
    server 127.0.0.1:3000 weight=3; #权重
    server 127.0.0.1:5000 weight=1; 
}
复制代码

weight表示权重,用于后端服务器性能不均的情况,访问比率约等于权重之比,权重越大,被访问的几率越大。
upstream是配置nginx与后端服务器负载均衡非常重要的一个模块,他还能对后端服务器的健康状态进行检查,若后端服务器中有一台发生故障,则前端请求不会转发到该故障机器。
2.然后在server模块里配置

localtion /webname{
    proxy_pass http://network1; 
}
复制代码

参数‘http://’是固定的,network1字符串要和upstream后面的字符串相等。代理转发到network1下,然后去匹配upstream,再去匹配upstream下的server。

node.js创建http服务器实现负载均衡

node http服务创建好以后,nginx 配置好以后,node作为代理服务器就可以访问node服务了。

upstream network1 {
        server 127.0.0.1:3000 weight=3;
	server 127.0.0.1:5000 weight=1;
        server 127.0.0.1:6000 weight=1;
    }

    server {
        listen       8080;
        server_name  localhost;

        location / {
            # 访问不到服务器,做详细配置。
	    #proxy_redirect off; 
            #proxy_set_header X-Real-IP $remote_addr; 
            #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
            #proxy_set_header X-Forwarded-Proto $scheme; 
            #proxy_set_header Host $http_host; 
            #proxy_set_header X-NginX-Proxy true; 
            #proxy_set_header Connection ""; 
            #proxy_http_version 1.1; 
           proxy_pass http://network1;
        }
复制代码
const http = require('http')
const fs = require('fs')
const url = require('url')
http
    .createServer((req, res) => {
        if(req.url == '/favicon.ico'){
            res.writeHead(200);
            res.end()   
            return;
        }
        res.writeHead(200);
        fs.createReadStream(__dirname + '/index.html') #不同的页面内容查看效果
        .pipe(res);
    })
    .listen(5000)  #3000、6000。。。
------------------------------------------
<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>node</title>
   <link rel="stylesheet" href="">
</head>
<body>
    <div>第 1 个node服务器</div>
    <!-- <div>第 2 个node服务器</div> -->	
    <!-- <div>第 3 个node服务器</div> -->				
</body>
</html>
复制代码

nginx可以成功访问服务器,访问比大约等于权重之比。



nginx常用负载均衡策略
  • 1.轮询(默认)

每个请求轮流分配到不同的后端服务器,如果后端服务器宕机,则自动剔除。具体轮询策略为第一次访问第一个,第二次访问第二个。。。。。

upstream network1 {
        server 127.0.0.1:3000;
	    server 127.0.0.1:5000;
        server 127.0.0.1:6000;
    }
复制代码
  • 2.权重

每个请求按照一定比例分发到不同的后端服务器,用于后端服务器性能不均的情况,访问比率约等于权重之比,权重越大,被访问的几率越大。

upstream network1 {
        server 127.0.0.1:3000 weight=3;
	    server 127.0.0.1:5000 weight=1;
        server 127.0.0.1:6000 weight=1;
    }
复制代码
  • 3.ip_hash

ip_hash也叫ip绑定,每个请求按照访问ip的哈希值分配,这样每个访问客户端会固定访问一个后端服务器,可以解决会话session丢失的问题,常见账号密码登录。
哈希函数hash(“192.168.0.164”)%2 = 0 / 1,ip不变的话访问的服务器不变。

upstream network1 {
       ip_hash;
       server 127.0.0.1:3000;
       server 127.0.0.1:5000;
       server 127.0.0.1:6000;
   }
复制代码

当使用ip_hash以后,会一直访问3000端口的第一台服务器。

  • 最少连接
    web请求会被转发到连接数最少的服务器上。
 upstream network1 {
        least_conn;
        server 127.0.0.1:3000;
	    server 127.0.0.1:5000;
        server 127.0.0.1:6000;
    }
复制代码
负载均衡其他配置
upstream network1 {
        least_conn;
        server 127.0.0.1:3000;
	server 127.0.0.1:5000 backup; #备份 当所有非backup机器宕机的时候才能请求backup机器。
    }
upstream network1 {
        least_conn;
        server 127.0.0.1:3000;
	server 127.0.0.1:5000 down;#当前机器是down状态,不参与负载均衡。
    } 
复制代码

3.静态代理

把所有静态资源的访问改为访问nginx,而不是后台服务器,nginx更擅长于静态资源的处理,性能更好,效率更高。在实际应用中将静态资源html,css,js,图片交给nginx处理。
nginx实现静态代理,在nginx.conf配置文件中添加静态资源的location。
1.基于请求资源的后缀

location ~ .*\.(js|css|htm|html|gif|jpg|jpeg|png|bmp|swf|ioc|rar|zip|txt|flv|mid|doc|ppt|pdf|xls|mp3|wma)${
    &emsp;root/html; #访问静态资源的路径,静态资源部署在nginx安装目录下,跟访问html/index.html一个道理。
}
复制代码

2.基于请求资源的存放目录

location ~ .*/(js|css|img|imgags)${
    &emsp;root/opt/static
}
复制代码

linux安装目录:/user/local/nginx,在linux作为nginx部署服务器时,请求linux中存放静态资源的某个目录,如/opt/static

4.动静分离

nginx的负载均衡和静态代理结合在一起,实现动静分离,服务器专注于动态资源,nginx专注于静态资源。


5.虚拟主机

虚拟主机就是把一台物理服务器划分成多个“虚拟”的服务器,这样我们的一台物理服务器就可以当做多个服务器来使用,从而可以配置多个网站。
Nginx下,一个server标签就是一个虚拟主机,设置多个虚拟主机,配置多个server即可。

基于端口的虚拟主机

根据nginx的不同端口即可访问到基于不同端口的服务器。

 server {
        listen       8080; #9090。。。
        server_name  localhost;
        location / {
		  proxy_pass http://network1;
        }
 }
 upstream network1 {
        server 127.0.0.1:3000;
    }
 upstream network2 {
        server 127.0.0.1:5000;
    }
复制代码
基于域名的虚拟主机

基于域名的虚拟主机访问时以域名的形式访问,这就涉及到DNS域名解析的知识,我们可以在windows的hosts文件给域名直接指定端口,windows C:\Windows\System32\drivers\etc有个hosts文件,如果在这里指定了一个域名对应的ip地址,那浏览器会首先使用这个ip地址。

 server {
        listen       8080;
        server_name  www.maanshan.com
        # server_name  www.laioyang.com
        location / {
		  proxy_pass http://network1;
          #proxy_pass http://network2;
        }
 }
复制代码
upstream network1 {
        server 127.0.0.1:3000;
    }
 upstream network2 {
        server 127.0.0.1:5000;
    }
复制代码
虚拟主机实例

www.meituan.com

  • www.liaoyang.meituan.com
  • www.maanshan.meituan.com



转载:https://juejin.im/post/5ddce45a6fb9a071594b3c76?utm_source=gold_browser_extension

By柏小白

Web网站打包成APP

appcan是国内比较老牌的平台
apicloud是新起之秀
phonegap是国外的大牌。
长期来看,phonegap是开源的,理所当然是技术最好。但是中文资料较少,国内应用的也不多。
appcan近期被apicloud蹂躏得不行不行的,基本全方位被碾压了。渐渐处于apicloud下风,后力不继。
apicloud还提供了简单的后端api,用户系统,自定义字段什么的基本不用写后端代码了。
但是最后我要劝你一句,不要用这种混合打包平台,最终都是然并卵的。君不见淘宝技术有多牛,写出来的app(html5)还是卡到死。

这里以APICLOUD为例演示:

1、首先访问http://www.apicloud.com/进行用户注册

1

2、注册成功登陆页面,点击左上角–创建应用

2

3、选择中间的–Web–输入项目名字–在网址一栏要输入在线网址(一定要是在线项目),点击创建即可3

4、直接点击云编译

4

5、填写应用名称–选择平台–类型–调试模式–全局加密–渠道打包–版本信息–点击云编译

5

6、如果点击云编译发现没有Android证书,则重新选择类型为–测试版,也可以先去申请个证书。

7、出现编译完成和二维码,就可以扫码或者下载你的个人APP了

6
————————————————
版权声明:本文为CSDN博主「小小猪~」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zyj_15067066062/article/details/78499410

By柏小白

webstorm 怎么打断点

  1. 打开Chrome软件的右上角 Jet插件,在下拉菜单中选择选项,如下图所示。

    1
  2. 在打开的Jet选项设置窗体,设置端口(要调试程序的访问端口)后并点击Apply。如下图所示。

    2
  3. 打开WebStorm项目工程,在软件首页的右上角位置打开Edit Configurations选项,添加一个JavaScript debug调试设置。

    3
  4. 在新增的JavaScript Debug调试设置,将URL设置为调试的路径,点击Apply,完成debug设置。

    4
  5. 在Terminal运行npm run dev命令,启动vue工程并设置断点,如下图所示。

    5
  6. 启动debug,自动打开chrome,可以看到成功命中断点。

    6

    7

 

By柏小白

手把手教你WebStorm+chrome实现时时调试刷新

  1. WebStorm 安装

    WebStorm是一款收费的IDE,即使你不购买也可以免费使用30天,个人认为非常靠谱。

    进入官网,点击Download即可下载最新版本,这里默认window版本;

    安装过程没有一般默认即可,安装之后打开页面如图

    1

    2

    3

  2. WebStorm设置

    WebStorm 是通过live edit功能与浏览器实现时时刷新,这里在页面中点击Configure然后选择Settings打开WebStore的设置对话框;

    在搜索框输入live edit回车找到live edit选项,选中Auto in(ms)然后设置时间。然后勾选Restart if hotswap fails.然后点击apply应用即可

    4
  3. Chrome插件安装

    打开Chrome在地址栏右侧最后面点击三杠,选择“更多工具”->”扩展程序”;

    打开已安装的扩展在页面底部点击“获取更多扩展程序”进入Chrome网上商店,搜索JetBrains IDE Suport扩展程序。点击“添加至Chrome“按钮,安装该扩展程序到Chrome。安装完成后会在地址栏后面看到对应的图标。

    5

    6

    7

    8ad4b31c8701a18bad38c40d992f07082938fea5

     

  4. 简单示例

    软件设置完毕,扩展也安装。这时候可以尝试一把了。

    创建一个项目,文件test.html。如图所示。然后右击文件选择“Debug

    ‘test.html’”设置默认打开浏览器为Chrome。然后就可以愉快的编写代码了。

    8
    9
    10
    11
    转载:https://jingyan.baidu.com/article/454316ab68ac03f7a7c03ae3.html

 

By柏小白

Nginx:TCP / 正向 / 反向代理 / 负载均衡

写在前面的话

 

在我们日常的工作中,不可能所有的服务都是简单的 HTML 静态网页,nginx 作为轻量级的 WEB 服务器,其实我们将它用于更多的地方还是作为我们网站的入口。不管你是后端接口,还是前端页面,我们让用户的请求都到这个服务。原因大致有以下几个:

1. 集中管理更便于管理。

2. 对外服务都是需要公网 IP 的,需要带宽,如果每台机器都专门配置公网 IP 和带宽,实在是太浪费,可以看看最近我整理的几个云服务商带宽收费情况:

带宽越高,费用越高,这种高还不是成几何式增长。

3. 我们的服务一般不会是单点服务,那么前后端如果需要通信,怎么配置地址,每个都配置?会不会太麻烦了点。

4. 在我们生成服务器中,类似 redis / 数据库这类的服务器一般是不允许联网的为了安全,但是我们是不是每次都必须要登录上服务器去查数据呢?

这还是一些简单的理由,接下来我们来聊聊几种代理,顺便说说他们的应用场景,当然这种设计对于中小型公司是足够了,毕竟没去过大厂,不知道他们的具体架构和实现方式。

 

 

TCP 代理

 

在说 TCP 代理之前我们肯定还是要先了解下代理是什么东西?

代理(proxy)其实就是一个网络信息的中转站,我们使用一个图来表示:

比如这样一个场景,用户在外边,能够通过公网能够访问到云服务商集群中的一台具有公网 IP 的服务器,但是集群内部其它服务器都是不具备公网 IP 的,所以通过公网,我们无法访问到,但是有公网 IP 的服务器恰好在集群中,它的内网网卡和这些没有公网 IP 的机器是可以通信的,这意味着这台机器既可以和用户通信,又可以和后端的集群通信。那我们就得想办法,用户要访问后面的集群,让这个有公网 IP 的服务器帮忙传达一下,那么这个,当个中间人,实现用户和后端集群之间互相传话,于是这个有公网 IP 机器就成为了代理服务器。

 

那啥又是 TCP 代理?

在我们日常访问 WEB 应用,我们都是使用 http://,https://。但是有些服务不是 http 的,比如连接 MySQL 这种,这种明显不是 WEB 服务,所以我们不能像代理 WEB 服务一样代理它。而这类服务,就是 TCP 服务,我们得专门使用 TCP 代理。

我们同时举例 TCP 代理 MySQL 来具体说说如何使用配置。

在我们编译得时候加入了 –with-stream 参数,该参数是我们使用代理不可或缺的。

同样,我们在主配置文件 nginx.conf 中通过 include 来配置单独的目录,用于放置 TCP 配置的配置文件:

值得注意是,TCP 代理不是 HTTP 服务,所有我们的 include 和之前的位置不一样,我们得放在 http 的外层:

复制代码
user  root;
...
http {
    ...
}
stream {
    include tcp/*.conf;
}
复制代码

我们新建 tcp 目录:我这里为了方便使用直接 nginx 使用 root 用户

mkdir /data/services/nginx/conf/tcp

在目录下增加 MySQL 代理配置文件:mysql-proxy-demo.conf

复制代码
upstream MYSQL-PROXY-DEMO {
    hash $remote_addr consistent;
    server 192.168.10.204:3306;
}

server {
    listen  5000;
    proxy_connect_timeout   10s;
    proxy_timeout   300s;
    proxy_pass  MYSQL-PROXY-DEMO;
}
复制代码

简单说明:

1. 在 nginx 中,如果需要对后端多个机器做代理,就需要使用到 upstream,MYSQL-PROXY-DEMO 是给这个 upstream 取的名称,要求唯一。

2. hash xxx 是一种调度模式,当然这里只写了一条 server 记录,所有不存在调度到其他节点问题。

3. server xxx 是一条需要代理的记录,每一条一个 server。

4. server 段和 http WEB 服务类似,但是不需要 server_name。

5. proxy_connect_timeout 为连接超时时间,proxy_timeout 代理超时时间。

6. proxy_pass,代理中最为关键的一句,指名了我们把这个端口代理到哪个服务或者哪个 upstream。后面还会用到。

 

重载 nginx 此时我们测试数据库连接:

可以看到,我们成功的使用代理服务器的 IP + 端口通过 Navict 连接到了其它主机的 MySQL 数据库。

 

 

正向代理

 

正向代理不是我们使用的重点,因为在日常的使用中用的并不多,但是在某些特殊的场景下很有用。

比如有个局域网用户,无法访问互联网,但是局域网中另外一台机器却能够访问到互联网,所有我们可以通过那台机器作为代理去访问互联网。

正向代理的好处在于能够对需要访问的网站隐藏用户的真实信息。

 

这是系统为我提供的解决方案,但是并不是很好用,如果你想直接使用,请直接跳到后面第三方模块搭建正向代理

我们在 vhosts 目录下新建配置:forward-proxy-demo.conf

复制代码
server {
    resolver 8.8.8.8;
    access_log off;
    listen 6080;
    location / {
        proxy_pass $scheme://$http_host$request_uri;
        proxy_set_header HOST $http_host;

        # 配置缓存大小,关闭磁盘缓存读写减少I/O,以及代理连接超时时间
        proxy_buffers 256 4k;
        proxy_max_temp_file_size 0;
        proxy_connect_timeout 30;

        # 配置代理服务器 Http 状态缓存时间
        proxy_cache_valid 200 302 10m;
        proxy_cache_valid 301 1h;
        proxy_cache_valid any 1m;
        proxy_next_upstream error timeout invalid_header http_502;
    }
}
复制代码

简单说明:

红色部分为核心配置,这里我们直到了,在 nginx 中我们是可以通过 proxy_set_header 来处理请求头的。

 

我们在另外一台不能上网的机器上增加配置:

重载 nginx 后我们在不能上网的机器上执行 curl 百度:

curl -I --proxy 192.168.100.111:6080 http://www.baidu.com

结果如下:

当然我们也可以将代理配置定义成环境变量:

export http_proxy=http://192.168.100.111:6080

这样就能直接执行:

 

 

第三方模块搭建正向代理

 

当然,上面的配置都是针对 HTTP 的,对于 HTTPS 代理或者说整个正向代理,我们推荐使用第三方模块:ngx_http_proxy_connect_module

GITHUB 地址:

https://github.com/chobits/ngx_http_proxy_connect_module

将下载的 zip 包上传到服务器,重新编译 nginx,具体方法参考前面的动态添加模块:

https://www.cnblogs.com/Dy1an/p/11227796.html

1. 安装依赖:

yum install -y patch

 

2. 解压打补丁,编译:

从 GITHUB 上面,我们可以看到各个版本的 nginx 对应的补丁版本:

cd /data/packages/nginx
unzip ngx_http_proxy_connect_module-master.zip
cd nginx-1.16.0/
patch -p1 < /data/packages/nginx/ngx_http_proxy_connect_module-master/patch/proxy_connect_rewrite_101504.patch

编译不安装:

复制代码
./configure --prefix=/data/services/nginx \
--user=nginx \
--group=nginx \--with-http_stub_status_module \
--with-http_gzip_static_module \
--with-http_secure_link_module \
--with-http_flv_module \
--with-http_ssl_module \
--with-http_mp4_module \
--with-stream \
--with-http_realip_module \
--with-http_v2_module \
--with-http_sub_module \
--with-http_image_filter_module \
--with-pcre=/data/packages/nginx/pcre-8.43 \
--with-openssl=/data/packages/nginx/openssl-1.1.1c \
--with-zlib=/data/packages/nginx/zlib-1.2.11 \
--add-module=/data/packages/nginx/nginx-upload-module-master \
--add-module=/data/packages/nginx/nginx-upstream-fair-master \
--add-module=/data/packages/nginx/ngx_cache_purge-master \
--add-module=/data/packages/nginx/ngx-fancyindex-master \
--add-module=/data/packages/nginx/echo-nginx-module-master \
--add-module=/data/packages/nginx/ngx_http_proxy_connect_module-master

# 编译
make
复制代码

 

3. 备份替换旧版:

复制代码
# 备份
mv /data/services/nginx/sbin/nginx  /data/backup/nginx/nginx_$(date +%F)

# 更新
cp /data/packages/nginx/nginx-1.16.0/objs/nginx /data/services/nginx/sbin/

# 查看
/data/services/nginx/sbin/nginx -V
复制代码

如图:

 

4. 添加 nginx 正向代理配置:

复制代码
server {
    listen       6080;
    resolver 202.96.128.166;
    resolver_timeout 30s;

    # 代理配置
    proxy_connect;
    proxy_connect_allow            443 563;
    proxy_connect_connect_timeout  10s;
    proxy_connect_read_timeout     10s;
    proxy_connect_send_timeout     10s;

    location / {
        proxy_pass http://$host;
        proxy_set_header Host $host;
    }
}
复制代码

重载配置访问测试:

curl -I --proxy 192.168.100.111:6080  http://www.baidu.com
curl -I --proxy 192.168.100.111:6080  https://www.alipay.com

HTTP 访问结果:

HTTPS 访问结果:

当然,我们也可以设置环境变量:

export http_proxy=http://192.168.100.111:6080
export https_proxy=http://192.168.100.111:6080
no_proxy="localhost,127.0.0.1,localaddress,.localdomain.com"

最后,小结一下:正向代理配置虽然能够满足我们的一定需求,但是有些时候不是很稳定,包括在配置过程中,有时候并不能一次就能访问成功,需要多测试几次。

 

 

反向代理 / 负载均衡

 

反向代理一直是我们 nginx 服务配置的重中之重,我们工作的项目中大部分其实都是围绕着反向代理展开的。如果你用 nginx,你说你没有配置过静态资源 WEB 我相信,但是你没有用过反向代理,那你一定不是做运维的。

那什么是反向代理?

这需要我们和正向代理结合起来理解,我们之前正向代理的时候是我们代理别人的服务让我们能够访问到。

那么反向代理就是代理我们的服务让别人能够访问到,是不是一下子就清晰了。

我们本次测试环境用到了三台机器,一台是我们的 nginx,另外两台是安装了 tomcat 服务的服务器。我们要实现以下图示:

用户访问 nginx 的 8090 端口调度到后端的 TOMCAT 8080 上面去。

至于 TOMCAT 怎么安装部署这里就不做过多说明,这里做了个小处理,在 TOMCAT webapps 下面默认 ROOT 项目的 index.jsp 文件增加了本机 IP 用于区分:

此时我们启动两个 TOMCAT 访问测试:

节点1结果:

 

节点2结果:

 

在 nginx 的 vhosts 目录下增加如下配置:reverse-proxy-demo.conf

复制代码
upstream REVERSE-PROXY-DEMO {
    ip_hash;
    server 192.168.100.112:8080 weight=1 max_fails=3 fail_timeout=10s;
    server 192.168.100.113:8080 weight=1 max_fails=3 fail_timeout=10s;
} 

server {
    listen      8090;
    server_name localhost;
    
    location / {
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_connect_timeout 30;
        proxy_send_timeout 30;
        proxy_read_timeout 30;
        proxy_pass http://REVERSE-PROXY-DEMO;
    }
}
复制代码

如 TCP 代理一般,TCP 代理其实也是反向代理的一种,我们定义的 upstream 的名称要求唯一。

重载 nginx 访问测试:

可以看到请求被分配到了 100.113 这台机器上面去了!

 

我们之前就在使用 upstream,但是 upstream 到底是啥我们一直没说,其实 upstream 就是负载均衡。

从字面上的意思就可以理解,负载均衡就是均衡的,按照特定的调度算法,将请求调度到指定的节点(upstream server)。

upstream 配置说明:

1. nginx 负载均衡调度算法加上我们安装的 fair 模块,大致有以下 4 种:

调度算法 说明
权重轮询(默认) 按照顺序逐一分配到不同的后端。自动剔除失败的机器,使访问不受影响。
ip_hash 每个请求按照 IP 的 Hash 结果分配,使来自同一 IP 的固定访问到同一后端。能解决部分程序 session 没共享的问题
fair 更智能的算法,可以根据页面大小和加载速度进行智能负载,响应快的优先分配。
url_hash 需要按照 nginx hash 模块,按照访问的 URL 定向到某个机器上,能提升后端缓存服务器的效率。

日常用到比较多的就是前三个。

 

2. server 后面的参数:

参数 说明
weight 分配到请求权重,权重比例多高分配到请求的机会越大。
max_fails 最大的失败连接次数。
fail_timeout 等待请求的目标服务器响应的时长。
backup 当所有机器都 down 掉才会调度到这台机器。
down 手动停用某台机器。

这其实就是一些健康检查参数,但是这些参数存在不足,在实际应用中,可以结合 keepalived 来完成,后面会单独说明。

 

server 段关于反向代理的一些配置:

参数 说明
proxy_redirect 重写应答头部的报文
proxy_connect_timeout nginx 将一个请求发送至 upstream server 之前等待的最大时长
proxy_set_header 将发送至 upsream server 的报文的某首部进行重写
proxy_cookie_domain 将 upstream server 通过 Set-Cookie 首部设定的 domain 修改为指定的值,可以为字符串、正则或变量
proxy_cookie_path 将 upstream server 通过 Set-Cookie 首部设定的 path 修改为指定的值,可以为字符串、正则或变量
proxy_hide_header 设定发送给客户端的报文中需要隐藏的首部
proxy_send_timeout 发送至 upstream server 的写操作的超时时长
proxy_read_timeout 发送至 upstream server 的读操作的超时时长
proxy_pass 指定将请求代理至 upstream server 的 URL 路径

其实上面的配置我们可以简单的做个调整就能变成我们反向代理的配置模板。

By柏小白

webstorm功能、操作技巧及快捷键索引

欲先善其事,必先利其器,如题。看到网上一篇介绍webstrom的文章,觉得功能确实强大,也知道为什么阿里巴巴的前端传到github上的文件为啥都有一个 .idea 文件,(传说淘宝内部推荐写js用webstrom)

我们可以理解 IDE 就是集成了很多你想要的功能,或者你不想要的功能。换句话说就是装了很多插件的 editor ,所以到目前为止,我还觉得没必要给它装什么插件。

那么接下来开始介绍webstrom的特色功能:

WebStorm 是 JetBrains 推出的一款商业的 JavaScript 开发工具

WebStorm 是 JetBrains 推出的一款商业的 JavaScript 开发工具
  1. 任何一个编辑器都需要保存(ctrl + s),这是所有win平台上编辑类软件的特点,但是webstorm编辑文件右上角是没有那个熟悉的 * 的。
    好处:省去了ctrl + s之后,在结合Firefox的vim,基本不动鼠标就可以看到结果页面了。
    坏处:没有以前的 * 标识,万一键盘误操作也会被立即存储。
  2. 任何一个编辑器只要文件关闭了就没有历史记录了,但是webstorm有。 vcs->Local History -> Show History(快捷键:ALT+~ -〉7)
    好处:只要webstorm不关闭,你的文件随时可以返回到之前的操作(这也是为啥在 webstorm 里ctrl+y是删除一行的原因了)。
    坏处:webstorm关闭重启后这些历史记录就没有了;还有一个坏处就是由此带来的内存消耗也必然比较大。
  3. 任何一个编辑器,除了服务器svn之外,没有本地版本,但是webstorm提供一个本地文件修改历史记录(快捷键:ALT+SHIFT+c)。也可以 Ctrl + E 弹出最近打开的文件。
    好处:相当于本地svn。
    坏处:内存消耗也必然比较大。
  4. 集成了zencoding,html5,ftp,即时编辑(chrome),自动完成,基于Mozilla的JavaScript调试器,JSLint、Less支持、CoffeeScript支持、Node.JS、单元测试、集成git和svn版本控制等特性。
  5. 在编写CSS中,会智能的提示各种文件以及图片的路径,就不用再去确认这个文件是否存在了。

其他的特性就介绍了,想了解更多可以去webstrom官网继续寻找。看完了这么介绍以及那么多给力的特性,是不是有种冲动马上试一试这款传说中的前端神器啊?嗯。

首先在官方下载页下载webstrom后,点击安装,然后一直下一步,最后会出现需要注册码界面,因为webstrom是商业的IDE,而天朝的屌丝前端当然没那么钱去买那么昂贵的神器了,不用担心,我们可以用WebStorm的密钥来学习使用这款前端神器。嘿嘿~~~~

安装成功后,肯定会有些不适,就像我一样觉得这配色真的太戳了。还有一些设置也不是我也要的。那么以下我就列出我习惯的设置。

webstorm安装后的一些设置技巧:

  • 如何更改主题(字体&配色):
    File -> settings -> Editor -> colors&fonts -> scheme name.主题下载地址
  • 如何让webstorm启动的时候不打开工程文件:
    File -> Settings->General去掉Reopen last project on startup.
  • 如何完美显示中文:
    File -> Settings->Appearance中勾选Override default fonts by (not recommended),设置Name:NSimSun,Size:12
  • 如何显示行号:
    File -> Settings->Editor,”Show line numbers”打上勾,就显示行号了
  • 如何代码自动换行:
    File -> settings -> Editor “Use Soft Wraps in editor” 打上钩,代码就自动换行了
  • 如何点击光标,显示在本行末尾:
    File -> Settings->Editor “Allow placement of caret after end of line”去掉勾就行了。
  • 如何修改快键键:
    File -> Settings->Keymap,然后双击要修改快捷的功能会有提示框出来,按提示操作
  • 换成自己熟悉编辑器的快键键:
    File ->Settings->Keymap,支持像Visual Studio、Eclipse、NetBeans这样的主流IDE。
  • javascript类库提示。
    File -> settings -> Javascript -> Libraries -> 然后在列表里选择自己经常用到的javascript类库,最后Download and Install就ok了.
  • 在开发js时发现,需要ctrl + return 才能选候选项:
    File -> Setting -> Editor -> Code Completion -> Preselect the first suggestion: “Smart” 改为 “Always”
  • js提示比较迟缓
    File -> Code Completion -> Autopopup in 下 1000改为0
  • git配置:
    File -> settings -> Editor -> github,进去改github的账户,如果没有git则不需要.
  • 插件安装:
    File ->plugins,然后就选择给力的插件们再安装.(“css-X-fire”插件,用于当使用firebug修改css属性时,编辑器内的css代码也会发生变化。)
  • 以后更新

webstorm使用心得

  • 收藏夹功能:
    当工程目录很庞大时,有些子目录很经常打开,但层级又很深,这时候可以把目录添加到收藏夹里面,添加成功后,左侧有个“Favorites”菜单
  • 面包屑导航:
    除了左侧的工程页面,可以选择目录之外,在顶部菜单下有一个类似网站面包屑导航一样的目录也可以实现相同功。点击每个目录就会有下拉菜单显示其下的子目录,很实用.
  • 构造器界面:
    注释符合格式的话就会出现。如果是js文件则是js类的函数和对象;css文件的话则是这个css文件的概括;html文件的话则是节点的结构图。话说这几个就是为了方便查看代码的结构性.
  • todo界面:
    给代码加todo注释就会出现这个界面
  • 双栏代码界面:
    右击代码选项卡上的文件,然后右键 -> spilt vertically(左右两屏)或者spilt horizontally(上下两屏)
  • 本地历史功能:
    找回代码的好办法

git使用

webstorm中只集成了git的常用操作,并不能完全替代命令行工具。在界面的右下角可以查看处于哪个git分支。也可以在上面点击切换或者新建分支。

  • 查看当前代码与版本库代码的差异:
    右击代码界面任意区域,选择git -> compare with然后选择要比较的版本库。

webstorm的快捷键说明:

Editing编辑相关快捷键

  • Ctrl + Space:
    Basic code completion (the name of any class, method or variable) 基本代码完成(任何类、函数或者变量名称),改为Alt+S
  • Ctrl + Shift + Enter:
    Complete statement 补全当前语句
  • Ctrl + P:
    Parameter info (within method call arguments) 参数信息 包括方法调用参数
  • Ctrl + mouse over code
    Brief Info 简单信息
  • Ctrl + F1
    Show description of error or warning at caret 显示光标所在位置的错误信息或者警告信息
  • Alt + Insert
    Generate code…(Getters, Setters, Constructors)新建一个文件或者生成代码,…构造函数,可以创建类里面任何字段的getter与setter方法
  • Ctrl + O
    Override methods 重载方法
  • Ctrl + I
    Implement methods 实现方法
  • Ctrl + Alt + T
    Surround with…(if, else, try, catch, for, etc)用 * 来围绕选中的代码行,( * 包括 if 、 while 、 try catch 等)
  • Ctrl + /
    Comment/uncomment with line comment 行注释/取消行注释
  • Ctrl + Shift + /
    Comment/uncomment with block comment 块注释/取消块注释
  • Ctrl + W
    Select successively increasing code blocks 选择代码块,一般是增量选择
  • Ctrl + Shift + W
    Decrease current selection to previous state 上个快捷键的回退,减量选择代码
  • Alt + Q
    Context info 上下文信息
  • Alt + Enter
    Show intention actions and quick-fixes 意图行动,快速见效
  • Ctrl + Alt + L
    Reformat code 根据模板格式对代码格式化
  • Tab/ Shift + Tab
    Indent/unindent selected lines 对所选行进行缩排处理/撤销缩排处理
  • Ctrl + X or Shift + Delete
    Cut current line or selected block to clipboard 剪切当前行或所选代码块到剪切板
  • Ctrl + C or Ctrl + Insert
    Copy current line or selected block to chipboard 拷贝当前行或者所选代码块到剪切板
  • Ctrl + V or Shift + Insert
    Paste from clipboard 粘贴剪切板上的内容
  • Ctrl + Shift + V
    Paste from recent buffers 粘贴缓冲器中最新的内容
  • Ctrl + D
    Duplicate current line or selected block 复制当前行或者所选代码块
  • Ctrl + Y
    Delete line at caret 删除光标所在位置行
  • Ctrl + Shift + J
    Smart line join(HTML and JavaScript only)加入智能行 (HTML 和JavaScript)
  • Ctrl + Enter
    Smart line split(HTML and JavaScript only)分离智能行 (HTML 和JavaScript)
  • Shift + Enter
    Start new line 另起一行
  • Ctrl + Shift + U
    Toggle case for word at caret or selected block 光标所在位置大小写转换
  • Ctrl + Shift + ]/[
    Select till code block end/start 选择直到代码块结束/开始
  • Ctrl + Delete
    Delete to word end 删除文字结束
  • Ctrl + Backspace
    Delete to word start 删除文字开始
  • Ctrl + NumPad+/-
    Expand/collapse code block 扩展/缩减代码块
  • Ctrl + Shift+ NumPad+
    Expand all 扩张所有
  • Ctrl + Shift+ NumPad-
    Collapse 缩减所有
  • Ctrl + F4
    Close active editor tab 关闭活跃编辑标签

Search/replace搜索/替代相关快捷键

  • Ctrl + F
    Find 当前文件内快速查找代码
  • Ctrl + Shift + F
    Find in path 指定文件内寻找路径
  • F3
    Find next 查找下一个
  • Shift + F3
    Find previous 查找上一个
  • Ctrl + R
    Replace 当前文件内代码替代
  • Ctrl + Shift + R
    Replace in path 指定文件内代码批量替代

Usage Search搜索相关快捷键

  • Alt + F7/Ctrl + F7
    Find usages/Find usages in file 找到使用/在文件找到使用
  • Ctrl + Shift + F7
    Highlight usages in file文件中精彩使用
  • Ctrl + Alt + F7
    Show usages 显示使用

Running运行

  • Alt + Shift + F10
    Select configuration and run 选择构架,运行
  • Alt + Shift + F9
    Select configuration and debug 选择构架,修补漏洞
  • Shift + F10
    Run 运行
  • Shift + F9
    Debug 修补漏洞
  • Ctrl + Shift + F10
    Run context configuration from editor 从编辑运行内容构架
  • Ctrl + Shift + X
    Run command line 运行命令行

Debugging Debugging相关快捷键

  • F8
    Step over 不进入函数
  • F7
    Step into 单步执行
  • Shift + F7
    Smart step into 智能单步执行
  • Shift + F8
    Step out 跳出
  • Alt + F9
    Run to cursor 运行到光标处
  • Alt+ F8
    Evaluate expression 评估表达
  • F9
    Resume program 重新开始程序
  • Ctrl + F8
    Toggle breakpoint 切换断点
  • Ctrl + Shift + F8
    View breakpoints 查看断点

Navigation 定位相关快捷键

  • Ctrl + N
    Go to class跳转到指定类
  • Ctrl + Shift + N
    Go to file 通过文件名快速查找工程内的文件
  • Ctrl + Alt +Shift + N
    Go to symbol 通过一个字符查找函数位置
  • Alt + Right/ left
    Go to next/ previous editor tab 进入下一个/ 上一个编辑器选项
  • F12
    Go back to previous tool window 进入上一个工具窗口
  • Esc
    Go to editor(from tool window) 从工具窗口进入编辑器
  • Shift + Esc
    Hide active or last active window 隐藏活动窗口
  • Ctrl + Shift + F4
    Close active run/message/find/…tab 关闭活动….标签
  • Ctrl + G
    Go to line 跳转到第几行
  • Ctrl + E
    Recent files popup 弹出最近打开的文件
  • Ctrl + Alt + Left/Right
    Navigate back/forward 导航前进/后退
  • Ctrl + Shift + Backspace
    Navigate to last edit location 向最近编辑定位导航
  • Alt + F1
    Select current file or symbol in any view 查找当前选中的代码或文件在其他界面模块的位置
  • Ctrl + B or Ctrl + Click
    Go to declaration跳转到定义处
  • Ctrl + Alt + B
    Go to implementation(s) 跳转方法实现处
  • Ctrl + Shift + B
    Go to type declaration 跳转方法定义处
  • Ctrl + Shift + I
    Open quick definition lookup 打开定义快速查找
  • Ctrl + U
    Go to super-method/super-class 跳转方法/超阶级
  • Alt + Up/Down
    Go to previous/next method 在方法间快速移动定位
  • Ctrl + ]/[
    Move to code block end/start 跳转到编码块结束/开始
  • Ctrl + F12
    File structure popup 文件结构弹出
  • Ctrl + H
    Type hierarchy 类型层次
  • Ctrl + Alt + H
    Call hierarchy 调用层次结构
  • F2/ Shift + F2
    Next/previous highlighted error 跳转到后一个/前一个错误,高亮错误或警告快速定位,使用这个快捷键可以快捷在出错的语句之间进行跳转。
  • F4/Ctrl + Enter
    Edit source/ View source 编辑源代码/查看源代码
  • Alt + Home
    Show navigation bar 显示导航栏
  • F11
    Toggle bookmark 切换标记
  • Ctrl + F11
    Toggle bookmark with mnemonic 采用记忆切换标记
  • Ctrl + #[0-9]
    Go to numbered bookmark 跳转到带编号的标记
  • Shift + F11
    Show bookmark 显示标记

Refactoring 重构相关快捷键

  • F5
    Copy 拷贝
  • F6
    Move 移动
  • Alt + Delete
    Safe Delete 安全删除
  • Shift + F6
    Rename 重新命名
  • Ctrl + Alt + N
    Inline Variable 嵌入变量
  • Ctrl + Alt + M
    Extract Method( Javascript only) 提取函数
  • Ctrl + Alt + V
    Introduce Variable 引入变量
  • Ctrl + Alt + F
    Introduce Field 引入域
  • Ctrl + Alt + C
    Introduce Constant 引入常量

VCS/Local History 版本控制系统/ 本地历史相关快捷键

  • Alt + BackQuote( )
    ‘VCS’quick popup 快速弹出 VCS
  • Ctrl + K
    Commit project to VCS 提交项目至VCS
  • Ctrl + T
    Update project from VCS 从VCS 更新项目
  • Alt + Shift + C
    View recent changes 查看最新改变

General 常用的相关快捷键

  • Ctrl + Shift +A
    Find action 查找并调用编辑器的功能
  • Alt + #[0-9]
    Open corresponding tool window 快速切换打开界面模块
  • Ctrl + Alt + F11
    Toggle full screen mode 切换全屏模式
  • Ctrl + Shift + F12
    Toggle maximizing editor 切换最大化编辑器
  • Alt + Shift + F
    Add to Favorites 将当前文件添至收藏夹
  • Alt + Shift + I
    Inspect current file with current profile 使用当前属性检查当前文件
  • Ctrl + BackQuote( )
    Quick switch current scheme 快速转换现有组合
  • Ctrl + Alt + S
    Open setting dialog 打开设置对话框
  • Ctrl + Tab
    Switch between tabs and tool window 标签和工具窗的转换(与windows快捷键冲突)

我把以上快捷做成pdf了,大家可以点击WebStorm.快捷键pdf分享

文章内容转载于http://www.html5jscss.com/ide-webstrom.html

By柏小白

九种跨域方式实现原理(完整版)

前言

前后端数据交互经常会碰到请求跨域,什么是跨域,以及有哪几种跨域方式,这是本文要探讨的内容。

本文完整的源代码请猛戳github博客,纸上得来终觉浅,建议大家动手敲敲代码。

一、什么是跨域?

1.什么是同源策略及其限制内容?

同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。所谓同源是指”协议+域名+端口”三者相同,即便两个不同的域名指向同一个ip地址,也非同源。

url的组成

同源策略限制内容有:

  • Cookie、LocalStorage、IndexedDB 等存储性内容
  • DOM 节点
  • AJAX 请求发送后,结果被浏览器拦截了

但是有三个标签是允许跨域加载资源:

  • <img src=XXX>
  • <link href=XXX>
  • <script src=XXX>

2.常见跨域场景

当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。常见跨域场景如下图所示:

特别说明两点:

第一:如果是协议和端口造成的跨域问题“前台”是无能为力的。

第二:在跨域问题上,仅仅是通过“URL的首部”来识别而不会根据域名对应的IP地址是否相同来判断。“URL的首部”可以理解为“协议, 域名和端口必须匹配”

这里你或许有个疑问:请求跨域了,那么请求到底发出去没有?

跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。你可能会疑问明明通过表单的方式可以发起跨域请求,为什么 Ajax 就不会?因为归根结底,跨域是为了阻止用户读取到另一个域名下的内容,Ajax 可以获取响应,浏览器认为这不安全,所以拦截了响应。但是表单并不会获取新的内容,所以可以发起跨域请求。同时也说明了跨域并不能完全阻止 CSRF,因为请求毕竟是发出去了。

二、跨域解决方案

1.jsonp

1) JSONP原理

利用 <script> 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP请求一定需要对方的服务器做支持才可以。

2) JSONP和AJAX对比

JSONP和AJAX相同,都是客户端向服务器端发送请求,从服务器端获取数据的方式。但AJAX属于同源策略,JSONP属于非同源策略(跨域请求)

3) JSONP优缺点

JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持get方法具有局限性,不安全可能会遭受XSS攻击。

4) JSONP的实现流程

  • 声明一个回调函数,其函数名(如show)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的data)。
  • 创建一个<script>标签,把那个跨域的API数据接口地址,赋值给script的src,还要在这个地址中向服务器传递该函数名(可以通过问号传参:?callback=show)。
  • 服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是show,它准备好的数据是show('我不爱你')
  • 最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),对返回的数据进行操作。

在开发中可能会遇到多个 JSONP 请求的回调函数名是相同的,这时候就需要自己封装一个 JSONP函数。

// index.html
function jsonp({ url, params, callback }) {
  return new Promise((resolve, reject) => {
    let script = document.createElement('script')
    window[callback] = function(data) {
      resolve(data)
      document.body.removeChild(script)
    }
    params = { ...params, callback } // wd=b&callback=show
    let arrs = []
    for (let key in params) {
      arrs.push(`${key}=${params[key]}`)
    }
    script.src = `${url}?${arrs.join('&')}`
    document.body.appendChild(script)
  })
}
jsonp({
  url: 'http://localhost:3000/say',
  params: { wd: 'Iloveyou' },
  callback: 'show'
}).then(data => {
  console.log(data)
})
复制代码

上面这段代码相当于向http://localhost:3000/say?wd=Iloveyou&callback=show这个地址请求数据,然后后台返回show('我不爱你'),最后会运行show()这个函数,打印出’我不爱你’

// server.js
let express = require('express')
let app = express()
app.get('/say', function(req, res) {
  let { wd, callback } = req.query
  console.log(wd) // Iloveyou
  console.log(callback) // show
  res.end(`${callback}('我不爱你')`)
})
app.listen(3000)
复制代码

5) jQuery的jsonp形式

JSONP都是GET和异步请求的,不存在其他的请求方式和同步请求,且jQuery默认就会给JSONP的请求清除缓存。

$.ajax({
url:"http://crossdomain.com/jsonServerResponse",
dataType:"jsonp",
type:"get",//可以省略
jsonpCallback:"show",//->自定义传递给服务器的函数名,而不是使用jQuery自动生成的,可省略
jsonp:"callback",//->把传递函数名的那个形参callback,可省略
success:function (data){
console.log(data);}
});
复制代码

2.cors

CORS 需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现

浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。

服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。

虽然设置 CORS 和前端没什么关系,但是通过这种方式解决跨域问题的话,会在发送请求时出现两种情况,分别为简单请求复杂请求

1) 简单请求

只要同时满足以下两大条件,就属于简单请求

条件1:使用下列方法之一:

  • GET
  • HEAD
  • POST

条件2:Content-Type 的值仅限于下列三者之一:

  • text/plain
  • multipart/form-data
  • application/x-www-form-urlencoded

请求中的任意 XMLHttpRequestUpload 对象均没有注册任何事件监听器; XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。

2) 复杂请求

不符合以上条件的请求就肯定是复杂请求了。 复杂请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求,该请求是 option 方法的,通过该请求来知道服务端是否允许跨域请求。

我们用PUT向后台请求时,属于复杂请求,后台需做如下配置:

// 允许哪个方法访问我
res.setHeader('Access-Control-Allow-Methods', 'PUT')
// 预检的存活时间
res.setHeader('Access-Control-Max-Age', 6)
// OPTIONS请求不做任何处理
if (req.method === 'OPTIONS') {
  res.end() 
}
// 定义后台返回的内容
app.put('/getData', function(req, res) {
  console.log(req.headers)
  res.end('我不爱你')
})
复制代码

接下来我们看下一个完整复杂请求的例子,并且介绍下CORS请求相关的字段

// index.html
let xhr = new XMLHttpRequest()
document.cookie = 'name=xiamen' // cookie不能跨域
xhr.withCredentials = true // 前端设置是否带cookie
xhr.open('PUT', 'http://localhost:4000/getData', true)
xhr.setRequestHeader('name', 'xiamen')
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4) {
    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
      console.log(xhr.response)
      //得到响应头,后台需设置Access-Control-Expose-Headers
      console.log(xhr.getResponseHeader('name'))
    }
  }
}
xhr.send()
复制代码
//server1.js
let express = require('express');
let app = express();
app.use(express.static(__dirname));
app.listen(3000);
复制代码
//server2.js
let express = require('express')
let app = express()
let whitList = ['http://localhost:3000'] //设置白名单
app.use(function(req, res, next) {
  let origin = req.headers.origin
  if (whitList.includes(origin)) {
    // 设置哪个源可以访问我
    res.setHeader('Access-Control-Allow-Origin', origin)
    // 允许携带哪个头访问我
    res.setHeader('Access-Control-Allow-Headers', 'name')
    // 允许哪个方法访问我
    res.setHeader('Access-Control-Allow-Methods', 'PUT')
    // 允许携带cookie
    res.setHeader('Access-Control-Allow-Credentials', true)
    // 预检的存活时间
    res.setHeader('Access-Control-Max-Age', 6)
    // 允许返回的头
    res.setHeader('Access-Control-Expose-Headers', 'name')
    if (req.method === 'OPTIONS') {
      res.end() // OPTIONS请求不做任何处理
    }
  }
  next()
})
app.put('/getData', function(req, res) {
  console.log(req.headers)
  res.setHeader('name', 'jw') //返回一个响应头,后台需设置
  res.end('我不爱你')
})
app.get('/getData', function(req, res) {
  console.log(req.headers)
  res.end('我不爱你')
})
app.use(express.static(__dirname))
app.listen(4000)
复制代码

上述代码由http://localhost:3000/index.htmlhttp://localhost:4000/跨域请求,正如我们上面所说的,后端是实现 CORS 通信的关键。

3.postMessage

postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:

  • 页面和其打开的新窗口的数据传递
  • 多窗口之间消息传递
  • 页面与嵌套的iframe消息传递
  • 上面三个场景的跨域数据传递

postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递

otherWindow.postMessage(message, targetOrigin, [transfer]);

  • message: 将要发送到其他 window的数据。
  • targetOrigin:通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串”*”(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。
  • transfer(可选):是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。

接下来我们看个例子: http://localhost:3000/a.html页面向http://localhost:4000/b.html传递“我爱你”,然后后者传回”我不爱你”。

// a.html
  <iframe src="http://localhost:4000/b.html" frameborder="0" id="frame" onload="load()"></iframe> //等它加载完触发一个事件
  //内嵌在http://localhost:3000/a.html
    <script>
      function load() {
        let frame = document.getElementById('frame')
        frame.contentWindow.postMessage('我爱你', 'http://localhost:4000') //发送数据
        window.onmessage = function(e) { //接受返回数据
          console.log(e.data) //我不爱你
        }
      }
    </script>
复制代码
// b.html
  window.onmessage = function(e) {
    console.log(e.data) //我爱你
    e.source.postMessage('我不爱你', e.origin)
 }
复制代码

4.websocket

Websocket是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。WebSocket和HTTP都是应用层协议,都基于 TCP 协议。但是 WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向对方发送或接收数据。同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。

原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。

我们先来看个例子:本地文件socket.html向localhost:3000发生数据和接受数据

// socket.html
<script>
    let socket = new WebSocket('ws://localhost:3000');
    socket.onopen = function () {
      socket.send('我爱你');//向服务器发送数据
    }
    socket.onmessage = function (e) {
      console.log(e.data);//接收服务器返回的数据
    }
</script>
复制代码
// server.js
let express = require('express');
let app = express();
let WebSocket = require('ws');//记得安装ws
let wss = new WebSocket.Server({port:3000});
wss.on('connection',function(ws) {
  ws.on('message', function (data) {
    console.log(data);
    ws.send('我不爱你')
  });
})
复制代码

5. Node中间件代理(两次跨域)

实现原理:同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略。 代理服务器,需要做以下几个步骤:

  • 接受客户端请求 。
  • 将请求 转发给服务器。
  • 拿到服务器 响应 数据。
  • 将 响应 转发给客户端。
    ” alt=”” data-src=”https://user-gold-cdn.xitu.io/2019/1/17/1685c5bed77e7788?imageView2/0/w/1280/h/960/format/webp/ignore-error/1″ data-width=”600″ data-height=”237″ />

我们先来看个例子:本地文件index.html文件,通过代理服务器http://localhost:3000向目标服务器http://localhost:4000请求数据。

// index.html(http://127.0.0.1:5500)
 <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script>
      $.ajax({
        url: 'http://localhost:3000',
        type: 'post',
        data: { name: 'xiamen', password: '123456' },
        contentType: 'application/json;charset=utf-8',
        success: function(result) {
          console.log(result) // {"title":"fontend","password":"123456"}
        },
        error: function(msg) {
          console.log(msg)
        }
      })
     </script>
复制代码
// server1.js 代理服务器(http://localhost:3000)
const http = require('http')
// 第一步:接受客户端请求
const server = http.createServer((request, response) => {
  // 代理服务器,直接和浏览器直接交互,需要设置CORS 的首部字段
  response.writeHead(200, {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': '*',
    'Access-Control-Allow-Headers': 'Content-Type'
  })
  // 第二步:将请求转发给服务器
  const proxyRequest = http
    .request(
      {
        host: '127.0.0.1',
        port: 4000,
        url: '/',
        method: request.method,
        headers: request.headers
      },
      serverResponse => {
        // 第三步:收到服务器的响应
        var body = ''
        serverResponse.on('data', chunk => {
          body += chunk
        })
        serverResponse.on('end', () => {
          console.log('The data is ' + body)
          // 第四步:将响应结果转发给浏览器
          response.end(body)
        })
      }
    )
    .end()
})
server.listen(3000, () => {
  console.log('The proxyServer is running at http://localhost:3000')
})
复制代码
// server2.js(http://localhost:4000)
const http = require('http')
const data = { title: 'fontend', password: '123456' }
const server = http.createServer((request, response) => {
  if (request.url === '/') {
    response.end(JSON.stringify(data))
  }
})
server.listen(4000, () => {
  console.log('The server is running at http://localhost:4000')
})
复制代码

上述代码经过两次跨域,值得注意的是浏览器向代理服务器发送请求,也遵循同源策略,最后在index.html文件打印出{"title":"fontend","password":"123456"}

6.nginx反向代理

实现原理类似于Node中间件代理,需要你搭建一个中转nginx服务器,用于转发请求。

使用nginx反向代理实现跨域,是最简单的跨域方式。只需要修改nginx的配置即可解决跨域问题,支持所有浏览器,支持session,不需要修改任何代码,并且不会影响服务器性能。

实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。

先下载nginx,然后将nginx目录下的nginx.conf修改如下:

// proxy服务器
server {
    listen       81;
    server_name  www.domain1.com;
    location / {
        proxy_pass   http://www.domain2.com:8080;  #反向代理
        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
        index  index.html index.htm;

        # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
        add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*
        add_header Access-Control-Allow-Credentials true;
    }
}
复制代码

最后通过命令行nginx -s reload启动nginx

// index.html
var xhr = new XMLHttpRequest();
// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;
// 访问nginx中的代理服务器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();
复制代码
// server.js
var http = require('http');
var server = http.createServer();
var qs = require('querystring');
server.on('request', function(req, res) {
    var params = qs.parse(req.url.substring(2));
    // 向前台写cookie
    res.writeHead(200, {
        'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'   // HttpOnly:脚本无法读取
    });
    res.write(JSON.stringify(params));
    res.end();
});
server.listen('8080');
console.log('Server is running at port 8080...');
复制代码

7.window.name + iframe

window.name属性的独特之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。

其中a.html和b.html是同域的,都是http://localhost:3000;而c.html是http://localhost:4000

 // a.html(http://localhost:3000/b.html)
  <iframe src="http://localhost:4000/c.html" frameborder="0" onload="load()" id="iframe"></iframe>
  <script>
    let first = true
    // onload事件会触发2次,第1次加载跨域页,并留存数据于window.name
    function load() {
      if(first){
      // 第1次onload(跨域页)成功后,切换到同域代理页面
        let iframe = document.getElementById('iframe');
        iframe.src = 'http://localhost:3000/b.html';
        first = false;
      }else{
      // 第2次onload(同域b.html页)成功后,读取同域window.name中数据
        console.log(iframe.contentWindow.name);
      }
    }
  </script>
复制代码

b.html为中间代理页,与a.html同域,内容为空。

 // c.html(http://localhost:4000/c.html)
  <script>
    window.name = '我不爱你'  
  </script>
复制代码

总结:通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。

8.location.hash + iframe

实现原理: a.html欲与c.html跨域相互通信,通过中间页b.html来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。

具体实现步骤:一开始a.html给c.html传一个hash值,然后c.html收到hash值后,再把hash值传递给b.html,最后b.html将结果放到a.html的hash值中。 同样的,a.html和b.html是同域的,都是http://localhost:3000;而c.html是http://localhost:4000

 // a.html
  <iframe src="http://localhost:4000/c.html#iloveyou"></iframe>
  <script>
    window.onhashchange = function () { //检测hash的变化
      console.log(location.hash);
    }
  </script>
复制代码
 // b.html
  <script>
    window.parent.parent.location.hash = location.hash 
    //b.html将结果放到a.html的hash值中,b.html可通过parent.parent访问a.html页面
  </script>
复制代码
 // c.html
 console.log(location.hash);
  let iframe = document.createElement('iframe');
  iframe.src = 'http://localhost:3000/b.html#idontloveyou';
  document.body.appendChild(iframe);
复制代码

9.document.domain + iframe

该方式只能用于二级域名相同的情况下,比如 a.test.comb.test.com 适用于该方式。 只需要给页面添加 document.domain ='test.com' 表示二级域名都相同就可以实现跨域。

实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。

我们看个例子:页面a.zf1.cn:3000/a.html获取页面b.zf1.cn:3000/b.html中a的值

// a.html
<body>
 helloa
  <iframe src="http://b.zf1.cn:3000/b.html" frameborder="0" onload="load()" id="frame"></iframe>
  <script>
    document.domain = 'zf1.cn'
    function load() {
      console.log(frame.contentWindow.a);
    }
  </script>
</body>
复制代码
// b.html
<body>
   hellob
   <script>
     document.domain = 'zf1.cn'
     var a = 100;
   </script>
</body>
复制代码

三、总结

  • CORS支持所有类型的HTTP请求,是跨域HTTP请求的根本解决方案
  • JSONP只支持GET请求,JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
  • 不管是Node中间件代理还是nginx反向代理,主要是通过同源策略对服务器不加限制。
  • 日常工作中,用得比较多的跨域方案是cors和nginx反向代理

给大家推荐一个好用的BUG监控工具Fundebug,欢迎免费试用!

作者:浪里行舟
链接:https://juejin.im/post/5c23993de51d457b8c1f4ee1
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。