前后端分离的跨域问题情况下,跨域问题有没有好的解决方案

问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
目前我在我的博客的时候,考虑到使用前后端分离,将前端和后端逻辑单独放在两个版本库,部署在两台服务器上。
我的主域名为: <(目前使用ghost,新的博客正在写...)
前端域名为:
后端域名为:
但是在通信的时候发现,提示跨域了,好吧,我之前以为在主域名相同情况下不会跨域(同域名不同端口也会跨域)。
目前我知道的可以解决的方法有两种:
使用JSONP,说实在我不是特别喜欢使用JSONP,感觉会导致安全问题,或者效率下降(这两点仅是我的猜测)。猜测的理由:
任何来源都能访问, 会不会存在js注入?
后端和前端需要都JSONP进行处理。(写起来不爽,而且是所有请求都要使用JSONP)。
在后端添加跨域头
如果前端域名比较多,需要添加很多域名,需要维护。如果别的系统需要请求你的接口,添加进跨域头?不好处理...
大家有没有更好一点的解决方法?
================【 9-22 17:25 】======================
补充下,设置跨域头,可以设置ip,仅供自己内部调用还是可以的。如果需要外部调用的话,就满足不了,另外不确定有没有老版本浏览器兼容问题。
=============== 【 9-29 17:39 】======================
补充下,后面碰到的问题吧,之前直接使用跨域头是可以正常GET和常规POST(表单方式)的,但是PUT和DELETE,和Content-Type为application/json等, 需要再进行其他设置(预处理)。
具体MDN的CORS的文档看
代码, PHP为例:
header("Access-Control-Allow-Origin: "); //允许的来源
//OPTIONS通过后,保存的时间,如果不超过这个时间,是不会再次发起OPTIONS请求的。
header("Access-Control-Max-Age: 86400");
//另外如果是OPTIONS头的话,需要返回200(通过验证)
header("Access-Control-Allow-Headers: Content-Type");
//允许的请求方式
header("Access-Control-Allow-Methods: OPTIONS, GET, PUT, POST, DELETE");
不想折腾的,直接反向代理到 /api 下吧, 不会产生跨域问题,但是url不太美观就是。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
当然是后端添加 Access-Control-Allow-Origin至于你说的前端域名过多的问题,让后端用点小技巧能处理好的,不麻烦思路:为 Access-Control-Allow-Origin 添加目标域名 (Origin 请求头) 而不是写死的域名或 *
大概实现 (伪代码):
// 允许跨域访问的域名数组
string[] allowOrigins =
// 判断 origin 是否是自己的前端域名,如果是则添加
if(allowOrigins.Count(x =& x.Contains(Request.Headers["Origin"])) & 0)
Response.Header.Add("Access-Control-Allow-Origin", Request.Headers["Origin"]);
不过话又说回来 请求头都是可以模拟的 所以建议将接口请求参数加密 前端脚本压缩混淆参考网易云音乐的接口加密吧(去抓下包看看)
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
nginx反向代理... 原来你自问自答了...
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
考虑下在后端添加跨域头,允许所有域名,然后在代码里过滤域名,不符合要求的域名直接返回 404。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
在服务器端设置头部 'Access-Control-Allow-Origin:*';
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
楼上说的这个。可以指定访问的 地址吧~
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
在前端代码不输的服务器进行域名转发
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
跨域头不是可以写成正则的形式吗
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
我采用的第一种方式,jsonp,callback,要是这种请求多了,写起来也麻烦;第二种方式,试了一下不好用啊,是在response上设置Access-Control-Allow-Origin..*?
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
楼主我想问下 我最近也在尝试完全分离 PHP提供接口 但是如果以后App要调用接口怎么办
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
自定义过滤器,在响应头中加跨域处理的项。
使用Nginx反向代理,同样也是为了在响应头中加跨域处理的项。
如果后端使用的是SpringMVC(当然是Java的),默认提供处理跨域的配置。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
楼主基本已经把常用的两个思路方法写出来了,平时使用的比较多的也是这两个方法,或者是结合着来使用。至于提及到的jsonp的安全性问题目前来说感觉还是不会有太大问题的,之前也一直在使用这些方法解决产品中的问题。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
如果是用node 做中间服务渲染层,那么可以考虑http-proxy-middleware这个npm包,简单粗暴,能直接实现代理的问题,就像本地访问服务端一样的掉用服务端就好了!!
同步到新浪微博
分享到微博?
Hi,欢迎来到 SegmentFault 技术社区!⊙▽⊙ 在这里,你可以提出编程相关的疑惑,关注感兴趣的问题,对认可的回答投赞同票;大家会帮你解决编程的问题,和你探讨技术更新,为你的回答投上赞同票。
明天提醒我
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要该,理由是:浅谈网站前后端分离开发模式_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
浅谈网站前后端分离开发模式
&&网站 运营 开发 快照 策划 搜索 排名
你可能喜欢前后端分离的情况下, 跨域问题有没有好的解决方案? - 知乎83被浏览7809分享邀请回答.hk/search?q=%E5%89%8D%E7%AB%AF%E8%B7%A8%E5%9F%9F&oq=%E5%89%8D%E7%AB%AF%E8%B7%A8%E5%9F%9F&aqs=chrome..69i57j69i61l2j69i60j69i61j69i65.&sourceid=chrome&ie=UTF-83添加评论分享收藏感谢收起2添加评论分享收藏感谢收起查看更多回答1 个回答被折叠()【开源】分享一个前后端分离方案
我的图书馆
【开源】分享一个前后端分离方案
&&& 半年前左右折腾了一个前后端分离的架子,这几天才想起来翻出来分享给大家。关于前后端分离这个话题大家也谈了很久了,希望我这个实践能对大家有点点帮助,演示和源码都贴在后面。
二、技术架构
&&& 这两年angularjs和reactjs算是比较火的项目了,而我选择angularjs并不是因为它火,而是因它的模块化、双向数据绑定、注入、指令等都是非常适合架构较复杂的前端应用,而且文档是相当的全,碰到问题基本上可以在网上都找到答案。所以前端基本思路就以angularjs为主、代码模块化,通过requirejs实现动态加载,ui选择dhtmlx为主配合少量bootstrap3使用。前端项目dhtmlx_web:& &&& 开发工具 Sublime Text& &&& 前端框架angularjs &&& 模块加载requirejs &&& 前端UI dhtmlx + bt3& &&& 包管理 bower &&& 构建工具 gruntjs &&& 服务架设 http_server.js &&& 浏览器支持IE8+ 实际是为了支持IE8我做了很多的努力,因为angluarjs 1.3已经不再支持IE8了,而我使用的angularjs是1.3.9 &&& 引入的一些其它类库或插件就不列出来了,太多了
服务端主要是提供restful数据服务,所以.net下毫无疑问选择asp.net webapi来实现了。 后端项目dhtmlx_webapi:& &&& 开发工具 VS2012 &&& 数据服务 Asp.net WebApi &&& 跨域实现 CORS &&& 依赖注入 Autofac &&& 日志组件 Log4net &&& 数据库已改为MS Access (为了方便大家可以直接运行)
三、前端介绍
&&& 1、基本说明&& &&& & &&& 项目主要分了三个文件夹assets存放引用类库及插件,app中则是项目文件,build中存放构建后的文件,先让大家看几个实现的页面,再介绍代码吧&
这是查询页面,查询条件、分页、排序都可用
这是虚拟分页的实现,也实现了过滤行,我自己也是挺喜欢这种风格的。
&&& 2、程序入口 以上的几个页面都比较典型,如果大家右键查看源码的话只能看到:
&!DOCTYPE html&
&title&权限管理系统&/title&
&meta http-equiv="X-UA-Compatible" content="IE=edge" /&
&meta http-equiv="Content-Type" content="text/ charset=UTF-8" /&
&link href="build/app.png" rel="shortcut icon" type="image/x-icon" /&
&link href="build/app.css" rel="stylesheet" /&
&!--[if lte IE 8]&&script src="build/ie.js"&&/script&&![endif]--&
&body ng-controller="myController"&
&div id="my_header"
&div id="my_layout"
&div id="my_footer"
&div id="my_setting" my-setting &&/div&
&div id="my_chat"
&script src="build/app.js"&&/script&
&&& 那我们就从这里开始讲起,实际上我设计的这个前端也可以看做是单页面应用SPA,只有一个页面也就是index.html点左边菜单栏打开的新tab实际只是加载了一个ng的controller渲染出来的一个层而已,当然为了实用也支持输入一个页面地址。 &&& 当然这个页面是build之后的结果,build之前的index.src.html不忍直视,主要是因为不想引入所有的dhtmlx类库,只是选择性引入很多文件没有用一个dhtml.js替代
&!DOCTYPE html&
&title&权限管理系统&/title&
&meta http-equiv="Content-Type" content="text/ charset=UTF-8" /&
&meta http-equiv="X-UA-Compatible" content="IE=edge" /&
&link href="app/img/logo/chitu-32.png" rel="shortcut icon" type="image/x-icon" /&
&link href="assets/css/default/icons.css" rel="stylesheet" /&
&link href="assets/css/default/scaffolding.css" rel="stylesheet" /&
&link href="assets/css/default/helpers.css" rel="stylesheet" /&
&link href="assets/vendors/buttons/css/buttons.min.css" rel="stylesheet" /&
&link href="assets/lib/dhtmlx/v412_std/skins/default/dhtmlx.css" rel="stylesheet" /&
&link href="app/css/fix.css" rel="stylesheet" /&
&link href="app/css/app.css" rel="stylesheet" /&
&!--[if lte IE 8]&
&script src="assets/lib/ie/html5shiv/html5shiv.min.js"&&/script&
&script src="assets/lib/ie/es5-shim/es5-shim.min.js"&&/script&
&script src="assets/lib/ie/json/json3.min.js"&&/script&
&script src="assets/lib/ie/respond/respond.min.js"&&/script&
&script src="assets/lib/ie/ieupdate/ieupdate.js"&&/script&
&![endif]--&
&body ng-controller="myController"&
&div id="my_header"
&div id="my_layout"
&div id="my_footer"
&div id="my_setting" my-setting &&/div&
&div id="my_chat"
&script src="assets/lib/jquery/jquery-1.11.2.min.js"&&/script&
&script src="assets/lib/jquery/jquery-ui.js"&&/script&
&script src="assets/lib/jquery/jquery.cookie.js"&&/script&
&script src="assets/vendors/jquery-pulsate/jquery.pulsate.custom.js"&&/script&
&script src="assets/vendors/jquery-slimscroll/jquery.slimscroll.min.js"&&/script&
&script src="assets/vendors/bootstrap/js/bootstrap.min.js"&&/script&
&script src="assets/vendors/buttons/js/buttons.js"&&/script&
&script src="assets/lib/angularjs/1.3.9/ie8/angular.js"&&/script&
&!--&script src="assets/lib/dhtmlx/v403_pro/codebase/dhtmlx.js"&&/script&
&script src="assets/lib/dhtmlx/v412_std/sources/dhtmlxTabbar/codebase/dhtmlxtabbar.js"&&/script&
&script src="assets/lib/dhtmlx/dhtmlx.custom.js"&&/script&--&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxCommon/codebase/dhtmlxcommon.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxCommon/codebase/dhtmlxcore.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxCommon/codebase/dhtmlxcontainer.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxLayout/codebase/dhtmlxlayout.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxAccordion/codebase/dhtmlxaccordion.js"&&/script&
&script src="assets/lib/dhtmlx/v412_std/sources/dhtmlxTabbar/codebase/dhtmlxtabbar.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxTree/codebase/dhtmlxtree.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxTree/codebase/ext/dhtmlxtree_json.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxToolbar/codebase/dhtmlxtoolbar.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxMenu/codebase/dhtmlxmenu.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/dhtmlxgrid.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_drag.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_export.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_filter.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_nxml.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_selection.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_srnd.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_validation.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/excells/dhtmlxgrid_excell_tree.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/excells/dhtmlxgrid_excell_link.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/excells/dhtmlxgrid_excell_grid.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/excells/dhtmlxgrid_excell_dhxcalendar.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/excells/dhtmlxgrid_excell_cntr.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/excells/dhtmlxgrid_excell_acheck.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/excells/dhtmlxgrid_excell_context.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_start.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_data.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_fast.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_filter_ext.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_form.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_group.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_hextra.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_hmenu.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_json.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_markers.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_math.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_mcol.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_over.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_pgn.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_post.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_rowspan.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_splt.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_ssc.js"&&/script&
&script src="assets/lib/dhtmlx/v403_pro/sources/dhtmlxGrid/codebase/ext/dhtmlxgrid_undo.js"&&/script&
&script src="assets/lib/dhtmlx/dhtmlx.custom.js"&&/script&
&script src="assets/lib/requirejs/require.min.js"&&/script&
&script src="app/main.js"&&/script&
不过从过里可以看出,我们的入口文件是main.js
!function () {
//config requirejs
require.config({
baseUrl: './app/js/',
assets: '../../assets/',
css: '../../assets/lib/requirejs/css',
text: '../../assets/lib/requirejs/text',
views: '../views',
config: 'config/global',
'angular-resource': '../../assets/lib/angularjs/1.3.9/angular-resource'
urlArgs: 'v=&r='+Math.random()
//init main
require(['app',
'angular-resource',
'service/index',
'directive/index',
'controller/index'],
function (app, config) {
app.init();
在main.js中,我们配置requriejs并且启动主程序,启动时载入了 app、config、angular-resource、service/index、directive/index、controller/index这六个模块,那我们就看app模块,其它不再分析了,大家自己去看代码吧
这里加载的app位于app/js/下的app.js文件
define(function (require) {
'use strict';
var app = angular.module('chituApp', ['ngResource']);
app.init = function () {
angular.bootstrap(document, ['chituApp']);
app.config(function ($controllerProvider, $provide, $compileProvider, $resourceProvider) {
// Save the older references.
app._controller = app.
app._service = app.
app._factory = app.
app._value = app.
app._directive = app.
// Provider-based controller.
app.controller = function (name, constructor) {
$controllerProvider.register(name, constructor);
return (this);
// Provider-based service.
app.service = function (name, constructor) {
$provide.service(name, constructor);
return (this);
// Provider-based factory.
app.factory = function (name, factory) {
$provide.factory(name, factory);
return (this);
// Provider-based value.
app.value = function (name, value) {
$provide.value(name, value);
return (this);
// Provider-based directive.
app.directive = function (name, factory) {
$compileProvider.directive(name, factory);
return (this);
// $resource settings
$resourceProvider.defaults.stripTrailingSlashes = false;
app.run(function (){
//run some code here ...
&&& &&& 3、与后台restful api交互
数据服务我准备都放在service文件夹下,比如菜单的数据服务在service/index,目前是静态数据。不过项目所有的ajax访问都是由ngResource实现的,实际对$http的封装,$resource可以方便的与resultful接口接合,我们可以大大简化操作,我是比较推荐它,一个简单示例:
app.factory('api', ['$resource', function ($resource) {
return $resource(url,{query:{..},update:{..},remove:{..},get:{..},insert:{..}});
app.controller('test',['api', function (api) {
api.query(params, function(data){
var list =
//获取并更新
api.get({id:1}, function(data){
data.name = 'new name';
data.update();
api.insert(data);
api.remove({id:1});
&&& 4、页面组件化
页面组件化思路基本就是依赖ng的指令,主页面上的各部分基本都是通过指令directive/index去渲染的,包括myHeader、myFooter、myLayout、mySetting、myChat五个指令分别实现各部分。一些通用的控件比如dhtmlxgrid、dhtmlxtoolbar我都写成了指令的方式,觉得以后常用的控件都可以用这种方式实现,方便而且还可以提高代码重用性。
define(['app'], function (app) {
app.directive('dhtmlxgrid', function ($resource) {
restrict: 'A',
replace: true,
fields: '@',
header1: '@',
header2: '@',
colwidth: '@',
colalign: '@',
coltype: '@',
colsorting: '@',
pagingsetting: '@',
autoheight: '=',
params:'@'
link: function (scope, element, attrs) {
scope.uid = app.genStr(12);
element.attr("id", "dhx_grid_" + scope.uid);
element.css({ "width": "100%", "border-width": "1px 0 0 0"});
scope.grid = new dhtmlXGridObject(element.attr("id"));
scope.header1
&& scope.grid.setHeader(scope.header1);
scope.header2
&& scope.grid.attachHeader(scope.header2);
scope.fields
&& scope.grid.setFields(scope.fields);
scope.colwidth
&& scope.grid.setInitWidths(scope.colwidth)
scope.colalign
&& scope.grid.setColAlign(scope.colalign)
scope.coltype
&& scope.grid.setColTypes(scope.coltype);
scope.colsorting && scope.grid.setColSorting(scope.colsorting);
scope.grid.entBox.onselectstart = function () { return true; };
if (scope.pagingsetting) {
var pagingArr = scope.pagingsetting.split(",");
var pageSize = parseInt(pagingArr[0]);
var pagesInGrp = parseInt(pagingArr[1]);
var pagingArea = document.createElement("div");
pagingArea.id = "pagingArea_" + scope.
pagingArea.style.borderWidth = "1px 0 0 0";
var recinfoArea = document.createElement("div");
recinfoArea.id = "recinfoArea_" + scope.
element.after(pagingArea);
element.after(recinfoArea);
scope.grid.enablePaging(true, pageSize, pagesInGrp, pagingArea.id, true, recinfoArea.id);
scope.grid.setPagingSkin("toolbar", "dhx_skyblue");
scope.grid.i18n.paging = {
results: "结果",
records: "显示",
page: "页",
perpage: "行每页",
first: "首页",
previous: "上一页",
found: "找到数据",
next: "下一页",
last: "末页",
of: " 的 ",
notfound: "查询无数据"
scope.grid.setImagePath(app.getProjectRoot("assets/lib/dhtmlx/v403_pro/skins/skyblue/imgs/"));
scope.grid.init();
if (scope.autoheight) {
var resizeGrid = function () {
element.height(element.parent().parent().height() - scope.autoheight);
scope.grid.setSizes();
$(window).resize(resizeGrid);
resizeGrid();
//scope.grid.enableSmartRendering(true);
if (scope.url) {
var url = app.getApiUrl(scope.url);
var param = scope.$parent[scope.params] || {};
var api = $resource(url, {}, { query: { method: 'GET', isArray: false } });
scope.grid.setQuery(api.query, param);
//保存grid到父作用域中
attrs.dhtmlxgrid && (scope.$parent[attrs.dhtmlxgrid] = scope.grid);
app.directive('dhtmlxtoolbar', function () {
restrict: 'A',
replace: false,
iconspath: '@',
link: function (scope, element, attrs) {
scope.uid = app.genStr(12);
element.attr("id", "dhx_toolbar_" + scope.uid);
element.css({ "border-width": "0 0 1px 0" });
scope.toolbar = new dhtmlXToolbarObject(element.attr("id"));
scope.toolbar.setIconsPath(app.getProjectRoot(scope.iconspath));
var items = eval("(" + scope.items + ")");
//scope.toolbar.loadStruct(items);
var index = 1;
var eventmap = {};
for (var i in items) {
var item = items[i];
if (item.action)
eventmap[item.id] = item.
if (item.type == 'button') {
scope.toolbar.addButton(item.id, index++, item.text, item.img, item.imgdis);
item.enabled == false && scope.toolbar.disableItem(item.id);
else if (item.type == 'separator') {
scope.toolbar.addSeparator(index++);
scope.toolbar.attachEvent("onClick", function (id) {
var name = eventmap[id];
if (name && scope.$parent[name] && angular.isFunction(scope.$parent[name]))
scope.$parent[name].call(this);
attrs.dhtmlxtoolbar && (scope.$parent[attrs.dhtmlxtoolbar] = scope.toolbar);
&&& 5、前端文件分类(文件夹介绍)
再来说说js文件的分类,实际上是根据angularjs的特点,把所有的js分为以下几个文件夹,都是angular的概念我可能没法一下子跟大家解释清楚,请大家自己去了解。
│& ├─config&&&&&&& 配置
├─constant&&& 常量& ├─controller&& 控制器
├─decorator&& 修饰器
├─directive&&& 指令
├─factory&&&&& 工厂
├─filter&&&&&&&&& 过滤器
├─provider&&&& 提供商
├─route&&&&&&&& 路由
├─service&&&&& 服务
└─value&&&&&&& 值
其中config、constant、value都是固定值,其中config属于自定义的
controller控制器、directive指令、route路由、filter过滤器大家都很熟悉我就不说了,
factory工厂、provider提供商、service服务这三者很像的,反正你都可以把它们看作是provider,至于区别看看下面源码自己体会(angularjs1.4.0源码第4027行)
至于修饰器decorator就是对现有的provider进行加工修饰的
function provider(name, provider_) {
assertNotHasOwnProperty(name, 'service');
if (isFunction(provider_) || isArray(provider_)) {
provider_ = providerInjector.instantiate(provider_);
if (!provider_.$get) {
throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
return providerCache[name + providerSuffix] = provider_;
function enforceReturnValue(name, factory) {
return function enforcedReturnValue() {
var result = instanceInjector.invoke(factory, this);
if (isUndefined(result)) {
throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
function factory(name, factoryFn, enforce) {
return provider(name, {
$get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
function service(name, constructor) {
return factory(name, ['$injector', function($injector) {
return $injector.instantiate(constructor);
&&& 6、前端权限控制
&&& 为什么我没有选择使用route + ngView来构建呢,网上有关angularjs的SPA基本上都是这个方案的,像我这样打开多个TAB页签层的几乎没有。主要是因为我自己一直在做企业应用,同时打开多个页面的tab页签方式会比只能打开一个页面的ng-view方便很多,所以选择自己去实现tab页的方式。但是这么一来权限控制就不能放在路由当中了,需要自己在处理tab页签时控制。
&&& 前端权限控制的思路是这样的:打开主页面时即加载身份认证数据、菜单数据及相关的其它权限数据,在打开tab页签处理时进行权限验证。
&&& 数据访问的权限控制可以这样做,登陆时计算得到一个Ticket,每次访问数据时在request header中添加这个Ticket,然后在服务端每次请求时对Ticket进行验证,前端可以在app.js中配置到$httpProvider.defaults.headers.post当中,服务端具体怎么处理介绍到服务端再说。
&&& 7、关于UI类库的说明
&&& 接下来说下UI的东西,适合自己的UI都会有需要改的如easyui我也改了很多,同样dhtmlx这款UI也有很多不合适或有问题的地方,比如不支持字体图标,比如grid更好的数据加载机制等等,我对它的修改集中放在dhtml.custom.js中,把它改到我能用或更好用,花费了我大量的时间,因为需要读懂它的源码后才能修改,具体修改大家可以查看这个文件。
&&& 还有大家可能会好奇我为什么在项目中还有引入
&script src="assets/lib/jquery/jquery-ui.js"&&/script&
&script src="assets/lib/jquery/jquery.cookie.js"&&/script&
&script src="assets/vendors/jquery-pulsate/jquery.pulsate.custom.js"&&/script&
&script src="assets/vendors/jquery-slimscroll/jquery.slimscroll.min.js"&&/script&
&script src="assets/vendors/bootstrap/js/bootstrap.min.js"&&/script&
&script src="assets/vendors/buttons/js/buttons.js"&&/script&
其实就是只使用dhtmlx开发出来的页面是很单调的,这些类库我在组件中都有用到,比如myheader中的这些,还有表单控件dhtmlx的form太丑了,所以引入bt3
&&& 8、关于nodejs的工具使用
这不是我们的重点,你只要知道npm install命令下载node_modules,然后就是运行grunt及httpjs,关于它们的详细介绍及如何配置gruntfile大家自己去了解,最后代码修改完成之后,记得运行grunt重新构建。
四、服务端介绍
&&& 服务端大家都很熟悉,没什么说的。
&&& 关于服务端为什么没分层,这里仅仅是一个数据服务而已,没必要太复杂所以我就没分层了。如果业务比较复杂是可以把控制层Controller、服务层Sevice分开的,同时也衍生出接口层 Interface及数据实体层Entity,关于服务层是不是需要再分或是不同业务要不要分开就要看具体情况了,看过一段时间的DDD,感觉是从技术上把简单的问题复杂化了,觉得实际做项目是没必要,可能是我的理解不够深,我的理念还是能满足需求的情况下能简则简。
在Global.asax.cs中添加FrameworkConfig.Register(),在FrameworkConfig.cs中
Configuration.Instance()
.RegisterComponents()
//注册公共组件
.RegisterDependencyResolver()
//注册依赖注入
.RegisterProjectModules()
//注册项目模块
.RegisterHttpCorsSupport();
//开启CORS支持
然后看一个租户数据示例,这个地址/api/tenant,请求进来之后控制器接收处理TenantController.cs
public class TenantController : ApiController
private ITenantService _tenantS
private RequestParameter _requestP
/// &summary&
/// 注入参数及服务
/// &/summary&
/// &param name="tenantService"&&/param&
/// &param name="requestParameter"&&/param&
public TenantController(ITenantService tenantService, RequestParameter requestParameter)
_tenantService = tenantS
_requestParameter = requestP
/// &summary&
/// 查询返回多条租户数据结果集
/// &/summary&
/// &returns&&/returns&
public Paging&bas_tenant& Get()
var query = _requestParameter.ReadAsQueryEntity();
var result = _tenantService.GetTenantListWithPaging(query);
/// &summary&
/// 创建新的租户信息
/// &/summary&
public string Post([FromBody]bas_tenant tenant)
return _tenantService.Insert(tenant);
/// &summary&
/// 查询返回单个租户信息
/// &/summary&
/// &param name="id"&&/param&
/// &returns&&/returns&
public bas_tenant Get(string id)
return _tenantService.GetTenant(id);
/// &summary&
/// 更新租户信息
/// &/summary&
/// &param name="id"&&/param&
public void Put(string id,[FromBody]bas_tenant tenant)
_tenantService.Update(id, tenant);
/// &summary&
/// 删除租户信息
/// &/summary&
/// &param name="id"&&/param&
public void Delete(string id)
_tenantService.Delete(id);
服务中处理TenantService.cs
public class TenantService : ITenantService
private IUser _
private IDbContext _
//注入用户信息及数据访问上下文
public TenantService(IUser user,IDbContext db)
//不分页查询
public List&bas_tenant& GetTenantList()
var result = _db.Select("*")
.From("bas_tenant")
.QueryMany&bas_tenant&();
//分页查询
public Paging&bas_tenant& GetTenantListWithPaging(QueryEntity qe)
var result = _db.Select("*")
.From("bas_tenant")
.Where&_StartWith&("tenant_id,tel", qe).IgnoreEmpty()
//商户编码、手机号
使用startwith查询 忽略空值
.Where&_Like&("tenant_name,charge_person,addr", qe).IgnoreEmpty()
//商户名、责任人、地址 使用like查询
.Where&_Eq&("*", qe).IgnoreEmpty()
//剩下的其它的字段都
使用equal查询
.Paging(qe, "tenant_id")
//分页参数在qe中 默认按商户编码排序 自动处理页面上的排序、分页请求
.QueryManyWithPaging&bas_tenant&();
//获取单条记录
public bas_tenant GetTenant(string id)
var result = _db.Select("*")
.From("bas_tenant")
.Where("tenant_id",id)
.QuerySingle&bas_tenant&();
public int Update(string id, bas_tenant tenant)
var result = _db.Update("bas_tenant", tenant)
.AutoMap(x =& x.tenant_id)
.Where("tenant_id", id)
.Execute();
public string Insert(bas_tenant tenant)
tenant.tenant_id = "T" + (new Random().Next(100) + 500).ToString();
var result = _db.Insert("bas_tenant", tenant)
.AutoMap()
.Execute();
return tenant.tenant_
public int Delete(string id)
var result = _db.Delete("bas_tenant")
.Where("tenant_id", id)
.Execute();
3、权限认证 在前端权限控制中我们说了,$http请求时request header中添加了身份认证的Ticket,我们要在每一次请求返回数据前都要验证这个Ticket,当然不能写在每个方法当中了,可以在过滤器中实现:
public class TicketAuthorizeAttribute : AuthorizationFilterAttribute
public override void OnAuthorization(HttpActionContext actionContext)
bool isAuthorizated = false;
IPrincipal principal = Thread.CurrentP
var RequestHeader = actionContext.Request.H
if (RequestHeader.Contains("Ticket"))
//在这里验证Ticket
string requestTicket = RequestHeader.GetValues("Ticket").First();
string serverTicket = EncryptHelper.MD5(...);
if (requestTicket == serverTicket)
isAuthorizated = true;
if (!isAuthorizated)
actionContext.Response = actionContext.ControllerContext.Request
.CreateErrorResponse(HttpStatusCode.Unauthorized, "已拒绝为此请求授权。");
五、源码下载
&&& 演示地址: (前端页面架设在8081,数据服务架设在8082)不稳定有时不能访问
&&& 前端项目 dhtmlx_web:&&&&
&&& 后端项目 dhtmlx_webapi:
大家下载代码后直接打开index.html是无效的因为其中有很多的ajax请求必须架设在web服务上。 如果没有nodejs的环境,也可以用VS打开运行或架设到IIS上。
如果大家有nodejs的环境,可以运行目录下的http.bat实现上是调用nodejs的http简易服务程序,注意端口我写死了是8080,自己去修改。
单独运行前端项目也能打开页面,但是没有动态数据,大家可以先运行服务端程序,运行起来后比如端口为8082,那么其数据服务地址为:
只要把这个服务端地址复制到前端项目的配置js/config/global.js中再运行前端项目就可以看到数据了。
这个架子折腾了一阵子可能还有一些不完整的地方,共享出来权当给大家一个参考,如果大家有什么意见或建议可以给我留言。
如果大家感兴趣就在右下角帮我【推荐】一下吧,谢谢大家。
TA的最新馆藏[转]&
喜欢该文的人也喜欢

我要回帖

更多关于 后端解决跨域问题 的文章

 

随机推荐