angularjs和ajax的结合使用 (四)

打印 上一主题 下一主题

主题 844|帖子 844|积分 2532

知道的朋友了解 我不是属于讲按部就班技术的那种人。什么xx入门 ,入门到精通,入门到入土。 其实非要严格说的话已经跟angularjs 什么ajax 偏的有点远了,之所以还是叫这个名称,因为都属于web应用 ,叫这个名称是一种延续,其实这个系列持续了几年了 是我自己从学习到一种适合我自己环境的特有应用方式的一种总结。主题还是一个:web应用,往细了装逼了说一种同时适合web 和winform 客户端 独到的 数据架构 处理方式。当然所有的都是基于以前的基础之上的。
主题:一种同时适合web 和winform 客户端 独特的 数据架构 处理方式

后台API权限控制

首先是后台的接口 ,使用webapi的方式 返回 json 数据 。当然这里有一个技巧 , 也就是权限控制。众所周知 http 有一种 方式 可以把授权放在header 里。后台验证 ,每个接口都要权限符合才能 请求到数据。都知道asp.net MVC有filter 可以用来先进行过滤 ,都在Java做web后台满大街 的年代 我们还在用中古时期的ASP.Net MVC。首先我们对后台代码和web部分进行了分层,数据访问对象为Entity ,controllers 为各个请求的API web的和winform的在一起,我们依旧使用了简单的三层架构,xxxLogic.cs 其实是实际的业务逻辑代码:

 
 
所有的是基于WebAPI形式的 老套路在初始化时进行 router注册 以便让请求映射到对应的controller 不用多说了,还有是asp.net MVC是可以配置返回数据格式为xml 或者json的。
  1. 1 public class Global : System.Web.HttpApplication
  2. 2 {
  3. 3
  4. 4     protected void Application_Start(object sender, EventArgs e)
  5. 5     {
  6. 6         
  7. 7         AreaRegistration.RegisterAllAreas();
  8. 8         //GlobalConfiguration.Configuration.ParameterBindingRules.
  9. 9         //    Insert(0,SimplePostVariableParameterBinding.HookupParameterBinding);
  10. 10         WebApiConfig.Register(GlobalConfiguration.Configuration);
  11. 11         FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
  12. 12         RouteConfig.RegisterRoutes(RouteTable.Routes);
  13. 13     }
  14. 14
  15. 15     protected void Session_Start(object sender, EventArgs e)
  16. 16     {
  17. 17
  18. 18     }
  19. 19     protected void Application_BeginRequest(object sender, EventArgs e)
  20. 20     {
  21. 21         if (Context.Request.FilePath == "/") Context.RewritePath("Default.aspx");
  22. 22     }
  23. 23
  24. 24     
  25. 25
  26. 26     public override void Init()
  27. 27     {
  28. 28         PostAuthenticateRequest += WebApiApplication_PostAuthenticateRequest;
  29. 29
  30. 30         base.Init();
  31. 31     }
  32. 32     void WebApiApplication_PostAuthenticateRequest(object sender, EventArgs e)
  33. 33     {
  34. 34         HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
  35. 35     }
  36. 36 }
复制代码
  1. 1 public class RouteConfig
  2. 2 {
  3. 3     public static void RegisterRoutes(RouteCollection routes)
  4. 4     {
  5. 5         routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
  6. 6         
  7. 7         routes.MapRoute(
  8. 8             name: "Default",
  9. 9             url: "{controller}/{action}/{id}",
  10. 10             defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
  11. 11         );
  12. 12     }
  13. 13 }
复制代码
  1. 1 public static class WebApiConfig
  2. 2 {
  3. 3     public static void Register(HttpConfiguration config)
  4. 4     {
  5. 5         System.Web.Mvc.ValueProviderFactories.Factories.Add(new System.Web.Mvc.JsonValueProviderFactory());
  6. 6         //下面这句务必加上 否则就只能在IE下才能得到正确的json数据
  7. 7         //必须要添加http.formating 那个dll的引用
  8. 8         GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
  9. 9         //GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
  10. 10         config.Routes.MapHttpRoute(
  11. 11             name: "DefaultApi",
  12. 12             routeTemplate: "api/{controller}/{action}/{id}",
  13. 13             defaults: new { controller = "Home", action = "Index", id = RouteParameter.Optional }//new { id = RouteParameter.Optional }
  14. 14         );
  15. 15
  16. 16     }
  17. 17 }
复制代码
来看下我们的后台进行权限控制的代码,我们并未进行复杂的权限控制 仅仅只是判断登录:
  1. 1 public class Power : System.Web.Http.Filters.ActionFilterAttribute
  2. 2 {
  3. 3     public Power(string actioinName)
  4. 4     {
  5. 5
  6. 6     }
  7. 7     public override void OnActionExecuting(HttpActionContext actionContext)
  8. 8     {
  9. 9         HttpRequest request = HttpContext.Current.Request;
  10. 10         string username = request.Headers["username"];
  11. 11         string password = request.Headers["password"];
  12. 12
  13. 13
  14. 14         LicWebUtil util = new LicWebUtil();
  15. 15         var loginuser = util.loginNormal(username, password);
  16. 16         if (loginuser==null)
  17. 17         {
  18. 18             throw new Exception("未登录啊");
  19. 19         }
  20. 20     }
  21. 21 }
复制代码
数据处理

然后是数据处理,我们在服务端构建了统一的数据格式APIResult:
  1. 1 public class APIResult
  2. 2 {
  3. 3     public bool Success { get; set; }
  4. 4     public object Data { get; set; }
  5. 5
  6. 6     public string Msg { get; set; }
  7. 7
  8. 8 }
复制代码
 
web端的 通用 js请求 ,get和post 注意promise 应用的机巧 ,以及提交时在登录成功了的情况下 会自动往header里加权限 。
  1. 1 var app = angular.module("scpSite", ["ui.router"]);
  2. 2
  3. 3 //注入一个util 工具类
  4. 4 app.service("Util", function ($http, $rootScope, $q, $compile) {
  5. 5
  6. 6     var boxPromise = function (promise) {
  7. 7         var myPromise = {
  8. 8             prom: promise,
  9. 9             then: function (fun1, fun2) {
  10. 10                 promise.then(function (response) {
  11. 11                     //response.status==200// response.status==500 这里就不使用这种方式判断了
  12. 12                     if (response && response.data && response.data.Success) {
  13. 13                         fun1(response);
  14. 14                     }
  15. 15                     else {
  16. 16                         //服务端有正常的错误消息返回
  17. 17                         if (response && response.data && response.data.Success == false) {
  18. 18                             alert("获取数据失败:" + response.data.Message);
  19. 19                         }
  20. 20                             //throw exception 500错误
  21. 21                         else {
  22. 22                             alert("获取数据失败:" + response.data.ExceptionMessage);
  23. 23                         }
  24. 24
  25. 25                         if (fun2 != undefined)
  26. 26                             fun2(response);
  27. 27                     }
  28. 28                 }, function () {//在刚httpget的时候可以 而在此处根本就不会执行此函数
  29. 29                 });
  30. 30             }
  31. 31         };
  32. 32         return myPromise;
  33. 33     };
  34. 34
  35. 35     var _this = {
  36. 36         get: function (path, _params) {
  37. 37             var promise;
  38. 38             if (_params != undefined && _params != null) {
  39. 39                 promise = $http.get(path, { headers: { username: $rootScope.username, password: $rootScope.password }, params: _params }).then(function (response) {
  40. 40                     return response;
  41. 41                 }, function (response) {
  42. 42                     return response;
  43. 43                 });
  44. 44             } else {
  45. 45                 promise = $http.get(path, { headers: { username: $rootScope.username, password: $rootScope.password } }).then(function (response) {
  46. 46                     return response;
  47. 47                 }, function (response) {
  48. 48                     return response;
  49. 49                 });
  50. 50             }
  51. 51             return boxPromise(promise);
  52. 52         },
  53. 53         post: function (url, data) {
  54. 54             var promise;
  55. 55             if (data != undefined && data != null) {
  56. 56                 promise = $http.post(url,data , { headers: { username: $rootScope.username, password: $rootScope.password }}).then(function (response) {
  57. 57                     return response;
  58. 58                 }, function (response) {
  59. 59                     return response;
  60. 60                 });
  61. 61             } else {
  62. 62                 promise = $http.post(url, { headers: { username: $rootScope.username, password: $rootScope.password } }).then(function (response) {
  63. 63                     return response;
  64. 64                 }, function (response) {
  65. 65                     return response;
  66. 66                 });
  67. 67             }
  68. 68             return boxPromise(promise);
  69. 69         }
  70. 70
  71. 71     }
  72. 72
  73. 73     return _this;
  74. 74
  75. 75 });
复制代码
然后是登录 ,登录成功了 angularjs 全局变量就会有用户信息 ,提交时post就会自动往权限里加。结合上面的一起运作 ,体会一下这样设计 以及联动运作的精妙之处,并且如果服务端有错误 在post之初就会自动报出错误 并且利用promise的机制 自动阻断 不让执行进程传到下层 不让逻辑往后走,如果数据成功 则会自动调用下层promise的数据展现操作进行页面渲染,这样来达到规范化:
  1. 1 app.controller("MainController", function ($rootScope, $scope, $rootScope, $stateParams, $state, Util) {
  2. 2
  3. 3     $rootScope.loginok = false;
  4. 4
  5. 5     $scope.username = "";
  6. 6     $scope.password = "";
  7. 7
  8. 8     $rootScope.username = "";
  9. 9     $rootScope.password = "";
  10. 10
  11. 11     //登录测试 否则弹出登录对话框
  12. 12     $scope.loginTest = function () {
  13. 13
  14. 14         if ($scope.loginok == false) {
  15. 15             $('#myModal').modal({ backdrop: 'static', keyboard: false, show: true });
  16. 16         }
  17. 17     }
  18. 18
  19. 19     $scope.login = function () {
  20. 20
  21. 21         Util.get("/api/Util/login", { username: $scope.username, password: $scope.password }).then(function (res) {
  22. 22             if (res.data.Data==null||res.data.Data == "") {
  23. 23                 alert("登录失败");
  24. 24             }
  25. 25             else {
  26. 26                 alert("登录成功");
  27. 27                 $rootScope.username = $scope.username;
  28. 28                 $rootScope.password = $scope.password;
  29. 29                 $rootScope.loginok = true;
  30. 30
  31. 31
  32. 32                 $('#myModal').modal('hide');
  33. 33             }
  34. 34         }, function (res) {
  35. 35             alert("登录遇到错误");
  36. 36         });
  37. 37
  38. 38
  39. 39     }
  40. 40
  41. 41     $scope.copyrightEndYear = "2018";
  42. 42     $scope.GetCopyrightEndYear = function () {
  43. 43         Util.get("/api/Util/GetCopyrightEndYear").then(function (res) {
  44. 44             $scope.copyrightEndYear = res.data.Data;
  45. 45         });
  46. 46     }
  47. 47     $scope.GetCopyrightEndYear();
  48. 48 });
复制代码
如果登录成功后那么后续就是简单的平铺直述的调用了,下面是一个简单的示例。
  1. 1 app.controller("DefaultController", function ($scope, Util) {
  2. 2     $scope.newsList = [];
  3. 3     $scope.inititalData = function () {
  4. 4         Util.get("/api/CMS/GetContentByCategory", { category: "新闻中心", top: 5, getContent: false }).then(function (res) {
  5. 5             $scope.newsList = res.data.Data;
  6. 6         });
  7. 7     }
  8. 8     $scope.inititalData();
  9. 9 });
复制代码
看下我们的使用效果


 
 不用想当然的在客户端调试把mask去掉以为就可用了哈,我们是做了完善的后台校验机制的。

 
同一接口应用于winform端

 看,跟上面相结合的统一处理 WebAPI promise post  的结合应用正是这个框架的优雅之处 ,并且 APIResult格式 是统一的  在winform客户端只要实现一个json解析 ,同一个接口或者业务逻辑就可应用于Windows客户端了 就这样简单的就达到了同步。关于winform 接口 结果数据的处理,很简单 我们构建一个跟json一致c#的类  反序列化即可。这是winform 构造出的请求 和其他代码,注意我们是用上面同一规格APIResult 来进行json解析的,通过httprequest 请求 ,原先设计的我们有广告 也就是图片 还有文本。那么应该怎么处理呢? 不是有泛型吗? 看我的:注意泛型的应用 ,如果是img则转而使用stream解析,普通的则使用APIResult 解析出文本。
  1. 1 public static T DownloadSomething<T>(string url,string appendUrl,Dictionary<string,string> pars)
  2. 2 {
  3. 3     try
  4. 4     {
  5. 5         if (string.IsNullOrEmpty(appendUrl) == false)
  6. 6         {
  7. 7             url += appendUrl;
  8. 8         }
  9. 9
  10. 10         if (pars != null)
  11. 11         {
  12. 12             var enumer = pars.GetEnumerator();
  13. 13             int parIndex = 0;
  14. 14             while (enumer.MoveNext())
  15. 15             {
  16. 16                 if (parIndex == 0)
  17. 17                 {
  18. 18                     url += string.Format("?{0}={1}", enumer.Current.Key, enumer.Current.Value);
  19. 19                 }
  20. 20                 else
  21. 21                 {
  22. 22                     url += string.Format("&{0}={1}", enumer.Current.Key, enumer.Current.Value);
  23. 23                 }
  24. 24                 parIndex++;
  25. 25             }
  26. 26         }
  27. 27
  28. 28         HttpWebRequest rq = (HttpWebRequest)WebRequest.Create(url);
  29. 29         rq.Headers["Accept-Encoding"] = "utf-8";
  30. 30
  31. 31         if (WriterRuntime.loginUser != null && string.IsNullOrEmpty(WriterRuntime.loginUser.UserNameOrHardwareID) == false)
  32. 32         {
  33. 33             rq.Headers["username"] = WriterRuntime.loginUser.UserNameOrHardwareID;
  34. 34             rq.Headers["password"] = WriterRuntime.loginUser.Password;
  35. 35         }
  36. 36         rq.Method = "GET";
  37. 37         rq.Timeout = 10000;//2秒超时
  38. 38         T retData = default(T);
  39. 39
  40. 40         HttpWebResponse rc = (HttpWebResponse)rq.GetResponse();
  41. 41         Stream stream = rc.GetResponseStream();
  42. 42         StreamReader sr = new StreamReader(stream, Encoding.UTF8);
  43. 43
  44. 44
  45. 45         if (typeof(T) == typeof(Image))
  46. 46         {
  47. 47             retData = (T)(object)Image.FromStream(stream);
  48. 48         }
  49. 49         else
  50. 50         {
  51. 51             APIResult apiResult = JsonConvert.DeserializeObject<APIResult>(sr.ReadToEnd());
  52. 52
  53. 53             if (apiResult.Data != null)
  54. 54             {
  55. 55                 if ((typeof(T) == typeof(string)))
  56. 56                 {
  57. 57                     retData = (T)(object)apiResult.Data;
  58. 58                 }
  59. 59                 else if ((typeof(T) == typeof(Int64)))
  60. 60                 {
  61. 61                     retData = (T)(object)apiResult.Data;
  62. 62                 }
  63. 63                 else if((typeof(T) == typeof(bool))){
  64. 64                     retData = (T)(object)apiResult.Data;
  65. 65                 }
  66. 66                 else
  67. 67                 {
  68. 68                     retData = ((Newtonsoft.Json.Linq.JObject)apiResult.Data).ToObject<T>();
  69. 69                 }
  70. 70
  71. 71             }
  72. 72         }
  73. 73
  74. 74         sr.Close();
  75. 75         rc.Close();
  76. 76
  77. 77         if (typeof(T) == typeof(string))
  78. 78         {
  79. 79             return retData;
  80. 80         }
  81. 81         else if (typeof(T) == typeof(Image))
  82. 82         {
  83. 83             return retData;
  84. 84         }
  85. 85         else
  86. 86         {
  87. 87             return retData;
  88. 88         }
  89. 89
  90. 90     }
  91. 91     catch (Exception ex)
  92. 92     {
  93. 93         MessageBox.Show("与服务器连接失败");
  94. 94         Application.Exit();
  95. 95     }
  96. 96     return default(T);
  97. 97 }
复制代码


 
还有,我们的客户端是支持升级的,固定的连接上服务器后先进性API版本校验 ,如果校验失败 会强迫客户端进行版本更新。强不强迫客户端更新的决定权在我们 只要把服务端的ver接口数字调高 就可以把低于某版本的客户端淘汰掉了。看资本的力量如此强大 ,这不就像手机上的某某xxAPP吗 其实啥实质功能都没更新 ,更新了一堆的广告。
  1. 1 private void LoginForm_Load(object sender, EventArgs e)
  2. 2 {
  3. 3
  4. 4     //先进行联网和版本检测 失败则退出
  5. 5     Int64 serverVer = USBKeyWriterUtil.DownloadSomething<Int64>(WriterRuntime.apiUrl, "Util/ClientAPI_Ver", null);
  6. 6     if (serverVer > WriterRuntime.ver)
  7. 7     {
  8. 8         USBKeyWriter.UI.Upgrade uDlg = new USBKeyWriter.UI.Upgrade();
  9. 9         uDlg.ShowDialog(this);
  10. 10         return;
  11. 11     }
  12. 12     Process[] app = Process.GetProcessesByName("NewScp");
  13. 13     if (app.Length > 0)
  14. 14     {
  15. 15         MessageBox.Show("请先退出Dicom打印服务软件,再运行此授权机程序。");//请先退出Dicom打印服务软件(任务管理器NewScp进程)再运行此授权机程序
  16. 16         Application.Exit();
  17. 17         return;
  18. 18     }
  19. 19
  20. 20     //初始化runtime
  21. 21     rt = WriterRuntime.GetInstance();
  22. 22     rt.Initial();
  23. 23
  24. 24     string deviceUserName = USBKeyWriterUtil.getDefaultLoginId();
  25. 25     if (string.IsNullOrEmpty(deviceUserName))
  26. 26     {
  27. 27         tbxuname.Text = "admin";
  28. 28     }
  29. 29     else
  30. 30     {
  31. 31         tbxuname.Text = deviceUserName;
  32. 32     }
  33. 33 }
复制代码
 

 
 
好了 全部 结束 ,感谢各位看官观赏,周末愉快。
 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

灌篮少年

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表