面试题5

1. django、Flask、Tornado区别?

django:大而全的全的框架;内置很多组件:ORM、admin、Form、ModelForm、中间件、信号、缓存、csrf等
flask: 微型框架、可扩展强,如果开发简单程序使用flask比较快速,如果实现某些功能就需要引入一些组件:flask-session/flask-SQLAlchemy/wtforms/flask-migrate/flask-script/blinker

这两个框架都是基于wsgi协议实现的,默认使用的wsgi模块不一样。
还有一个显著的特点,他们处理请求的方式不同:
django是通过将请求封装成Request对象,再通过参数进行传递。
而flask是通过上下文管理实现。

tornado:是一个轻量级的Web框架,可扩展性强,用于快速开发简单程序,拥有强大的异步非阻塞和内置WebSocket功能

2. Flask使用过的组件?

flask-session

flask-SQLAlchemy

wtforms

flask-migrate

flask-script

blinker

3. django所有组件?

ORM

admin

Form

ModelForm

中间件

信号

缓存

csrf


4. 你对那个组件最熟悉?

ORM
5. orm中你都记得那些方法?

- 增删改查

- values/values_list

- F/Q

- 性能相关

  select_related

  prefetch_related

- 原生SQL

  extra

  raw

  执行原生SQL


6. Django/Flask请求生命周期

a. 首先经过wsgi, 本质是创建socket服务端,用于接收用户请求并对请求进行初次封装。
b. 再经过中间件,对所有请求到来之前,响应之前定制一些操作。
c. 通过路由匹配,在url和视图函数对应关系中,根据当前请求url找到相应的函数。
d. 接着执行视图函数,进行业务处理,也就是通过ORM去数据库中获取数据,再去拿到模板,然后将数据和模板进行渲染
e. 再经过所有中间件
f. 最后通过wsgi将响应返回给用户。

6.5 浏览器上输入地址,回车然后发生了什么? => Http请求生命周期
- 域名解析
- 连接成功
- 浏览器发送数据
- 服务端接收数据并处理再响应
- 服务端做的事比较复杂,如果是:Django、Flask,返回第六题答案


7. 谈谈你对Http协议的理解?
- 协议:短连接、无状态、响应头和相应体通过\r\n\r\n分割,请求头之间通过\r\n分割。
- 请求头:User-Agent,Referer,Host,Content-Type,Cookie,Accept
- 请求方法: GET,POST ,HEAD,OPTIONS,PUT,DELETE,TRACE 
- 状态码:

1开头的状态码
100 Continue 继续。客户端应继续其请求
101 Switching Protocols 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议
2开头的状态码
200 OK 请求成功。一般用于GET与POST请求
201 Created 已创建。成功请求并创建了新的资源
202 Accepted 已接受。已经接受请求,但未处理完成
203 Non-Authoritative Information 非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本
204 No Content 无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档
205 Reset Content 重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域
206 Partial Content 部分内容。服务器成功处理了部分GET请求
3开头的状态码
300 Multiple Choices 多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择
301 Moved Permanently 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
302 Found 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
303 See Other 查看其它地址。与301类似。使用GET和POST请求查看
304 Not Modified 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
305 Use Proxy 使用代理。所请求的资源必须通过代理访问
306 Unused 已经被废弃的HTTP状态码
307 Temporary Redirect 临时重定向。与302类似。使用GET请求重定向
4开头的状态码
400 Bad Request 客户端请求的语法错误,服务器无法理解
401 Unauthorized 请求要求用户的身份认证
402 Payment Required 保留,将来使用
403 Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求
404 Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
405 Method Not Allowed 客户端请求中的方法被禁止
406 Not Acceptable 服务器无法根据客户端请求的内容特性完成请求
407 Proxy Authentication Required 请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权
408 Request Time-out 服务器等待客户端发送的请求时间过长,超时
409 Conflict 服务器完成客户端的PUT请求是可能返回此代码,服务器处理请求时发生了冲突
410 Gone 客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置
411 Length Required 服务器无法处理客户端发送的不带Content-Length的请求信息
412 Precondition Failed 客户端请求信息的先决条件错误
413 Request Entity Too Large 由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息
414 Request-URI Too Large 请求的URI过长(URI通常为网址),服务器无法处理
415 Unsupported Media Type 服务器无法处理请求附带的媒体格式
416 Requested range not satisfiable 客户端请求的范围无效
417 Expectation Failed 服务器无法满足Expect的请求头信息
5开头的状态码
500 Internal Server Error 服务器内部错误,无法完成请求
501 Not Implemented 服务器不支持请求的功能,无法完成请求
502 Bad Gateway 充当网关或代理的服务器,从远端服务器接收到了一个无效的请求
503 Service Unavailable 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
504 Gateway Time-out 充当网关或代理的服务器,未及时从远端服务器获取请求
505 HTTP Version not supported 服务器不支持请求的HTTP协议的版本,无法完成处理



8. 中间件的作用?用它做过什么?以及涉及的方法?
作用:对所有的请求进行批量处理,在视图函数执行前后进行自定义操作

应用:

- 登录验证,如果不使用就需要为每个函数添加装饰器,太繁琐。
- 权限处理,用户登录后,将权限放到session中,然后再每次请求时需要判断当前用户是否有权访问当前url,这检查的东西就可以放到中间件中进行统一处理。
- 还有一些内置:
  - csrf,在setting中设置csrf中间件,在每次post请求是避免csrf攻击,不用每次请求都去加装饰器
  - session,在setting中设置session中间件,在每次请求都保持会话状态
  - 全站缓存 ,把返回的数据保存到内存,再次访问不用进行数据库操作,在执行完所有process-request之后和执行完所有process-response之后进行
- 另外,还有一个就是处理跨域 (前后端分离时,本地测试开发时使用的。)

方法:

1)process_request(self,request) 

请求来时执行,不写时直接跳过,执行下一个中间件;当有return HttpResonse时,下面中间件不再执行

(2)process_view(self, request, callback, callback_args, callback_kwargs)   

先执行process_request,执行完后,再从起始执行proces_view

(3)process_template_response(self,request,response)  

如果Views中的函数返回的对象中,具有render方法,此方法执行

(4)process_exception(self, request, exception)             

异常触发执行,当views.py函数执行出错后,此方法执行;出错时,最低层的exception优先级最高,执行最近的一个,

然后执行respnse方法

(5)process_response(self, request, response)      

请求返回时执行,不写时直接跳过,执行下一个中间件;当有return HttpResonse时,会替换原数据



9. MTV和MVC?

MVC: model(数据模型),view(前端模板),controller(视图函数)

MTV(django): model(数据模型),tempalte(前端模板),view (视图函数)

10. 视图 FBV和CBV

FBV就是在视图里使用函数处理请求。 

def index(request):
    pass 

CBV就是在视图里使用类处理请求。 

class IndexView(View):

    # 如果是crsf相关,必须放在此处
    def dispach(self,request):
    # 通过反射执行post/get 

    @method_decoretor(装饰器函数)
    def get(self,request):
        pass

    def post(self,request):
        pass 

# 路由:IndexView.as_view()    

CBV优点主要下面两种:

提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承) 
可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性

FBV和CBV的区别
这两个没什么区别,因为他们的本质都是函数。CBV的.as_view()返回的view函数,view函数中调用类的dispatch方法,在dispatch方法中通过反射执行get/post/delete/put等方法。
CBV比较简洁,GET/POST等业务功能分别放在不同get/post函数中。而FBV自己做判断进行区分


CBV中的注意事项?

11. 遇到的难题:
- CBV是添加csrf装饰器,放到dispatch方法上
- 多数据库配置 allow_relation方法

12. 模板
- 索引: {{v.0}}
- 方法执行: {% for item in dic.items %} {%endfor%}
- 模板继承

重复性代码--->模板继承

流程:

1、写好一个html文件。

2、下面有个新的html文件需要继承上面这个html。

  直接在这个新的html文件的最顶端,写上下面的内容

#这个manger.html就是母版的文件名称,告诉这个html去哪里继承。
{% extends 'manger.html' %}

3、问题来了,我们既然是继承,总不能全部继承吧,我们肯定是需要写一些新的内容在新的html文件中展示,我们只是继承母版中的一部分,比如head之类的。

  比如我们要在html的一个位置更换新的内容。

  1、定义一个继承的块,叫index。

{% block index %}{% endblock %}

  2、继承者在自己的文件中需要更换新的内容,需要这么写。

  注意:只需要在继承的任意位置写上{% block index %}内容区{% endblock  %}

{% block index %}
    <div style="height: 45px;line-height: 45px;font-size: 15px;font-weight: bolder">
        <span>首页&nbsp;></span>
        <span>资产管理</span>
    </div>
{% endblock %}

4、问题由来了,如果我的html内容特别多,我看着特别乱,这样我可以把这些内容都写到一个新的html文件里面,然后在我的html文件中直接引用就OK。

例:我单独写了一个test.html文件。

我想在html中引用直接写入地下的东西就OK了。

{% include "test.html" %}

 5、写自己的CSS和JS样式或操作。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
    <style>
        body{
            height: 38px;
            width: 300px;
        }
    </style>
    {% block css %}{% endblock %}
<body>
    <div>
        
    </div>
    <script src="jquery-x.x.x"></script>
    {% block js %}{% endblock %}
</body>
</html>

   这样,我们就可以在自己的html文件中直接写自己的css样式或者js操作。

{% block css %}
    <style>

    </style>
{% endblock %}

{% block js %}
    <script>

    </script>
{% endblock %}

- 自定义方法:
- simple_tag

simple_tag
a. app下创建templatetags目录
b. 任意xxoo.py文件
c. 创建template对象 register
d.
__author__ = 'Administrator'
from django import template
from django.utils.safestring import mark_safe

register = template.Library()
@register.simple_tag

def func(a1,a2,a3....)
return "asdfasd"
e. settings中注册APP
f. 顶部 {% load xxoo %}
g. {% 函数名 arg1 arg2 %}
# 缺点:
# 不能作为if条件

# 优点:
# 参数任意

- inclusion_tags

这种类型的标签可以被其他模板进行渲染,然后将渲染结果输出

Library.inclusion_tag()支持take_context=True,用法类似Library.simple_tag()

from django import template
register = template.Library()
 
@register.inclusion_tag('result.html')
def test():
  a=['first','second','third']
  return {'choices':a}

result.html 内容

<ul>
{% for choice in choices %}
  <li> {{ choice }} </li>
{% endfor %}
</ul>

test.html内容

{% load mytags %}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
{% test %}
</body>
</html>

view函数:

def test(request):
  return render(request,'test.html')

- filter

filter
a. app下创建templatetags目录
b. 任意xxoo.py文件
c. 创建template对象 register
d. 
@register.filter
def func(a1,a2)
return "asdfasd"
e. settings中注册APP
f. 顶部 {% load xxoo %}
g. {{ 参数1|函数名:"参数二,参数三" }} {{ 参数1|函数名:数字 }}
# 缺点:
# 最多两个参数,不能加空格
# 优点:
# 能作为if条件

13. Form和ModelForm的作用?区别?应用场景?
- 作用:
  - 对用户请求数据格式进行校验
  - 自动生成HTML标签
- 区别:
  - Form,字段需要自己手写。

class Form(Form):
    xx = fields.CharField(.)
    xx = fields.CharField(.)
    xx = fields.CharField(.)
    xx = fields.CharField(.)

- ModelForm,可以通过Meta进行定义

class MForm(ModelForm):
    class Meta:
        fields = "__all__"
        model = UserInfo    

- 应用:只要是客户端向服务端发送表单数据时,都可以进行使用,如:用户登录注册

14. 为什么要用缓存?
将常用且不太频繁修改的数据放入缓存。
以后用户再来访问,先去缓存查看是否存在,如果有就返回
否则,去数据库中获取并返回给用户(再加入到缓存,以便下次访问)

15. django内部支持哪些缓存?
Django中提供了6种缓存方式:
开发调试(不加缓存)
内存
文件
数据库
Memcache缓存(python-memcached模块)
Memcache缓存(pylibmc模块)

安装第三方组件支持redis:
django-redis组件

16. 设置缓存
- 全站缓存(中间件)

MIDDLEWARE_CLASSES = (
'django.middleware.cache.CacheMiddleware',
)

- 视图函数缓存

from django.views.decorators.cache import cache_page

@cache_page(60 * 15)
def my_view(request, param):

# ...

- 局部模板缓存

在模板的顶端附近加入{% load cache %}以通知模板存取缓存标签。

模板标签{% cache %}在给定的时间内缓存了块的内容。 它至少需要两个参数: 缓存超时时间(以秒计)和指定缓存片段的名称。 示例:

{% load cache %}
{% cache 500 sidebar %}
  .. sidebar ..
{% endcache %}

17. django的信号作用?
django的信号其实就是django内部为开发者预留的一些自定制功能的钩子。
只要在某个信号中注册了函数,那么django内部执行的过程中就会自动触发注册在信号中的函数。
如:
pre_init # django的modal执行其构造方法前,自动触发
post_init # django的modal执行其构造方法后,自动触发
pre_save # django的modal对象保存前,自动触发
post_save # django的modal对象保存后,自动触发

用信号做过什么?
在数据库某些表中添加数据时,可以进行日志记录。

18. 序列化

# 内置:
from django.core import serializers

#queryset = [obj,obj,obj]
ret = models.BookType.objects.all()
data = serializers.serialize("json", ret)
# json:
import json

#ret = models.BookType.objects.all().values('caption')
ret = models.BookType.objects.all().values_list('caption')
ret=list(ret)
result = json.dumps(ret)

# 补充:中文显示问题
- json.dumps(ensure_ascii=True)
- json.dumps(cls=JSONEncoder)

19. admin & stark组件

- 为公司定制更适用于自己的组件: stark组件

仿照django的admin,开发自己的stark组件。实现类似数据库客户端的功能,对数据进行增删改查。

包括:数据的增删改查,search模糊查询,批量操作action,分页,多级过滤,POPUP功能

20. ContentType的作用?以及应用场景?

contenttype是django的一个组件(app),为我们找到django程序中所有app中的所有表并添加到记录中。

可以使用他再加上表中的两个字段实现:一张表和N张表创建FK关系。
- 字段:表名称
- 字段:数据行ID

应用:路飞表结构优惠券和专题课和学位课关联。

21. 谈谈你对restfull 规范的认识?
- https
- 域名
  - api.oldboy.com
  - www.oldboy.com/api
- 版本:
  - www.oldboy.com/api/v1

- URL资源,名词
  - www.oldboy.com/api/v1/student

- 请求方式:
  - GET/POST/PUT/DELETE/PATCH/OPTIONS/HEADERS/TRACE
- 返回值:
  - www.oldboy.com/api/v1/student/ -> 结果集
  - www.oldboy.com/api/v1/student/1/ -> 单个对象
- URL添加条件
  - www.oldboy.com/api/v1/student?page=11&size=9
- 状态码:
  - 200
  - 300
  - 301
  - 302
  - 400
  - 403
  - 404
  - 500
- 错误信息
  {
  code:1000,
  meg:'xxxx'
  }
- hyperlink
  {
  id:1
  name: ‘xiangl’,
  type: http://www.xxx.com/api/v1/type/1/
  }

22. 幂等性是什么意思?
幂等性:就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用。举个最简单的例子,那就是支付,用户购买商品使用约支付,支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了,用户再次点击按钮,此时会进行第二次扣款,返回结果成功,用户查询余额返发现多扣钱了,流水记录也变成了两条...

那么如何设计接口才能做到幂等呢?

方法一、单次支付请求,也就是直接支付了,不需要额外的数据库操作了,这个时候发起异步请求创建一个唯一的ticketId,就是门票,这张门票只能使用一次就作废,具体步骤如下:

  1. 异步请求获取门票

  2. 调用支付,传入门票

  3. 根据门票ID查询此次操作是否存在,如果存在则表示该操作已经执行过,直接返回结果;如果不存在,支付扣款,保存结果

  4. 返回结果到客户端

如果步骤4通信失败,用户再次发起请求,那么最终结果还是一样的

方法二、分布式环境下各个服务相互调用

这边就要举例我们的系统了,我们支付的时候先要扣款,然后更新订单,这个地方就涉及到了订单服务以及支付服务了。

用户调用支付,扣款成功后,更新对应订单状态,然后再保存流水。

而在这个地方就没必要使用门票ticketId了,因为会比较闲的麻烦

(支付状态:未支付,已支付)

步骤:

1、查询订单支付状态

2、如果已经支付,直接返回结果

3、如果未支付,则支付扣款并且保存流水

4、返回支付结果

如果步骤4通信失败,用户再次发起请求,那么最终结果还是一样的

对于做过支付的朋友,幂等,也可以称之为冲正,保证客户端与服务端的交易一致性,避免多次扣款。

最后来看一下我们的订单流程,虽然不是很复杂,但是最后在支付环境是一定要实现幂等性的


23. 远程数据交互:
- webservice, webservice是在rest api广泛引用之前大家使用的一个跨平台交互的接口。
- restfull api
- RPC

RPC(远程过程调用)是一项广泛用于支持分布式应用程序(不同组件分布在不同计算机上的应用程序)的技术。RPC 的主要目的是为组件提供一种相互通信的方式,使这些组件之间能够相互发出请求并传递这些请求的结果。 没有语言限制

扩展:WCF他是创建了一个双工通道。

24. HTTPS
Http: 80端
https: 443端口

- 自定义证书
  - 服务端:创建一对证书
  - 客户端:必须携带证书
- 购买证书
  - 服务端: 创建一对证书,。。。。
  - 客户端: 去机构获取证书,数据加密后发给咱们的服务单
  - 证书机构:公钥给改机构


Django rest framework 框架

25. 用django写接口时,有没有用什么框架?
- 使用rest framework框架
- 原生CBV

26. rest framework框架优点?

rest framework帮助开发者提供了很多组件,可以提高开发效率。

27. rest framework都有哪些组件?

- 路由,自动帮助开发者快速为一个视图创建4个url
  www.oldboyedu.com/api/v1/student/$
  www.oldboyedu.com/api/v1/student(?P<format>\w+)$

  www.oldboyedu.com/api/v1/student/(?P<pk>\d+)/$
  www.oldboyedu.com/api/v1/student/(?P<pk>\d+)(?P<format>\w+)$

- 版本处理
  - 问题:版本都可以放在那里?
    - url
    - GET
    - 请求头
- 认证
  - 问题:认证流程?

1,self.initial(request, *args, **kwargs)

2,self.perform_authentication(request)

3,request.user

4,self._authenticate()

返回三种情况:

1,(user, token)表示认证通过并设置用户名和Token;
2,None 没有通过验证
3,AuthenticationFailed异常

- 权限
  - 权限是否可以放在中间件中?以及为什么?

  可以放在中间件,用户登录后,将权限放到session中,然后再每次请求时需要判断当前用户是否有权访问当前url,这检查的东西就可以放到中间件中进行统一处理,注意配置里要放在session中间件之后。


- 访问频率的控制
  - 匿名用户可以真正的防止?无法做到真正的访问频率控制,只能把小白拒之门外。
  如果要封IP,使用防火墙来做。
  - 登录用户可以通过用户名作为唯一标示进行控制,如果有人注册很多账号,也无法防止。

- 视图
- 解析器 ,根据Content-Type请求头对请求体中的数据格式进行处理。request.data
- 分页

a. 根据页码进行分页

b. 位置和个数进行分页

c. 游标分页

- 序列化
  - source,一对一,多对一
  - 定义方法c. 游标分页,多对多,SerializerMethodField
  - 请求数据格式校验,渲染HTML标签

- 渲染器


28. 视图中都可以继承哪些类?

1.GenericAPIView # 视图
2.GenericViewSet # 视图 劣势:指向同一个视图,get无法区分是查询单个对象还是所有数据。
3.ModelViewSet # 视图 最强大:增、删、改、查、局部更新6个方法。
总结:
a. 增删改查 ModelViewSet 最强大:增、删、改、查、局部更新6个方法。
b. 增删 CreateModelMixin,DestroyModelMixin, GenericViewSet
c. 复杂逻辑 GenericViewSet 或 APIView


29. 渲染器有坑
- 指定渲染器只用JSON
- 视图:
  class UserView(...):
  queryset = []     #自己定义queryset

30. assert 是的作用?

条件成立则继续往下,否则跑出异常,一般用于:满足某个条件之后,才能执行,否则应该跑出异常。

应用场景:rest framework

class GenericAPIView(views.APIView):
"""
Base class for all other generic views.
"""
# You'll need to either set these attributes,
# or override `get_queryset()`/`get_serializer_class()`.
# If you are overriding a view method, it is important that you call
# `get_queryset()` instead of accessing the `queryset` property directly,
# as `queryset` will get evaluated only once, and those results are cached
# for all subsequent requests.
queryset = None
serializer_class = None

# If you want to use object lookups other than pk, set 'lookup_field'.
# For more complex lookup requirements override `get_object()`.
lookup_field = 'pk'
lookup_url_kwarg = None

# The filter backend classes to use for queryset filtering
filter_backends = api_settings.DEFAULT_FILTER_BACKENDS

# The style to use for queryset pagination.
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS

def get_queryset(self):
"""
Get the list of items for this view.
This must be an iterable, and may be a queryset.
Defaults to using `self.queryset`.

This method should always be used rather than accessing `self.queryset`
directly, as `self.queryset` gets evaluated only once, and those results
are cached for all subsequent requests.

You may want to override this if you need to provide different
querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)
"""
assert self.queryset is not None, (
"'%s' should either include a `queryset` attribute, "
"or override the `get_queryset()` method."
% self.__class__.__name__
)

queryset = self.queryset
if isinstance(queryset, QuerySet):
# Ensure queryset is re-evaluated on each request.
queryset = queryset.all()
return queryset
断言相关代码

31. 跨域

- JSONP

JSONP实现跨域请求的原理简单的说就是动态创建<script>标签,然后利用<script>的src 不受同源策略约束来跨域获取数据。只能发送get请求

JSONP 的理念就是,与服务端约定好一个回调函数名,服务端接收到请求后,将返回一段 Javascript,在这段 Javascript 代码中调用了约定好的回调函数,并且将数据作为参数进行传递。当网页接收到这段 Javascript 代码后,就会执行这个回调函数,这时数据已经成功传输到客户端了。

- CORS

本质是添加响应头

1.Access-Control-Allow-Origin,指定一个允许向该服务器提交请求的URL

2.Access-Control-Allow-Credentials,告知客户端,当请求的credentials属性是true的时候,响应是否可以被得到。当它作为预请求的响应的一部分时,它用来告知实际的请求是否使用了credentials。

3.Access-Control-Allow-Methods,指明资源可以被请求的方式有哪些(一个或多个)。这个响应头信息在客户端发出预检请求的时候会被返回

4.Access-Control-Allow-Headers,也是在响应预检请求的时候使用。用来指明在实际的请求中,可以使用哪些自定义HTTP请求头

CORS 对比 JSONP

都能解决 Ajax直接请求普通文件存在跨域无权限访问的问题

  1. JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求
  2. 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理
  3. JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS


























猜你喜欢

转载自www.cnblogs.com/weiwu1578/p/9058200.html