咱们在使用先后端分离的模式进行开发的时候,若是前端项目和api项目不是一个域名下每每会有跨域问题。今天来介绍一下咱们在Ocelot网关配置的跨域。javascript
跨域:css
浏览器对于javascript的同源策略的限制,例如a.cn下面的js不能调用b.cn中的js,对象或数据(由于a.cn和b.cn是不一样域),因此跨域就出现了.html
上面提到的,同域的概念又是什么呢??? 简单的解释就是相同域名,端口相同,协议相同前端
同源策略:java
请求的url地址,必须与浏览器上的url地址处于同域上,也就是域名,端口,协议相同.node
好比:我在本地上的域名是study.cn,请求另一个域名一段数据jquery
这个时候在浏览器上会报错:nginx
这个就是同源策略的保护,若是浏览器对javascript没有同源策略的保护,那么一些重要的机密网站将会很危险~git
study.cn/json/jsonp/jsonp.htmlgithub
当协议、子域名、主域名、端口号中任意一个不相同时,都算做不一样域。不一样域之间相互请求资源,就算做“跨域”。
请求地址 | 形式 | 结果 |
---|---|---|
http://study.cn/test/a.html | 同一域名,不一样文件夹 | 成功 |
http://study.cn/json/jsonp/jsonp.html | 同一域名,统一文件夹 | 成功 |
http://a.study.cn/json/jsonp/jsonp.html | 不一样域名,文件路径相同 | 失败 |
http://study.cn:8080/json/jsonp/jsonp.html | 同一域名,不一样端口 | 失败 |
https://study.cn/json/jsonp/jsonp.html | 同一域名,不一样协议 | 失败 |
解决跨域问题有几种常见的解决方案:
经过在服务器端配置 CORS 策略便可,每门语言可能有不一样的配置方式,可是从本质上来讲,最终都是在须要配置跨域的资源的Response上增长容许跨域的响应头,以实现浏览器跨域资源访问,详细能够参考MDN上的这篇CORS介绍
JSONP 方式实际上返回的是一个callbak,一般为了减轻web服务器的负载,咱们把js、css,img等静态资源分离到另外一台独立域名的服务器上,在html页面中再经过相应的标签从不一样域名下加载静态资源,而被浏览器容许,基于此原理,咱们能够经过动态建立script,再请求一个带参网址实现跨域通讯。
<script> var script = document.createElement('script'); script.type = 'text/javascript'; // 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数 script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback'; document.head.appendChild(script); // 回调执行函数 function handleCallback(res) { alert(JSON.stringify(res)); } </script>
服务端返回以下(返回时即执行全局函数):
handleCallback({"status": true, "user": "admin"})
$.ajax({ url: 'http://www.domain2.com:8080/login', type: 'get', dataType: 'jsonp', // 请求方式为jsonp jsonpCallback: "handleCallback", // 自定义回调函数名 data: {} });
后端 node.js 代码示例:
var querystring = require('querystring'); var http = require('http'); var server = http.createServer(); server.on('request', function(req, res) { var params = qs.parse(req.url.split('?')[1]); var fn = params.callback; // jsonp返回设置 res.writeHead(200, { 'Content-Type': 'text/javascript' }); res.write(fn + '(' + JSON.stringify(params) + ')'); res.end(); }); server.listen('8080'); console.log('Server is running at port 8080...');
JSONP 只支持 GET 请求
在现代化的前端开发的时候每每能够配置开发代理服务器,实际做用至关于作了一个请求转发,但实际请求的api地址是没有跨域问题的,而后由实际请求的api服务器转发请求到实际的存在跨域问题的api地址。
angular 配置开发代理能够参考 angular反向代理配置
后端能够经过一个反向代理如(nginx),统一暴露一个服务地址,而后为全部的请求设置跨域配置,配置 CORS 响应头,Ocelot是ApiGateway,也能够算是api的反向代理,但不单单如此。
示例代码:
app.UseOcelot((ocelotBuilder, pipelineConfiguration) => { // This is registered to catch any global exceptions that are not handled // It also sets the Request Id if anything is set globally ocelotBuilder.UseExceptionHandlerMiddleware(); // Allow the user to respond with absolutely anything they want. if (pipelineConfiguration.PreErrorResponderMiddleware != null) { ocelotBuilder.Use(pipelineConfiguration.PreErrorResponderMiddleware); } // This is registered first so it can catch any errors and issue an appropriate response ocelotBuilder.UseResponderMiddleware(); ocelotBuilder.UseDownstreamRouteFinderMiddleware(); ocelotBuilder.UseDownstreamRequestInitialiser(); ocelotBuilder.UseRequestIdMiddleware(); ocelotBuilder.UseMiddleware<ClaimsToHeadersMiddleware>(); ocelotBuilder.UseLoadBalancingMiddleware(); ocelotBuilder.UseDownstreamUrlCreatorMiddleware(); ocelotBuilder.UseOutputCacheMiddleware(); ocelotBuilder.UseMiddleware<HttpRequesterMiddleware>(); // cors headers ocelotBuilder.Use(async (context, next) => { if (!context.DownstreamResponse.Headers.Exists(h => h.Key == HeaderNames.AccessControlAllowOrigin)) { var allowedOrigins = Configuration.GetAppSetting("AllowedOrigins").SplitArray<string>(); context.DownstreamResponse.Headers.Add(new Header(HeaderNames.AccessControlAllowOrigin, allowedOrigins.Length == 0 ? new[] { "*" } : allowedOrigins)); context.DownstreamResponse.Headers.Add(new Header(HeaderNames.AccessControlAllowHeaders, new[] { "*" })); context.DownstreamResponse.Headers.Add(new Header(HeaderNames.AccessControlRequestMethod, new[] { "*" })); } await next(); }); }) .Wait();
这里扩展了一个 Ocelot pipeline 的配置,这样咱们能够直接很方便的直接在 Startup 里配置 Ocelot 的请求管道。
核心代码:
// cors headers ocelotBuilder.Use(async (context, next) => { if (!context.DownstreamResponse.Headers.Exists(h => h.Key == HeaderNames.AccessControlAllowOrigin)) { var allowedOrigins = Configuration.GetAppSetting("AllowedOrigins").SplitArray<string>(); context.DownstreamResponse.Headers.Add(new Header(HeaderNames.AccessControlAllowOrigin, allowedOrigins.Length == 0 ? new[] { "*" } : allowedOrigins)); context.DownstreamResponse.Headers.Add(new Header(HeaderNames.AccessControlAllowHeaders, new[] { "*" })); context.DownstreamResponse.Headers.Add(new Header(HeaderNames.AccessControlRequestMethod, new[] { "*" })); } await next(); });
在 HttpRequester 中间件后面添加这个中间件在响应中增长跨域请求头配置,这里先判断了一下下面的api有没有配置,若是已经配置则再也不配置,使用下游api的跨域配置,这样一来,只须要在网关配置指定的容许跨域访问的源即便下游api没有设置跨域也是能够访问了
须要说明一下的是若是想要这样配置须要 Ocelot 13.2.0 以上的包,由于以前 HttpRequester 这个中间件没有调用下一个中间件,详见 https://github.com/ThreeMammals/Ocelot/pull/830