随着 web 应用的日益普及,前端开发中的 ajax 调用后端接口也越来越常见。在这个过程中,很容易遇到跨域问题,即 ajax 请求的目标服务端与当前页的域名不一致。本文将探讨 RESTful API 实现中的跨域资源共享技巧,介绍常见的跨域问题及解决方案。
什么是跨域请求
跨域请求,又称 CORS (Cross-Origin Resource Sharing),是指在浏览器之间进行 HTTP 请求时,由于当前页面的域名和请求的目标地址不同,导致浏览器在安全策略上做出的限制,请求被禁止。通俗地说,就是你用 ajax 请求一个不是你当前域名下的接口,请求会被禁止。
跨域资源共享的实现
CORS 是 W3C 标准,所有浏览器都支持。CORS 操作是浏览器自动完成的,不需要用户参与。相比 JSONP,CORS 的优势在于:
- JSONP 只支持 GET 请求,而 CORS 支持所有类型的 HTTP 请求。
- 使用 CORS,开发者可以使用普通的 XMLHttpRequest,将请求发送到任何想要使用的网站上。jsonp 的实现主要是在前端使用Script标签动态引入,不支持http header。
- CORS 支持所有类型的 HTTP 认证,而 JSONP 只支持 GET 请求无法访问请求的响应头等其他信息。
大致原理:
- 客户端发起跨域请求。
- 服务端在响应头中添加 Access-Control-Allow-* 字段,表示允许客户端请求服务器。
- 客户端检查响应头中的 Access-Control-Allow-* 字段,确定是否可以继续执行请求。
前端实现
在 ajax 调用接口时,可以通过 XMLHttpRequest 对象的 setRequestHeader 方法设置请求头,包括 Origin、Credentials、Content-Type 等字段。服务器收到请求后,解析请求头,根据需要添加响应头。
-- -------------------- ---- ------- ----- --- - --- ----------------- ---------------- ----------------------------- ------ ------------------------------------ -------------------- ---------------------------------------- ------------------ ------------------- - ----- -- ----------------------------- ---------------------- - -------- -- - -- --------------- --- ------------------- -- ---------- --- ---- - -------------------------- - -- ------------------------- ----- ----- ---- -- ----
后端实现
在服务端返回响应时,在响应头部分添加 Access-Control-Allow-* 字段。
-- -------------------- ---- ------- -- -- ---- -- ----- --- - --------------- ----- --- - --- ------ ------------- ----- ----- -- - ----------------------------------------------- ----- -- ----------- ------------------------------------------------ -------------- --------------- -------------- ------- ------------------- -- ------------- ------------------------------------------------ ----- ----- ---- ------- ---------- -- ------------ -- ------------------- --- ---------- - -- ------ ---------- - ---- ------- - ----- ------- --- ------------- ----- ----- -- - -- --------- --- -------- - ----- ------- ------- - -- -------- ---- --- ----- ---- - ----- --- ----------------- ------- -- - ----- ------ - --- ------------------ ----- -- -------------------- ----------------- -- -- - ----- ------ - ---------------------- ----- ---- - ------------------ -------------------------- --- --- -- ------ ----- - ----- --- - - ----- ----- ------- - - --- -- ----- --- -- -- --------------- -------- - -------- --- ---------------- -- -- - ------------------- ------- -- ------------------------ ---
常见问题及解决方案
1. 请求头携带Cookie无法发送
发送 ajax 请求时,浏览器默认不会携带 Cookie 信息,需要服务器设置响应头中的 Access-Control-Allow-Credentials 字段为 true。
前端代码:
xhr.withCredentials = true; // 允许发送包含凭据的请求(Cookie、HTTP认证信息等)
后端代码:
ctx.response.set('Access-Control-Allow-Credentials', true); // 授权允许发送 Cookie
2. 自定义请求头字段
默认情况下,浏览器只允许浏览器自定义请求头字段 X-Requested-With 和 Accept,则可以让服务端响应“Access-Control-Allow-Headers”头,该头部字段是对浏览器的指定。
前端代码:
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
后端代码:
ctx.response.set('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With');
3. 跨域请求的 PUT、DELETE 方法
浏览器默认不支持跨域请求的 PUT、DELETE 方法,需要服务器设置响应头中的 Access-Control-Allow-Methods 字段为 PUT、DELETE。
前端代码:
xhr.open('PUT', 'http://localhost:3000/data/1', true); xhr.send(JSON.stringify({ name: '李四' }));
后端代码:
ctx.response.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
4. 预检请求 OPTIONS 的处理
对于跨域请求,浏览器在发送正式请求之前会先发送 OPTIONS 请求,用于获知服务端对于请求的支持情况、请求中是否合法以及是否有访问权限。常见返回码如下:
- 200:表明该路由或方法在本次请求期间是合法的,浏览器可以继续发送真正的请求;
- 403:表示该路由或方法在本次请求期间是非法的,浏览器不再继续发送真正的请求;
- 404:表示该路由或资源不存在(但该方法依然合法),浏览器不再继续发送真正的请求;
- 405:表示该路由虽然有效,但该方法是被禁止的,浏览器不应再次发送带有相同路径和方法的请求。
- 206:表示请求内容范围未得到满足,请求头 Range 指定的范围超出实际范围。将Range Range: bytes=0-5
前端代码:
-- -------------------- ---- ------- ---------------- ----------------------------- ------ ------------------------------------ -------------------- ---------------------------------------- ------------------ ------------------- - ----- -- --------------- --- ------------------- -- ---------- --- ---- - -------------------------- - ---- -- --------------- --- ------------------- -- ---------- --- -- - ------------------ ------- ------------------------- ----- ----- ---- -- ---- - ---- -- --------------- --- ------------------- -- ---------- --- ---- - ------------------------ - ---- - ------------------------ -
后端代码:
-- -------------------- ---- ------- ------------- ----- ----- -- - -- ------------------- --- ---------- - -- ------ -------- - --- ----------------------------------------------- ----- ------------------------------------------------ -------------- --------------- -------------- ------- ------------------- ------------------------------------------------ ----- ----- ---- ------- ---------- ------------------------------------------ --------- -- ------ -- - ------------------- - ---- ------- - ----- ------- ---
总结
总的来说,CORS 协议是一种用来解决跨域问题的标准,它通过在服务端添加一些响应头,来告知浏览器跨域请求是否允许。IE 10 以下比较麻烦,不过通常前后端工程师会考虑到这点,解决方法大致分以下几点:
- 需要客户端发送请求时,设置 withCredentials 字段;
- 设置响应头 Access-Control-Allow-Origin 为 *,或是请求来自的地址;
- 如果用户自定义 header ,需要服务端请求头部中添加相应的响应头 Access-Control-Allow-Headers;
- 如果服务端允许跨域请求的 PUT、DELETE 方法,需要添加 Access-Control-Allow-Methods 响应头;
- 添加 OPTIONS 预检请求,对后端响应头进行增加,保证与真实请求相同条件。
- 处理 OPTIONS 预检请求,推荐使用 nookies 这个轮子。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/647d5340968c7c53b0823e4e