转自:http://www.thinksaas.cn/topics/0/34/34536.html

前端同学李雷和后台同学韩梅梅分别在自己电脑上进行开发,后台接口写好的时候,李雷改动完就把前端代码上传到gitlab,然后在测试机上从gitlab上拉下来,然后在测试机上移动最新代码,最后回到本机刷新页面。有时候碰到网速不好的情况传个git传了半天,或者李雷刚上传完发现少写了一个单词,加上再传上一看尼玛单词又写错了。对于实时需要改动代码的李雷同学而言

另外一种方案是李雷和韩梅梅分别在自己电脑上进行开发,韩梅梅写完一个接口,李雷马上在本地调用接口进行请求,有需要改动的地方,改完按下F5,看到的就是最新的。perfect!

既然这样,为什么不选用方便快捷的后者呢。后者会遇到哪些问题?这里就要好好扯一下跨域了。

什么是跨域?

浏览器的同源策略限制了一个源(origin)中加载文本或脚本与来自其它源(origin)中资源的交互方式。如果两个页面的协议(protocol)、端口(如果指定)、和主机任一不相同,则他们就不是同源的,在他们之间进行请求则认为是跨域。

跨域的解决办法

跨域的解决方案主要有JSONP、改变domain、CORS等等,因为项目适用于使用CORS,所以我们在项目中使用此方案,这里也只讨论CORS。

CORS

CORS(Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。 
换句话说,就是服务器在响应头里设置好允许哪些地址访问(通过Access-Control-Allow-Origin设置),当浏览器收到服务端响应时,查看这个字段,如果包含请求方的地址或设置为了*,则允许此次跨域。

那么,我们在项目里只需要在服务端设置好Access-Control-Allow-Origin头,即可允许前端同学发起的请求跨域。

对于php而言,即设置:

header('Access-Control-Allow-Origin: *');
  • 1

这样就能愉快的跨域了!

看到这里的时候,国存和昕晨一定是这个表情 

国存:真的假的,那跟你调试的时候怎么老是收不到你的数据,前面还多的一个OPTIONS请求是什么狗? 
昕晨:我用jQuery明明就请求到数据,你用angularJS就不行,你这不讲道理。

讲道理的话是应该没有跨域的问题了,但是!这里牵扯到了跨域的其他限制以及jQuery和angularJS的Ajax请求的差异! 

下面来详细说明这些坑:

jQuery和angularJS的Ajax请求的差异

一、为什么有时候用jQuery请求的到数据,用angularJS请求就拿不到?

这在于jQuery和angularJS序列化和传输数据有差异,通过jQuery请求时,请求头的Content-TypeContent-Type: x-www-form-urlencoded,即类似name=lilei&age=20,而angularJS使用的是Content-Type: application/json,类似{ "name": "lilei", "age": "20" }

而在php里,我们一开始是通过$_POST方法来获取参数的,在php官网上对$_POST的文档里,我在下面的User Contributed Notes中找到这么一段: 

那么也就是说,angularJS的Content-Type: application/json是不能通过$_POST方法获取的。在Make AngularJS $http service behave like jQuery.ajax()这篇文章里面,作者也提到

AngularJS, however, transmits data using Content-Type: application/json and{ "foo": "bar", "baz": "moe" } JSON serialization, which unfortunately some Web server languages—notably PHP—do not unserialize natively.

作者也提到了解决办法: 
1. 在客户端改变angularJS默认传输格式为Content-Type: x-www-form-urlencoded,并改变传输数据的格式。 
2. 在php中使用$params = json_decode(file_get_contents('php://input'));来读取输入流。 
(具体见本文末中的原文链接)

二、为什么用angularJS进行POST请求之前还发送了一个OPTIONS请求

在MDN上对HTTP访问控制中有这样一段:

跨源资源共享标准通过新增一系列 HTTP 头,让服务器能声明那些来源可以通过浏览器访问该服务器上的资源。另外,对那些会对服务器数据造成破坏性影响的 HTTP 请求方法(特别是 GET 以外的 HTTP 方法,或者搭配某些MIME类型的POST请求),标准强烈要求浏览器必须先以 OPTIONS 请求方式发送一个预请求(preflight request),从而获知服务器端对跨源请求所支持 HTTP 方法。 在确认服务器允许该跨源请求的情况下,以实际的 HTTP 请求方法发送那个真正的请求。服务器端也可以通知客户端,是不是需要随同请求一起发送信用信息(包括 Cookies 和 HTTP 认证相关数据)。

也就是说,当进行POST请求时,浏览器应该先发送OPTIONS请求,以判断服务器接受哪些跨源请求的方法。

那为什么jQuery没有发出OPTIONS请求呢?这是因为jQuery发出的请求是一个简单请求,所谓的简单,是指:

  1. 只使用 GET, HEAD 或者 POST 请求方法。如果使用 POST 向服务器端传送数据,则数据类型(Content-Type)只能是 application/x-www-form-urlencodedmultipart/form-data 或text/plain中的一种。
  2. 不会使用自定义请求头(类似于 X-Modified 这种)。

jQuery的请求幸运的成为了一个简单请求,而很不幸的,angularJS的请求是一个复杂请求。

部分CORS请求并不是直接就可以向服务器发送的,不能直接发的这部分请求我们就称为复杂请求

WTF??? 什么是复杂请求呢

  1. It uses methods other than GET, HEAD or POST. Also, if POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded,multipart/form-data, or text/plain, e.g. if the POST request sends an XML payload to the server using application/xml or text/xml, then the request is preflighted.
  2. It sets custom headers in the request (e.g. the request uses a header such as X-PINGOTHER)

然后,AngularJS就被干掉了。

三、为什么还要在服务端加上Access-Control-Allow-Headers: Content-Type

对于jQuery是不必加的,但是使用AngularJS必须要加。

来看一下AngularJS发送的OPTIONS 请求:

OPTIONS /index.php/accountmanager/list_all HTTP/1.1
Host: 10.170.202.246:2872
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://localhost:9000
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36
Access-Control-Request-Headers: accept, content-type
Accept: */*
Referer: http://localhost:9000/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这是使用angularJS进行请求时的预请求,可以看到里面有几个特别的请求头’Access-Control-Request-Method: POST’、Access-Control-Request-Headers: accept, content-type
第一个头说明本次实际跨域请求方法是POST请求(告诉服务器:等会我就要发POST了),第二个头用来告诉服务器在实际请求时会携带的自定义头信息,可以看到自定义头信息里面竟然有一个content-type。这是因为application/json实际上不是浏览器原生支持的方法。所以服务器也需要设置我允许哪些自定义头信息,即Access-Control-Allow-Headers

最后,言而总之,总而言之,跨域开发并不可怕,搞定这个问题开发速度翻一倍! 不过跨域也有不足之处:只支持IE8以上。

(完)

参考文章:

angularjs跨域post解决方案的更多相关文章

  1. C#进阶系列——WebApi 跨域问题解决方案:CORS

    前言:上篇总结了下WebApi的接口测试工具的使用,这篇接着来看看WebAPI的另一个常见问题:跨域问题.本篇主要从实例的角度分享下CORS解决跨域问题一些细节. WebApi系列文章 C#进阶系列— ...

  2. thinkphp,javascript跨域请求解决方案

    javascript跨域请求解决方案 前言 对于很多前端或者做混合开发的同学,我们难免会遇到跨域发起请求业务,比如A站点向B站点请求数据等等.由于最近要做一个站点集群的项目,所以具体业务要求很多个站点 ...

  3. jquery跨域访问解决方案(转)

    客户端“跨域访问”一直是一个头疼的问题,好在有jQuery帮忙,从jQuery-1.2以后跨域问题便迎刃而解.由于自己在项目中遇到跨域问题,借此机会对跨域问题来刨根问底,查阅了相关资料和自己的实践,算 ...

  4. C#进阶系列——WebApi 跨域问题解决方案:CORS(转载)

    C#进阶系列——WebApi 跨域问题解决方案:CORS   阅读目录 一.跨域问题的由来 二.跨域问题解决原理 三.跨域问题解决细节 1.场景描述 2.场景测试 四.总结 正文 前言:上篇总结了下W ...

  5. 跨域学习笔记2--WebApi 跨域问题解决方案:CORS

    自己并不懂,在此先记录下来,留待以后学习... 正文 前言:上篇总结了下WebApi的接口测试工具的使用,这篇接着来看看WebAPI的另一个常见问题:跨域问题.本篇主要从实例的角度分享下CORS解决跨 ...

  6. cors跨域和jsonp劫持漏洞 和 同源策略和跨域请求解决方案

    cors跨域和jsonp劫持漏洞: https://www.toutiao.com/a6759064986984645127/ 同源策略和跨域请求解决方案:https://www.jianshu.co ...

  7. uni-app运行到浏览器跨域H5页面的跨域问题解决方案

    官方文档对跨域的解决方案推荐: https://ask.dcloud.net.cn/article/35267 更方便的解决方案 项目根目录直接创建一个vue.config.js文件,并在里面配置代理 ...

  8. 干掉Session?这个跨域认证解决方案真的优雅!

    用户登录认证是 Web 应用中非常常见的一个业务,一般的流程是这样的: 客户端向服务器端发送用户名和密码 服务器端验证通过后,在当前会话(session)中保存相关数据,比如说登录时间.登录 IP 等 ...

  9. vue.js及H5常见跨域问题解决方案

    一.原生H5跨域问题解决方案 1.live-server 代理解决 首先在有node.js环境下,打开命令行工具,输入 npm install live-server -g 全局安装全局安装 live ...

随机推荐

  1. delphi -----TListView的用法

    层次关系: TListView:           ->Columns:           ->Items  : -->TListItems:                   ...

  2. gerrit添加appkey以及简单添加分支

    最近团队开放用上gerrit版本项目管理工具,简单说一下appkey配置过程 首先是拿到gerrit分配的账户密码.然后进入到首页,假如是新搭建的应该是没有信息,我这里有一些提交的信息,然后找到右上角 ...

  3. Cisco路由器DHCP配置浅析

    enable  config terminal (进入配置模式)  ip dhcp pool global(配置一个根地址池,global是地址池的名称,你可以采用有意义的字符串来表示) config ...

  4. Linux基础服务

    作业一:nginx服务1.二进制安装nginx包 [root@bogon ~]# systemctl disable firewalld #关闭Firewalld自启动 Removed symlink ...

  5. 错误0x80070522:客户端没有所需的特权

    win10或win7 C盘复制文件等遇到"错误0x80070522:客户端没有所需的特权" 在运行中输入 icacls c:\ /setintegritylevel M

  6. redis 字符串和集合操作

    字符串 redis中的String在在内存中按照一个name对应一个value来存储 set() #在Redis中设置值,默认不存在则创建,存在则修改 r.set('name', 'zhangsan' ...

  7. mysql聚合函数操作

    1.mysql对中文进行排序 注:是用convert函数用gb2312编码转换 SELECT * FROM 表名 ORDER BY CONVERT(字段名 USING gb2312 ) ASC;

  8. 003-Java非堆CodeCache详解

    一.概述 Java的内存由堆和非堆两个部分组成.对于堆来说,它的组成是比较确定的,它包含了年轻代和年老代两个部分,而年轻代又是由Eden区和两个Survivor区组成.可是,非堆由哪些部分组成呢? 在 ...

  9. MySQL读写分离之amoeba

    MySQL读写分离之amoeba主从复制的搭建环境参考:http://www.cnblogs.com/fansik/p/5270334.htmlamoeba依赖于jdk环境:jdk环境搭建参考:htt ...

  10. SignalTap II进阶学习

    1. 多级触发 有时候我们可能需要利用多个信号(最多10个)依次触发后,观察特定的值.这这时候我们可以增加触发条件来满足我们的需求. 多个触发信号如上图所示,只有依次发生SW[0]上升沿. SW[1] ...