restful服务性能好、基于约定、松耦合,完全可以

作者: 编程  发布:2019-11-04

  wcf虽然功能多、扩展性强但是也面临配置忒多,而且restful的功能相当怪异,并且目前没法移植。asp.net core虽然支持webapi,但是功能也相对繁多、配置复杂。就没有一个能让码农们安安心心的写webapi,无需考虑性能、配置、甚至根据问题场景自行设计、改造等问题的方案么?

前言

小李:“胖子,上头叫你对接我的数据好了没有?”

胖子:“那是你的事,你都不提供数据源,我咋接?”

小李:“你想要什么样的数据源?”

胖子:“我想要一个调用简单点的!”

小李:“我这个数据源是在linux平台使用docker封装发布的,webapi的怎么样?”

胖子:“也行,这项目工期快用完了,你得提供api封装sdk,另外我这边对性能有要求的!”

小李:“webapi多好,基于json各个平台都能对接,性能还不错的!”

胖子:“我只关心我的业务,不是我的业务代码,多一行我都不想码,不然没按时完成算你的!另外用webapi到时候请求量一大,到时候端口用完了,连接不了这锅也得你背!”

小李:“我@##¥%*#¥@#&##@……”

面对胖子这些说辞,小李心里面虽然一万只草泥马在奔腾,但是项目还是要完成是不?另外胖子说的也不无道理!小李作为一个在C#下侵淫多年老鸟,很快想出一个办法——rpc!首先当然是选wcf,这个巨硬的企业级产品在快速开发上除了配置上坑爹了一点,针对客户端的对接真的非常快。小李仔细一研究wcf service 发现目前在linux下玩不了,心里面又是了一阵@##¥%*#¥@#&##@……

胖子:“小李纠结啥,要不就弄个三方的搞一下算了,就算出事了,你说不定都已经离职了,怕啥……”

看着胖子一脸猥琐的表情,小李那是一个气啊,就怪自已平时牛逼吹上天,这时候怎么好怂呢,一咬牙:“你放心,误不了你的事!”。小李一边回复,心里面开始盘算着自行实现一个功能简易,性能高效,使用简单的rpc了。

  上面小李与胖子的场景,在开发的时候也是经典案例,回到正题来:本人认为rpc主要是:调用方法及参数序列化、socket传输、调用方法及参数反序列化、映射到本地并采用与请求相同流程回应客户端的一套方案。其中关键点简单分析主要有:序列化与反序列化、高性能tcp、远程方法反转、客户端代码生成四个方面;tcp还是使用iocp好了,其他接着一一分析。

        摘要:“约定优于配置”这是一个相当棒的经验,SOAP服务性能差、基于配置、紧耦合,restful服务性能好、基于约定、松耦合,现在我就把使用Spring MVC发布restful服务的过程同大家分享。代码之优雅、过程之简单、编码之愉快,不是发布SOAP服务所能匹敌的。

1.MVC教程首页
http://www.asp.net/learn/mvc/?lang=cs

  当然不是,特别是在dnc2.0已经相当强大的此时,完全可以自行设计一套简洁、高效的webapi框架!说到自行写一套框架,很多码农们就可能会想到开发工作量难以想像,事实真的如此么?java因为开源众多,很多对mvc稍有了解的都可以拿这个拿那个拼出一个自已的mvc框架;而面对日益强大的dnc,本人觉得C#根本无需东拼西凑这么麻烦,完全可以根据自已的需求简单快速的写出一个来,不服就开干!

序列化与反序列化

  序列化与反序列化这个选二进制一般比json的好,ms版的BinaryFormatter 通用性强,但是他的性能、model的标记写法等估计又要被喷了;找到Expression序列化,结果还是走的类似于soap xml这一套,想想算了:本地方法调用都是纳秒级的,io都是毫秒级别的,socket的一次就传这么传这么大一堆,就算局域网也伤不起呀,想轻量化提升性能都难,自行实现一个简单的好了。

  1 /****************************************************************************
  2 *Copyright (c) 2018 Microsoft All Rights Reserved.
  3 *CLR版本: 4.0.30319.42000
  4 *机器名称:WENLI-PC
  5 *公司名称:Microsoft
  6 *命名空间:SAEA.RPC.Serialize
  7 *文件名: SerializeUtil
  8 *版本号: V1.0.0.0
  9 *唯一标识:9e919430-465d-49a3-91be-b36ac682e283
 10 *当前的用户域:WENLI-PC
 11 *创建人: yswenli
 12 *电子邮箱:wenguoli_520@qq.com
 13 *创建时间:2018/5/22 13:17:36
 14 *描述:
 15 *
 16 *=====================================================================
 17 *修改标记
 18 *修改时间:2018/5/22 13:17:36
 19 *修改人: yswenli
 20 *版本号: V1.0.0.0
 21 *描述:
 22 *
 23 *****************************************************************************/
 24 using SAEA.RPC.Model;
 25 using System;
 26 using System.Collections.Generic;
 27 using System.Text;
 28 
 29 namespace SAEA.RPC.Serialize
 30 {
 31     /// <summary>
 32     /// rpc参数序列化处理
 33     /// </summary>
 34     public class ParamsSerializeUtil
 35     {
 36         /// <summary>
 37         /// len+data
 38         /// </summary>
 39         /// <param name="param"></param>
 40         /// <returns></returns>
 41         public static byte[] Serialize(object param)
 42         {
 43             List<byte> datas = new List<byte>();
 44 
 45             var len = 0;
 46             byte[] data = null;
 47 
 48             if (param == null)
 49             {
 50                 len = 0;
 51             }
 52             else
 53             {
 54                 if (param is string)
 55                 {
 56                     data = Encoding.UTF8.GetBytes((string)param);
 57                 }
 58                 else if (param is byte)
 59                 {
 60                     data = new byte[] { (byte)param };
 61                 }
 62                 else if (param is bool)
 63                 {
 64                     data = BitConverter.GetBytes((bool)param);
 65                 }
 66                 else if (param is short)
 67                 {
 68                     data = BitConverter.GetBytes((short)param);
 69                 }
 70                 else if (param is int)
 71                 {
 72                     data = BitConverter.GetBytes((int)param);
 73                 }
 74                 else if (param is long)
 75                 {
 76                     data = BitConverter.GetBytes((long)param);
 77                 }
 78                 else if (param is float)
 79                 {
 80                     data = BitConverter.GetBytes((float)param);
 81                 }
 82                 else if (param is double)
 83                 {
 84                     data = BitConverter.GetBytes((double)param);
 85                 }
 86                 else if (param is DateTime)
 87                 {
 88                     var str = "wl" + ((DateTime)param).Ticks;
 89                     data = Encoding.UTF8.GetBytes(str);
 90                 }
 91                 else if (param is byte[])
 92                 {
 93                     data = (byte[])param;
 94                 }
 95                 else
 96                 {
 97                     var type = param.GetType();
 98 
 99                     if (type.IsGenericType || type.IsArray)
100                     {
101                         data = SerializeList((System.Collections.IEnumerable)param);
102                     }
103                     else if (type.IsGenericTypeDefinition)
104                     {
105                         data = SerializeDic((System.Collections.IDictionary)param);
106                     }
107                     else if (type.IsClass)
108                     {
109                         var ps = type.GetProperties();
110 
111                         if (ps != null && ps.Length > 0)
112                         {
113                             List<object> clist = new List<object>();
114 
115                             foreach (var p in ps)
116                             {
117                                 clist.Add(p.GetValue(param));
118                             }
119                             data = Serialize(clist.ToArray());
120                         }
121                     }
122                 }
123                 len = data.Length;
124             }
125             datas.AddRange(BitConverter.GetBytes(len));
126             if (len > 0)
127             {
128                 datas.AddRange(data);
129             }
130             return datas.Count == 0 ? null : datas.ToArray();
131         }
132 
133 
134         private static byte[] SerializeList(System.Collections.IEnumerable param)
135         {
136             List<byte> list = new List<byte>();
137 
138             if (param != null)
139             {
140                 List<byte> slist = new List<byte>();
141 
142                 foreach (var item in param)
143                 {
144                     var type = item.GetType();
145 
146                     var ps = type.GetProperties();
147                     if (ps != null && ps.Length > 0)
148                     {
149                         List<object> clist = new List<object>();
150                         foreach (var p in ps)
151                         {
152                             clist.Add(p.GetValue(item));
153                         }
154 
155                         var clen = 0;
156 
157                         var cdata = Serialize(clist.ToArray());
158 
159                         if (cdata != null)
160                         {
161                             clen = cdata.Length;
162                         }
163 
164                         slist.AddRange(BitConverter.GetBytes(clen));
165                         slist.AddRange(cdata);
166                     }
167                 }
168 
169                 var len = 0;
170 
171                 if (slist.Count > 0)
172                 {
173                     len = slist.Count;
174                 }
175                 list.AddRange(BitConverter.GetBytes(len));
176                 list.AddRange(slist.ToArray());
177             }
178             return list.ToArray();
179         }
180 
181         private static byte[] SerializeDic(System.Collections.IDictionary param)
182         {
183             List<byte> list = new List<byte>();
184 
185             if (param != null && param.Count > 0)
186             {
187                 foreach (KeyValuePair item in param)
188                 {
189                     var type = item.GetType();
190                     var ps = type.GetProperties();
191                     if (ps != null && ps.Length > 0)
192                     {
193                         List<object> clist = new List<object>();
194                         foreach (var p in ps)
195                         {
196                             clist.Add(p.GetValue(item));
197                         }
198                         var clen = 0;
199 
200                         var cdata = Serialize(clist.ToArray());
201 
202                         if (cdata != null)
203                         {
204                             clen = cdata.Length;
205                         }
206 
207                         list.AddRange(BitConverter.GetBytes(clen));
208                         list.AddRange(cdata);
209                     }
210                 }
211             }
212             return list.ToArray();
213         }
214 
215         /// <summary>
216         /// len+data
217         /// </summary>
218         /// <param name="params"></param>
219         /// <returns></returns>
220         public static byte[] Serialize(params object[] @params)
221         {
222             List<byte> datas = new List<byte>();
223 
224             if (@params != null)
225             {
226                 foreach (var param in @params)
227                 {
228                     datas.AddRange(Serialize(param));
229                 }
230             }
231 
232             return datas.Count == 0 ? null : datas.ToArray();
233         }
234 
235         /// <summary>
236         /// 反序列化
237         /// </summary>
238         /// <param name="types"></param>
239         /// <param name="datas"></param>
240         /// <returns></returns>
241         public static object[] Deserialize(Type[] types, byte[] datas)
242         {
243             List<object> list = new List<object>();
244 
245             var len = 0;
246 
247             byte[] data = null;
248 
249             int offset = 0;
250 
251             for (int i = 0; i < types.Length; i++)
252             {
253                 list.Add(Deserialize(types[i], datas, ref offset));
254             }
255 
256             return list.ToArray();
257         }
258 
259         /// <summary>
260         /// 反序列化
261         /// </summary>
262         /// <param name="type"></param>
263         /// <param name="datas"></param>
264         /// <param name="offset"></param>
265         /// <returns></returns>
266         public static object Deserialize(Type type, byte[] datas, ref int offset)
267         {
268             dynamic obj = null;
269 
270             var len = 0;
271 
272             byte[] data = null;
273 
274             len = BitConverter.ToInt32(datas, offset);
275             offset += 4;
276             if (len > 0)
277             {
278                 data = new byte[len];
279                 Buffer.BlockCopy(datas, offset, data, 0, len);
280                 offset += len;
281 
282                 if (type == typeof(string))
283                 {
284                     obj = Encoding.UTF8.GetString(data);
285                 }
286                 else if (type == typeof(byte))
287                 {
288                     obj = (data);
289                 }
290                 else if (type == typeof(bool))
291                 {
292                     obj = (BitConverter.ToBoolean(data, 0));
293                 }
294                 else if (type == typeof(short))
295                 {
296                     obj = (BitConverter.ToInt16(data, 0));
297                 }
298                 else if (type == typeof(int))
299                 {
300                     obj = (BitConverter.ToInt32(data, 0));
301                 }
302                 else if (type == typeof(long))
303                 {
304                     obj = (BitConverter.ToInt64(data, 0));
305                 }
306                 else if (type == typeof(float))
307                 {
308                     obj = (BitConverter.ToSingle(data, 0));
309                 }
310                 else if (type == typeof(double))
311                 {
312                     obj = (BitConverter.ToDouble(data, 0));
313                 }
314                 else if (type == typeof(decimal))
315                 {
316                     obj = (BitConverter.ToDouble(data, 0));
317                 }
318                 else if (type == typeof(DateTime))
319                 {
320                     var dstr = Encoding.UTF8.GetString(data);
321                     var ticks = long.Parse(dstr.Substring(2));
322                     obj = (new DateTime(ticks));
323                 }
324                 else if (type == typeof(byte[]))
325                 {
326                     obj = (byte[])data;
327                 }
328                 else if (type.IsGenericType)
329                 {
330                     obj = DeserializeList(type, data);
331                 }
332                 else if (type.IsArray)
333                 {
334                     obj = DeserializeArray(type, data);
335                 }
336                 else if (type.IsGenericTypeDefinition)
337                 {
338                     obj = DeserializeDic(type, data);
339                 }
340                 else if (type.IsClass)
341                 {
342                     var instance = Activator.CreateInstance(type);
343 
344                     var ts = new List<Type>();
345 
346                     var ps = type.GetProperties();
347 
348                     if (ps != null)
349                     {
350                         foreach (var p in ps)
351                         {
352                             ts.Add(p.PropertyType);
353                         }
354                         var vas = Deserialize(ts.ToArray(), data);
355 
356                         for (int j = 0; j < ps.Length; j++)
357                         {
358                             try
359                             {
360                                 if (!ps[j].PropertyType.IsGenericType)
361                                 {
362                                     ps[j].SetValue(instance, Convert.ChangeType(vas[j], ps[j].PropertyType), null);
363                                 }
364                                 else
365                                 {
366                                     Type genericTypeDefinition = ps[j].PropertyType.GetGenericTypeDefinition();
367                                     if (genericTypeDefinition == typeof(Nullable<>))
368                                     {
369                                         ps[j].SetValue(instance, Convert.ChangeType(vas[j], Nullable.GetUnderlyingType(ps[j].PropertyType)), null);
370                                     }
371                                     else
372                                     {
373                                         //List<T>问题
374                                         ps[j].SetValue(instance, Convert.ChangeType(vas[j], ps[j].PropertyType), null);
375                                     }
376                                 }
377                             }
378                             catch (Exception ex)
379                             {
380                                 Console.WriteLine("反序列化不支持的类型:" + ex.Message);
381                             }
382                         }
383                     }
384                     obj = (instance);
385                 }
386                 else
387                 {
388                     throw new RPCPamarsException("ParamsSerializeUtil.Deserialize 未定义的类型:" + type.ToString());
389                 }
390 
391             }
392             return obj;
393         }
394 
395 
396         private static object DeserializeList(Type type, byte[] datas)
397         {
398             List<object> result = new List<object>();
399             var stype = type.GenericTypeArguments[0];
400 
401             var len = 0;
402             var offset = 0;
403             //容器大小
404             len = BitConverter.ToInt32(datas, offset);
405             offset += 4;
406             byte[] cdata = new byte[len];
407             Buffer.BlockCopy(datas, offset, cdata, 0, len);
408             offset += len;
409 
410             //子项内容
411             var slen = 0;
412             var soffset = 0;
413             while (soffset < len)
414             {
415                 slen = BitConverter.ToInt32(cdata, soffset);
416                 var sdata = new byte[slen + 4];
417                 Buffer.BlockCopy(cdata, soffset, sdata, 0, slen + 4);
418                 soffset += slen + 4;
419 
420                 if (slen > 0)
421                 {
422                     int lloffset = 0;
423                     var sobj = Deserialize(stype, sdata, ref lloffset);
424                     if (sobj != null)
425                         result.Add(sobj);
426                 }
427                 else
428                 {
429                     result.Add(null);
430                 }
431             }
432             return result;
433         }
434 
435         private static object DeserializeArray(Type type, byte[] datas)
436         {
437             var obj = DeserializeList(type, datas);
438 
439             if (obj == null) return null;
440 
441             var list = (obj as List<object>);
442 
443             return list.ToArray();
444         }
445 
446         private static object DeserializeDic(Type type, byte[] datas)
447         {
448             dynamic obj = null;
449 
450 
451 
452             return obj;
453         }
454     }
455 }

  实现的过程中,一般结构、类都还比较顺利,但是数组、List、Dictionary还是遇到了一些麻烦,暂时先放着,找到办法再说。真要是传这些,目前先用其他序列化成byte[]来做……

        关键字:java, rest, webservice, spring mvc

2.MVC概况
2.1创建一个基于数据库的"电影"web应用
http://www.asp.net/learn/mvc/tutorial-21-cs.aspx

  设计的编码思路就是仿asp.net mvc,原因就是asp.net mvc成功发展了这么多年,有着大量的C#码农习惯了这套优良的编码方式;至于spring mvc、spring boot那些,站在使用者的角度来说,光配置和注解都能敲死人,如要要说简洁快速,asp.net mvc比他强多了,更别提ruby on rails。不扯远了,下面就按C#经典来。那么需要考虑的问题有tcp、http、request、response、server、controller、actionresult、routetable等,下面就一一来解决这个问题。

远程方法反转

  远程方法反转即是将接收到的数据定位到本地的对象方法上,如果代码生成、参数使用使用泛型反序列化,理论上是可以提升一些性能的;但是一边写服务业务,一边编写定义结构文件、还一边生成服务代码,本地方法都是纳秒级、相对io的速度来讲,如果为了这点性能提升,在使用的时候估计又是一阵@##¥%*#¥@#&##@……,所以还是使用反射、拆箱吧。

  1 /****************************************************************************
  2 *Copyright (c) 2018 Microsoft All Rights Reserved.
  3 *CLR版本: 4.0.30319.42000
  4 *机器名称:WENLI-PC
  5 *公司名称:Microsoft
  6 *命名空间:SAEA.RPC.Common
  7 *文件名: RPCInovker
  8 *版本号: V1.0.0.0
  9 *唯一标识:289c03b9-3910-4e15-8072-93243507689c
 10 *当前的用户域:WENLI-PC
 11 *创建人: yswenli
 12 *电子邮箱:wenguoli_520@qq.com
 13 *创建时间:2018/5/17 14:11:30
 14 *描述:
 15 *
 16 *=====================================================================
 17 *修改标记
 18 *修改时间:2018/5/17 14:11:30
 19 *修改人: yswenli
 20 *版本号: V1.0.0.0
 21 *描述:
 22 *
 23 *****************************************************************************/
 24 using SAEA.RPC.Model;
 25 using SAEA.RPC.Net;
 26 using SAEA.RPC.Serialize;
 27 using SAEA.Sockets.Interface;
 28 using System;
 29 using System.Linq;
 30 using System.Reflection;
 31 
 32 namespace SAEA.RPC.Common
 33 {
 34     /// <summary>
 35     /// RPC将远程调用反转到本地服务
 36     /// </summary>
 37     public class RPCReversal
 38     {
 39         static object _locker = new object();
 40         
 41 
 42         /// <summary>
 43         /// 执行方法
 44         /// </summary>
 45         /// <param name="action"></param>
 46         /// <param name="obj"></param>
 47         /// <param name="args"></param>
 48         /// <returns></returns>
 49         private static object ReversalMethod(MethodInfo action, object obj, object[] args)
 50         {
 51             object result = null;
 52             try
 53             {
 54                 var @params = action.GetParameters();
 55 
 56                 if (@params != null && @params.Length > 0)
 57                 {
 58                     result = action.Invoke(obj, args);
 59                 }
 60                 else
 61                 {
 62                     result = action.Invoke(obj, null);
 63                 }
 64             }
 65             catch (Exception ex)
 66             {
 67                 throw new RPCPamarsException($"{obj}/{action.Name},出现异常:{ex.Message}", ex);
 68             }
 69             return result;
 70         }
 71 
 72 
 73         public static object Reversal(IUserToken userToken, string serviceName, string methodName, object[] inputs)
 74         {
 75             lock (_locker)
 76             {
 77                 try
 78                 {
 79                     var serviceInfo = RPCMapping.Get(serviceName, methodName);
 80 
 81                     if (serviceInfo == null)
 82                     {
 83                         throw new RPCNotFundException($"当前请求找不到:{serviceName}/{methodName}", null);
 84                     }
 85 
 86                     var nargs = new object[] { userToken, serviceName, methodName, inputs };
 87 
 88                     if (serviceInfo.FilterAtrrs != null && serviceInfo.FilterAtrrs.Count > 0)
 89                     {
 90                         foreach (var arr in serviceInfo.FilterAtrrs)
 91                         {
 92                             var goOn = (bool)arr.GetType().GetMethod("OnActionExecuting").Invoke(arr, nargs.ToArray());
 93 
 94                             if (!goOn)
 95                             {
 96                                 return new RPCNotFundException("当前逻辑已被拦截!", null);
 97                             }
 98                         }
 99                     }
100 
101                     if (serviceInfo.ActionFilterAtrrs != null && serviceInfo.ActionFilterAtrrs.Count > 0)
102                     {
103                         foreach (var arr in serviceInfo.ActionFilterAtrrs)
104                         {
105                             var goOn = (bool)arr.GetType().GetMethod("OnActionExecuting").Invoke(arr, nargs.ToArray());
106 
107                             if (!goOn)
108                             {
109                                 return new RPCNotFundException("当前逻辑已被拦截!", null);
110                             }
111                         }
112                     }
113 
114                     var result = ReversalMethod(serviceInfo.Mothd, serviceInfo.Instance, inputs);
115 
116                     nargs = new object[] { userToken, serviceName, methodName, inputs, result };
117 
118                     if (serviceInfo.FilterAtrrs != null && serviceInfo.FilterAtrrs.Count > 0)
119                     {
120                         foreach (var arr in serviceInfo.FilterAtrrs)
121                         {
122                             arr.GetType().GetMethod("OnActionExecuted").Invoke(arr, nargs);
123                         }
124                     }
125 
126                     if (serviceInfo.ActionFilterAtrrs != null && serviceInfo.ActionFilterAtrrs.Count > 0)
127                     {
128                         foreach (var arr in serviceInfo.FilterAtrrs)
129                         {
130                             arr.GetType().GetMethod("OnActionExecuted").Invoke(arr, nargs);
131                         }
132                     }
133                     return result;
134                 }
135                 catch (Exception ex)
136                 {
137                     if (ex.Message.Contains("找不到此rpc方法"))
138                     {
139                         return new RPCNotFundException("找不到此rpc方法", ex);
140                     }
141                     else
142                     {
143                         return new RPCNotFundException("找不到此rpc方法", ex);
144                     }
145                 }
146             }
147         }
148 
149         /// <summary>
150         /// 反转到具体的方法上
151         /// </summary>
152         /// <param name="userToken"></param>
153         /// <param name="msg"></param>
154         /// <returns></returns>
155         public static byte[] Reversal(IUserToken userToken, RSocketMsg msg)
156         {
157             byte[] result = null;
158             try
159             {
160                 object[] inputs = null;
161 
162                 if (msg.Data != null)
163                 {
164                     var ptypes = RPCMapping.Get(msg.ServiceName, msg.MethodName).Pamars.Values.ToArray();
165 
166                     inputs = ParamsSerializeUtil.Deserialize(ptypes, msg.Data);
167                 }
168 
169                 var r = Reversal(userToken, msg.ServiceName, msg.MethodName, inputs);
170 
171                 if (r != null)
172                 {
173                     return ParamsSerializeUtil.Serialize(r);
174                 }
175             }
176             catch (Exception ex)
177             {
178                 throw new RPCPamarsException("RPCInovker.Invoke error:" + ex.Message, ex);
179             }
180             return result;
181 
182         }
183     }
184 }

        前提:IntelliJ IDEA (13.1.5 版本), apache maven (3.2.3 版本), Tomcat(7.0.56版本), Spring(3.2.4版本)

2.2MVC执行过程
http://www.asp.net/learn/mvc/tutorial-22-cs.aspx

  一、Tcp:这个是实现传输通信的底层,当然采用IOCP来提高吞吐量和性能,本人之前在做Redis Client等的时候就使用这个IOCP Socket的框架,此时正好也可以用上

客户端代码生成

  为了方便客户使用rpc,所以有rpc相关的代码在客户端那肯定是越少越好,如果光服务端方便,客户端估计又要@##¥%*#¥@#&##@……,所以将一些rpc相关代码生成好,客户端透明调用是必须的。

  1 /****************************************************************************
  2 *Copyright (c) 2018 Microsoft All Rights Reserved.
  3 *CLR版本: 4.0.30319.42000
  4 *机器名称:WENLI-PC
  5 *公司名称:Microsoft
  6 *命名空间:SAEA.RPC.Generater
  7 *文件名: CodeGnerater
  8 *版本号: V1.0.0.0
  9 *唯一标识:59ba5e2a-2fd0-444b-a260-ab68c726d7ee
 10 *当前的用户域:WENLI-PC
 11 *创建人: yswenli
 12 *电子邮箱:wenguoli_520@qq.com
 13 *创建时间:2018/5/17 18:30:57
 14 *描述:
 15 *
 16 *=====================================================================
 17 *修改标记
 18 *修改时间:2018/5/17 18:30:57
 19 *修改人: yswenli
 20 *版本号: V1.0.0.0
 21 *描述:
 22 *
 23 *****************************************************************************/
 24 using SAEA.RPC.Common;
 25 using SAEA.RPC.Model;
 26 using System;
 27 using System.Collections.Generic;
 28 using System.IO;
 29 using System.Linq;
 30 using System.Reflection;
 31 using System.Text;
 32 
 33 namespace SAEA.RPC.Generater
 34 {
 35     /// <summary>
 36     /// 代码生成器
 37     /// </summary>
 38     public static class CodeGnerater
 39     {
 40         static string space4 = "    ";
 41 
 42         /// <summary>
 43         /// 获取指定数量的空格
 44         /// </summary>
 45         /// <param name="num"></param>
 46         /// <returns></returns>
 47         static string GetSpace(int num = 1)
 48         {
 49             var sb = new StringBuilder();
 50 
 51             for (int i = 0; i < num; i++)
 52             {
 53                 sb.Append(space4);
 54             }
 55 
 56             return sb.ToString();
 57         }
 58 
 59         /// <summary>
 60         /// 获取变量名
 61         /// </summary>
 62         /// <param name="str"></param>
 63         /// <returns></returns>
 64         static string GetSuffixStr(string str)
 65         {
 66             return "_" + str.Substring(0, 1).ToLower() + str.Substring(1);
 67         }
 68 
 69         /// <summary>
 70         /// 生成代码头部
 71         /// </summary>
 72         /// <returns></returns>
 73         static string Header(params string[] usings)
 74         {
 75             var sb = new StringBuilder();
 76             sb.AppendLine("/*******");
 77             sb.AppendLine($"*此代码为SAEA.RPCGenerater生成 {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}");
 78             sb.AppendLine("*******/" + Environment.NewLine);
 79             sb.AppendLine("using System;");
 80             if (usings != null)
 81             {
 82                 foreach (var u in usings)
 83                 {
 84                     sb.AppendLine(u);
 85                 }
 86             }
 87             return sb.ToString();
 88         }
 89 
 90         static string _proxyStr;
 91 
 92         static List<string> _serviceStrs = new List<string>();
 93 
 94         static Dictionary<string, string> _modelStrs = new Dictionary<string, string>();
 95 
 96         /// <summary>
 97         /// 生成代理代码
 98         /// </summary>
 99         /// <param name="spaceName"></param>
100         internal static void GenerateProxy(string spaceName)
101         {
102             StringBuilder csStr = new StringBuilder();
103             csStr.AppendLine(Header("using SAEA.RPC.Consumer;", $"using {spaceName}.Consumer.Model;", $"using {spaceName}.Consumer.Service;"));
104             csStr.AppendLine($"namespace {spaceName}.Consumer");
105             csStr.AppendLine("{");
106             csStr.AppendLine($"{GetSpace(1)}public class RPCServiceProxy");
107             csStr.AppendLine(GetSpace(1) + "{");
108 
109             csStr.AppendLine(GetSpace(2) + "ServiceConsumer _serviceConsumer;");
110             csStr.AppendLine(GetSpace(2) + "public RPCServiceProxy(string uri = "rpc://127.0.0.1:39654") : this(new Uri(uri)){}");
111             csStr.AppendLine(GetSpace(2) + "public RPCServiceProxy(Uri uri)");
112             csStr.AppendLine(GetSpace(2) + "{");
113 
114             csStr.AppendLine(GetSpace(3) + "_serviceConsumer = new ServiceConsumer(uri);");
115 
116             var names = RPCMapping.GetServiceNames();
117 
118             if (names != null)
119             {
120                 foreach (var name in names)
121                 {
122                     csStr.AppendLine(GetSpace(3) + GetSuffixStr(name) + $" = new {name}(_serviceConsumer);");
123                 }
124             }
125             csStr.AppendLine(GetSpace(2) + "}");
126 
127             if (names != null)
128             {
129                 foreach (var name in names)
130                 {
131                     var suffixStr = GetSuffixStr(name);
132 
133                     csStr.AppendLine(GetSpace(2) + $"{name} {suffixStr};");
134                     csStr.AppendLine(GetSpace(2) + $"public {name} {name}");
135                     csStr.AppendLine(GetSpace(2) + "{");
136                     csStr.AppendLine($"{GetSpace(3)} get{{ return {suffixStr}; }}");
137                     csStr.AppendLine(GetSpace(2) + "}");
138 
139                     var list = RPCMapping.GetAll(name);
140                     if (list != null)
141                     {
142                         GenerateService(spaceName, name, list);
143                     }
144                 }
145             }
146 
147             csStr.AppendLine(GetSpace(1) + "}");
148             csStr.AppendLine("}");
149             _proxyStr = csStr.ToString();
150         }
151         /// <summary>
152         /// 生成调用服务代码
153         /// </summary>
154         /// <param name="spaceName"></param>
155         /// <param name="serviceName"></param>
156         /// <param name="methods"></param>
157         internal static void GenerateService(string spaceName, string serviceName, Dictionary<string, ServiceInfo> methods)
158         {
159             StringBuilder csStr = new StringBuilder();
160             csStr.AppendLine($"namespace {spaceName}.Consumer.Service");
161             csStr.AppendLine("{");
162             csStr.AppendLine($"{GetSpace(1)}public class {serviceName}");
163             csStr.AppendLine(GetSpace(1) + "{");
164             csStr.AppendLine(GetSpace(2) + "ServiceConsumer _serviceConsumer;");
165             csStr.AppendLine(GetSpace(2) + $"public {serviceName}(ServiceConsumer serviceConsumer)");
166             csStr.AppendLine(GetSpace(2) + "{");
167             csStr.AppendLine(GetSpace(3) + "_serviceConsumer = serviceConsumer;");
168             csStr.AppendLine(GetSpace(2) + "}");
169 
170             foreach (var item in methods)
171             {
172                 var rtype = item.Value.Mothd.ReturnType;
173 
174                 if (rtype != null)
175                 {
176                     if (!_modelStrs.ContainsKey($"{spaceName}.Consumer.Model.{rtype.Name}"))
177                     {
178                         GenerateModel(spaceName, rtype);
179                     }
180                 }
181 
182                 var argsStr = new StringBuilder();
183 
184                 var argsInput = new StringBuilder();
185 
186                 if (item.Value.Pamars != null)
187                 {
188                     int i = 0;
189                     foreach (var arg in item.Value.Pamars)
190                     {
191                         i++;
192                         argsStr.Append(arg.Value.Name);
193                         argsStr.Append(" ");
194                         argsStr.Append(arg.Key);
195                         if (i < item.Value.Pamars.Count)
196                             argsStr.Append(", ");
197 
198                         if (arg.Value != null && arg.Value.IsClass)
199                         {
200                             if (!_modelStrs.ContainsKey($"{spaceName}.Consumer.Model.{arg.Value.Name}"))
201                             {
202                                 GenerateModel(spaceName, arg.Value);
203                             }
204                         }
205 
206                         argsInput.Append(", ");
207                         argsInput.Append(arg.Key);
208                     }
209                 }
210 
211                 csStr.AppendLine(GetSpace(2) + $"public {rtype.Name} {item.Key}({argsStr.ToString()})");
212                 csStr.AppendLine(GetSpace(2) + "{");
213                 csStr.AppendLine(GetSpace(3) + $"return _serviceConsumer.RemoteCall<{rtype.Name}>("{serviceName}", "{item.Key}"{argsInput.ToString()});");
214                 csStr.AppendLine(GetSpace(2) + "}");
215 
216 
217             }
218 
219             csStr.AppendLine(GetSpace(1) + "}");
220             csStr.AppendLine("}");
221             _serviceStrs.Add(csStr.ToString());
222         }
223 
224         /// <summary>
225         /// 生成实体代码
226         /// </summary>
227         /// <typeparam name="T"></typeparam>
228         /// <param name="t"></param>
229         /// <returns></returns>
230         internal static void GenerateModel(string spaceName, Type type)
231         {
232             if (!IsModel(type)) return;
233             StringBuilder csStr = new StringBuilder();
234             csStr.AppendLine($"namespace {spaceName}.Consumer.Model");
235             csStr.AppendLine("{");
236             csStr.AppendLine($"{GetSpace(1)}public class {type.Name}");
237             csStr.AppendLine(GetSpace(1) + "{");
238             var ps = type.GetProperties();
239             foreach (var p in ps)
240             {
241                 csStr.AppendLine($"{GetSpace(2)}public {p.PropertyType.Name} {p.Name}");
242                 csStr.AppendLine(GetSpace(2) + "{");
243                 csStr.AppendLine(GetSpace(3) + "get;set;");
244                 csStr.AppendLine(GetSpace(2) + "}");
245             }
246             csStr.AppendLine(GetSpace(1) + "}");
247             csStr.AppendLine("}");
248             _modelStrs.Add($"{spaceName}.Consumer.Model.{type.Name}", csStr.ToString());
249         }
250 
251         /// <summary>
252         /// 是否是实体
253         /// </summary>
254         /// <param name="type"></param>
255         /// <returns></returns>
256         internal static bool IsModel(Type type)
257         {
258             if (type.IsArray || type.IsSealed || !type.IsClass)
259             {
260                 return false;
261             }
262             return true;
263         }
264 
265         /// <summary>
266         /// 生成客户端C#代码文件
267         /// </summary>
268         /// <param name="folder"></param>
269         /// <param name="spaceName"></param>
270         public static void Generate(string folder, string spaceName)
271         {
272             RPCMapping.RegistAll();
273 
274             GenerateProxy(spaceName);
275 
276             var filePath = Path.Combine(folder, "RPCServiceProxy.cs");
277 
278             StringBuilder sb = new StringBuilder();
279 
280             sb.AppendLine(_proxyStr);
281 
282             if (_serviceStrs != null && _serviceStrs.Count > 0)
283             {
284                 foreach (var serviceStr in _serviceStrs)
285                 {
286                     sb.AppendLine(serviceStr);
287                 }
288             }
289 
290             if (_modelStrs != null && _modelStrs.Count > 0)
291             {
292                 foreach (var entry in _modelStrs)
293                 {
294                     sb.AppendLine(entry.Value);
295                 }
296             }
297 
298             if (File.Exists(filePath))
299                 File.Delete(filePath);
300 
301             File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8);
302         }
303 
304 
305     }
306 }

  无论在服务端根据数据将远程调用反转本地方法、还是生成客户端代码的过程都离不开服务结构的问题。如果是根据结构文件来处理,则先要编写结构文件;服务端码农活不重事不多啊?文档没发你啊?啥锅都往这边甩……此处省略一万字。另外一种方式就是类似web mvc采用约定方式,写完服务业务代码后,再自动生成结构并缓存在内存里。

  1 /****************************************************************************
  2 *Copyright (c) 2018 Microsoft All Rights Reserved.
  3 *CLR版本: 4.0.30319.42000
  4 *机器名称:WENLI-PC
  5 *公司名称:Microsoft
  6 *命名空间:SAEA.RPC.Provider
  7 *文件名: ServiceTable
  8 *版本号: V1.0.0.0
  9 *唯一标识:e95f1d0b-f172-49c7-b75f-67f333504260
 10 *当前的用户域:WENLI-PC
 11 *创建人: yswenli
 12 *电子邮箱:wenguoli_520@qq.com
 13 *创建时间:2018/5/16 17:46:34
 14 *描述:
 15 *
 16 *=====================================================================
 17 *修改标记
 18 *修改时间:2018/5/16 17:46:34
 19 *修改人: yswenli
 20 *版本号: V1.0.0.0
 21 *描述:
 22 *
 23 *****************************************************************************/
 24 using SAEA.Commom;
 25 using SAEA.RPC.Model;
 26 using System;
 27 using System.Collections.Concurrent;
 28 using System.Collections.Generic;
 29 using System.Diagnostics;
 30 using System.Linq;
 31 using System.Reflection;
 32 
 33 namespace SAEA.RPC.Common
 34 {
 35     /// <summary>
 36     /// 服务类缓存表
 37     /// md5+ServiceInfo反射结果
 38     /// </summary>
 39     internal static class RPCMapping
 40     {
 41         static object _locker = new object();
 42 
 43         static HashMap<string, string, ServiceInfo> _serviceMap = new HashMap<string, string, ServiceInfo>();
 44 
 45         /// <summary>
 46         /// 本地注册RPC服务缓存
 47         /// </summary>
 48         public static HashMap<string, string, ServiceInfo> ServiceMap
 49         {
 50             get
 51             {
 52                 return _serviceMap;
 53             }
 54         }
 55 
 56         /// <summary>
 57         /// 本地注册RPC服务
 58         /// </summary>
 59         /// <param name="type"></param>
 60         public static void Regist(Type type)
 61         {
 62             lock (_locker)
 63             {
 64                 var serviceName = type.Name;
 65 
 66                 if (IsRPCService(type))
 67                 {
 68                     var methods = type.GetMethods();
 69 
 70                     var rms = GetRPCMehod(methods);
 71 
 72                     if (rms.Count > 0)
 73                     {
 74                         foreach (var m in rms)
 75                         {
 76                             var serviceInfo = new ServiceInfo()
 77                             {
 78                                 Type = type,
 79                                 Instance = Activator.CreateInstance(type),
 80                                 Mothd = m,
 81                                 Pamars = m.GetParameters().ToDic()
 82                             };
 83 
 84                             List<object> iAttrs = null;
 85 
 86                             //类上面的过滤
 87                             var attrs = type.GetCustomAttributes(true);
 88 
 89                             if (attrs != null && attrs.Length > 0)
 90                             {
 91                                 var classAttrs = attrs.Where(b => b.GetType().BaseType.Name == "ActionFilterAttribute").ToList();
 92 
 93                                 if (classAttrs != null && classAttrs.Count > 0)
 94 
 95                                     iAttrs = classAttrs;
 96 
 97                             }
 98 
 99                             serviceInfo.FilterAtrrs = iAttrs;
100 
101                             //action上面的过滤
102                             var actionAttrs = m.GetCustomAttributes(true);
103 
104                             if (actionAttrs != null)
105                             {
106                                 var filterAttrs = attrs.Where(b => b.GetType().BaseType.Name == "ActionFilterAttribute").ToList();
107 
108                                 if (filterAttrs != null && filterAttrs.Count > 0)
109 
110                                     serviceInfo.ActionFilterAtrrs = filterAttrs;
111                             }
112 
113                             _serviceMap.Set(serviceName, m.Name, serviceInfo);
114                         }
115                     }
116                 }
117             }
118         }
119 
120         /// <summary>
121         /// 本地注册RPC服务
122         /// 若为空,则默认全部注册带有ServiceAttribute的服务
123         /// </summary>
124         /// <param name="types"></param>
125         public static void Regists(params Type[] types)
126         {
127             if (types != null)
128                 foreach (var type in types)
129                 {
130                     Regist(type);
131                 }
132             else
133                 RegistAll();
134         }
135         /// <summary>
136         /// 全部注册带有ServiceAttribute的服务
137         /// </summary>
138         public static void RegistAll()
139         {
140             StackTrace ss = new StackTrace(true);
141             MethodBase mb = ss.GetFrame(2).GetMethod();
142             var space = mb.DeclaringType.Namespace;
143             var tt = mb.DeclaringType.Assembly.GetTypes();
144             Regists(tt);
145         }
146 
147         /// <summary>
148         /// 判断类是否是RPCService
149         /// </summary>
150         /// <param name="type"></param>
151         /// <returns></returns>
152         public static bool IsRPCService(Type type)
153         {
154             var isService = false;
155             var cAttrs = type.GetCustomAttributes(true);
156             if (cAttrs != null)
157             {
158                 foreach (var cAttr in cAttrs)
159                 {
160                     if (cAttr is RPCServiceAttribute)
161                     {
162                         isService = true;
163                         break;
164                     }
165                 }
166             }
167             return isService;
168         }
169 
170         /// <summary>
171         /// 获取RPC方法集合
172         /// </summary>
173         /// <param name="mInfos"></param>
174         /// <returns></returns>
175         public static List<MethodInfo> GetRPCMehod(MethodInfo[] mInfos)
176         {
177             List<MethodInfo> result = new List<MethodInfo>();
178             if (mInfos != null)
179             {
180                 var isRPC = false;
181                 foreach (var method in mInfos)
182                 {
183                     if (method.IsAbstract || method.IsConstructor || method.IsFamily || method.IsPrivate || method.IsStatic || method.IsVirtual)
184                     {
185                         break;
186                     }
187                     
188                     isRPC = true;
189                     var attrs = method.GetCustomAttributes(true);
190                     if (attrs != null)
191                     {
192                         foreach (var attr in attrs)
193                         {
194                             if (attr is NoRpcAttribute)
195                             {
196                                 isRPC = false;
197                                 break;
198                             }
199                         }
200                     }
201                     if (isRPC)
202                     {
203                         result.Add(method);
204                     }
205                 }
206             }
207             return result;
208         }
209 
210         /// <summary>
211         /// 转换成字典
212         /// </summary>
213         /// <param name="parameterInfos"></param>
214         /// <returns></returns>
215         public static Dictionary<string, Type> ToDic(this ParameterInfo[] parameterInfos)
216         {
217             if (parameterInfos == null) return null;
218 
219             Dictionary<string, Type> dic = new Dictionary<string, Type>();
220 
221             foreach (var p in parameterInfos)
222             {
223                 dic.Add(p.Name, p.ParameterType);
224             }
225 
226             return dic;
227         }
228 
229 
230         /// <summary>
231         /// 获取缓存内容
232         /// </summary>
233         /// <param name="serviceName"></param>
234         /// <param name="methodName"></param>
235         /// <returns></returns>
236         public static ServiceInfo Get(string serviceName, string methodName)
237         {
238             lock (_locker)
239             {
240                 return _serviceMap.Get(serviceName, methodName);
241             }
242         }
243 
244         /// <summary>
245         /// 获取缓存内容
246         /// </summary>
247         /// <returns></returns>
248         public static List<string> GetServiceNames()
249         {
250             lock (_locker)
251             {
252                 return _serviceMap.GetHashIDs();
253             }
254         }
255         /// <summary>
256         /// 获取服务的全部信息
257         /// </summary>
258         /// <param name="serviceName"></param>
259         /// <returns></returns>
260         public static Dictionary<string, ServiceInfo> GetAll(string serviceName)
261         {
262             lock (_locker)
263             {
264                 return _serviceMap.GetAll(serviceName);
265             }
266         }
267 
268 
269 
270     }
271 }

2.3理解Models(模型),View(视图),Controllers(控制器)
http://www.asp.net/learn/mvc/tutorial-22-cs.aspx

 1 /****************************************************************************
 2 *Copyright (c) 2018 Microsoft All Rights Reserved.
 3 *CLR版本: 4.0.30319.42000
 4 *机器名称:WENLI-PC
 5 *公司名称:Microsoft
 6 *命名空间:SAEA.WebAPI.Http.Net
 7 *文件名: ServerSocket
 8 *版本号: V1.0.0.0
 9 *唯一标识:ab912b9a-c7ed-44d9-8e48-eef0b6ff86a2
10 *当前的用户域:WENLI-PC
11 *创建人: yswenli
12 *电子邮箱:wenguoli_520@qq.com
13 *创建时间:2018/4/8 17:11:15
14 *描述:
15 *
16 *=====================================================================
17 *修改标记
18 *修改时间:2018/4/8 17:11:15
19 *修改人: yswenli
20 *版本号: V1.0.0.0
21 *描述:
22 *
23 *****************************************************************************/
24 using SAEA.Sockets.Core;
25 using SAEA.Sockets.Interface;
26 using System;
27 using System.Collections.Generic;
28 using System.Net;
29 using System.Text;
30 
31 namespace SAEA.WebAPI.Http.Net
32 {
33     class ServerSocket : BaseServerSocket
34     {
35         public event Action<IUserToken, string> OnRequested;
36 
37         public ServerSocket(int bufferSize = 1024 * 100, int count = 10000) : base(new HContext(), bufferSize, true, count)
38         {
39 
40         }
41 
42         protected override void OnReceiveBytes(IUserToken userToken, byte[] data)
43         {
44             HCoder coder = (HCoder)userToken.Coder;
45 
46             coder.GetRequest(data, (result) =>
47             {
48                 OnRequested?.Invoke(userToken, result);
49             });
50         }
51 
52         public void Reply(IUserToken userToken, byte[] data)
53         {
54             base.Send(userToken, data);
55             base.Disconnected(userToken);
56         }
57     }
58 }

测试

  至此几个关键点都完成了,下面是vs2017的代码结构:

9159.com 1

  SAEA.RPCTest是测试项目,Provider为模拟服务端代码、RPCServiceProxy为生成器根据服务端生成的客户端代码,Program.cs中是使用SAEA.RPC使用、测试代码:

  1 using SAEA.Commom;
  2 using SAEA.RPC.Provider;
  3 using SAEA.RPCTest.Consumer;
  4 //using SAEA.RPCTest.Consumer;
  5 using System;
  6 using System.Diagnostics;
  7 using System.Threading;
  8 using System.Threading.Tasks;
  9 
 10 namespace SAEA.RPCTest
 11 {
 12     class Program
 13     {
 14         static void Main(string[] args)
 15         {
 16             ConsoleHelper.WriteLine($"SAEA.RPC功能测试: {Environment.NewLine}   p 启动rpc provider{Environment.NewLine}   c 启动rpc consumer{Environment.NewLine}   g 启动rpc consumer代码生成器");
 17 
 18             var inputStr = ConsoleHelper.ReadLine();
 19 
 20             if (string.IsNullOrEmpty(inputStr))
 21             {
 22                 inputStr = "p";
 23             }
 24 
 25             if (inputStr == "c")
 26             {
 27                 ConsoleHelper.WriteLine("开始Consumer测试!");
 28                 ConsumerInit();
 29                 ConsoleHelper.WriteLine("回车结束!");
 30                 ConsoleHelper.ReadLine();
 31             }
 32             else if (inputStr == "a")
 33             {
 34                 ProviderInit();
 35                 ConsoleHelper.WriteLine("回车开始Consumer测试!");
 36                 ConsoleHelper.ReadLine();
 37                 ConsumerInit();
 38                 ConsoleHelper.WriteLine("回车结束!");
 39                 ConsoleHelper.ReadLine();
 40             }
 41             else if (inputStr == "g")
 42             {
 43                 ConsoleHelper.WriteLine("正在代码生成中...");
 44                 Generate();
 45                 ConsoleHelper.WriteLine("代码生成完毕,回车结束!");
 46                 ConsoleHelper.ReadLine();
 47             }
 48             else
 49             {
 50                 ProviderInit();
 51                 ConsoleHelper.WriteLine("回车结束!");
 52                 ConsoleHelper.ReadLine();
 53             }
 54         }
 55 
 56 
 57         static void ProviderInit()
 58         {
 59             ConsoleHelper.Title = "SAEA.RPC.Provider";
 60             ConsoleHelper.WriteLine("Provider正在启动HelloService。。。");
 61             var sp = new ServiceProvider(new Type[] { typeof(Provider.HelloService) });
 62             sp.Start();
 63             ConsoleHelper.WriteLine("Provider就绪!");
 64         }
 65 
 66         static void Generate()
 67         {
 68             RPC.Generater.CodeGnerater.Generate(PathHelper.Current, "SAEA.RPCTest");
 69         }
 70 
 71         static void ConsumerInit()
 72         {
 73             ConsoleHelper.Title = "SAEA.RPC.Consumer";
 74 
 75             var url = "rpc://127.0.0.1:39654";
 76 
 77             ConsoleHelper.WriteLine($"Consumer正在连接到{url}...");
 78 
 79             RPCServiceProxy cp = new RPCServiceProxy(url);
 80 
 81             ConsoleHelper.WriteLine("Consumer连接成功");
 82 
 83             ConsoleHelper.WriteLine("HelloService/Hello:" + cp.HelloService.Hello());
 84             ConsoleHelper.WriteLine("HelloService/Plus:" + cp.HelloService.Plus(1, 9));
 85             ConsoleHelper.WriteLine("HelloService/Update/UserName:" + cp.HelloService.Update(new Consumer.Model.UserInfo() { ID = 1, UserName = "yswenli" }).UserName);
 86             ConsoleHelper.WriteLine("HelloService/GetGroupInfo/Creator.UserName:" + cp.HelloService.GetGroupInfo(1).Creator.UserName);
 87             ConsoleHelper.WriteLine("HelloService/SendData:" + System.Text.Encoding.UTF8.GetString(cp.HelloService.SendData(System.Text.Encoding.UTF8.GetBytes("Hello Data"))));
 88             ConsoleHelper.WriteLine("回车启动性能测试!");
 89 
 90             ConsoleHelper.ReadLine();
 91 
 92             #region 性能测试
 93 
 94             Stopwatch sw = new Stopwatch();
 95 
 96             int count = 1000000;
 97 
 98             ConsoleHelper.WriteLine($"{count} 次实体传输调用测试中...");
 99 
100             var ui = new Consumer.Model.UserInfo() { ID = 1, UserName = "yswenli" };
101 
102             sw.Start();
103 
104             for (int i = 0; i < count; i++)
105             {
106                 cp.HelloService.Update(ui);
107             }
108             ConsoleHelper.WriteLine($"实体传输:{count * 1000 / sw.ElapsedMilliseconds} 次/秒");
109 
110             sw.Stop();
111 
112             #endregion
113 
114 
115 
116         }
117     }
118 }

  在命令行中将SAEA.RPCTest发布输入dotnet pulish -r win7-x64后运行exe如下:

9159.com 2

至此一个使用方便、高性能rpc就初步完成了。

转载请标明本文来源:
更多内容欢迎star/fork作者的github:
如果发现本文有什么问题和任何建议,也随时欢迎交流~

 

3.路由
3.1路由概述
http://www.asp.net/learn/mvc/tutorial-05-cs.aspx

  二、Http:这个是个应用协议,本人了解下来至少有3个版本,完全熟悉的话估计没个半年都搞不定;但是只需要关键,比如说http1.1的工作模式、传输格式、常见异常code、常见mime类型、js跨域支持等,这些基本能覆盖绝大部分日常场景,至于更多的那些细枝末节的理它作甚,本人的做法就是用Chrome的开发人员工具来查看相关network详情,这样的话就可以清楚http这个协议的具体编码解码了。

        

3.2创建自定义路由
http://www.asp.net/learn/mvc/tutorial-23-cs.aspx

 1         public void GetRequest(byte[] data, Action<string> onUnpackage)
 2         {
 3             lock (_locker)
 4             {
 5                 var str = Encoding.UTF8.GetString(data);
 6 
 7                 var index = str.IndexOf(ENDSTR);
 8 
 9                 if (index > -1)
10                 {
11                     var s = str.Substring(0, index);
12 
13                     _result.Append(s);
14 
15                     onUnpackage.Invoke(_result.ToString());
16 
17                     _result.Clear();
18 
19                     if (str.Length > index + 4)
20                     {
21                         _result.Append(str.Substring(index + 4));
22                     }
23                 }
24                 else
25                 {
26                     _result.Append(str);
27                 }
28             }
29         }

  “约定优于配置”这是一个相当棒的经验,对于我来说,恐怕是自“面向对象”以来对我最大冲击的理念了吧。首先,SOAP服务可以说是基于配置的,它在HTTP的基础上使用XML配置表达服务与数据,为了实现远程访问,它生成的服务信息尤其复杂,无论是C#客户端还是JAVA客户端,甚至可以通过WSDL服务描述来自动生成一整套代码,这样基于配置的服务的缺点就在于服务端与客户端有着较强的编码耦合性,服务端接口修改后,需要对客户端的服务依赖代码重新生成。而restful服务是基于约定的,是http的get、delete、post还是put都约定好了不同的含义,我们依照约定来进行http请求便可以实现各式的操作。更不用说SOAP服务的性能之差、与restful服务不是一个数量级之缺点了。基于约定,我们可以实现代码间的松耦合。虽然Hessian性能略优于restful服务、其实是一个数量级、restful编解码做好了就性能差不多,但Hessian也还是基于代码的紧耦合了。

3.3创建路由限制
http://www.asp.net/learn/mvc/tutorial-24-cs.aspx

  经过分析后http的内容格式其实就是字符回车分隔,再加上一些约定生成的分隔符bound完成的。

 

3.4创建自定义路由限制
http://www.asp.net/learn/mvc/tutorial-25-cs.aspx

 1         public HttpRequest(Stream stream)
 2         {
 3             this._dataStream = stream;
 4             var data = GetRequestData(_dataStream);
 5             var rows = Regex.Split(data, Environment.NewLine);
 6 
 7             //Request URL & Method & Version
 8             var first = Regex.Split(rows[0], @"(s+)")
 9                 .Where(e => e.Trim() != string.Empty)
10                 .ToArray();
11             if (first.Length > 0) this.Method = first[0];
12             if (first.Length > 1)
13             {
14                 this.Query = first[1];
15 
16                 if (this.Query.Contains("?"))
17                 {
18                     var qarr = this.Query.Split("?");
19                     this.URL = qarr[0];
20                     this.Params = GetRequestParameters(qarr[1]);
21                 }
22                 else
23                 {
24                     this.URL = this.Query;
25                 }
26 
27                 var uarr = this.URL.Split("/");
28 
29                 if (long.TryParse(uarr[uarr.Length - 1], out long id))
30                 {
31                     this.URL = this.URL.Substring(0, this.URL.LastIndexOf("/"));
32                     this.Params.Set("id", id.ToString());
33                 }
34             }
35             if (first.Length > 2) this.Protocols = first[2];
36 
37             //Request Headers
38             this.Headers = GetRequestHeaders(rows);
39 
40             //Request "GET"
41             if (this.Method == "GET")
42             {
43                 this.Body = GetRequestBody(rows);
44             }
45 
46             //Request "POST"
47             if (this.Method == "POST")
48             {
49                 this.Body = GetRequestBody(rows);
50                 var contentType = GetHeader(RequestHeaderType.ContentType);
51                 var isUrlencoded = contentType == @"application/x-www-form-urlencoded";
52                 if (isUrlencoded) this.Params = GetRequestParameters(this.Body);
53             }
54         }

        首先,我们先在IDEA中新建一个名为dp-parent的Project,作为所有工程的父节点,其<packaging>pom</packaging>。然后,我们在dp-parent下新建一个名为dp-restfulservice的Module,其<packaging>war</packaging>,并在其pom.xml中添加其所需的spring依赖。如:

4.控件器
4.1控件器概述
http://www.asp.net/learn/mvc/tutorial-03-cs.aspx

  看到上面,有人肯定会说你这个传文件咋办?一个呢本人这个是针对webapi;另外一个,如真有这个场景,可以用Chrome的开发人员工具来查看相关network详情,也可以使用httpanalyzerstd、httpwatch等众多工具分析下,其实也就是使用了一些约定的分隔符bound完成,每个浏览器还不一样,有兴趣的完全可以自行扩展一个。

 

4.2创建控件器
http://www.asp.net/learn/mvc/tutorial-33-cs.aspx

  三、Reponse这个是webapi服务端相当重要的一个组件,本人也是尽可能方便并且按尽量按asp.net mvc的命名来实现,另外这里加入支持js跨域所需大部分场景heads,如果还有特殊的heads,完全可以自已添加。

<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>3.2.4.RELEASE</version>
</dependency>

4.3创建Action处理方法
http://www.asp.net/learn/mvc/tutorial-34-cs.aspx

9159.com 39159.com 4

        

5.视图
5.1视图概述
http://www.asp.net/learn/mvc/tutorial-04-cs.aspx

  1 /****************************************************************************
  2 *Copyright (c) 2018 Microsoft All Rights Reserved.
  3 *CLR版本: 4.0.30319.42000
  4 *机器名称:WENLI-PC
  5 *公司名称:Microsoft
  6 *命名空间:SAEA.WebAPI.Http
  7 *文件名: HttpResponse
  8 *版本号: V1.0.0.0
  9 *唯一标识:2e43075f-a43d-4b60-bee1-1f9107e2d133
 10 *当前的用户域:WENLI-PC
 11 *创建人: yswenli
 12 *电子邮箱:wenguoli_520@qq.com
 13 *创建时间:2018/4/8 16:46:40
 14 *描述:
 15 *
 16 *=====================================================================
 17 *修改标记
 18 *修改时间:2018/4/8 16:46:40
 19 *修改人: yswenli
 20 *版本号: V1.0.0.0
 21 *描述:
 22 *
 23 *****************************************************************************/
 24 using SAEA.Commom;
 25 using SAEA.Sockets.Interface;
 26 using SAEA.WebAPI.Http.Base;
 27 using SAEA.WebAPI.Mvc;
 28 using System.Collections.Generic;
 29 using System.Net;
 30 using System.Text;
 31 
 32 namespace SAEA.WebAPI.Http
 33 {
 34     public class HttpResponse : BaseHeader
 35     {
 36         public HttpStatusCode Status { get; set; } = HttpStatusCode.OK;
 37 
 38         public byte[] Content { get; private set; }
 39 
 40 
 41 
 42         internal HttpServer HttpServer { get; set; }
 43 
 44         internal IUserToken UserToken { get; set; }
 45         /// <summary>
 46         /// 创建一个HttpRequest实例
 47         /// </summary>
 48         /// <param name="httpServer"></param>
 49         /// <param name="userToken"></param>
 50         /// <param name="stream"></param>
 51         /// <returns></returns>
 52         internal static HttpResponse CreateInstance(HttpServer httpServer, IUserToken userToken)
 53         {
 54             HttpResponse httpResponse = new HttpResponse("");
 55             httpResponse.HttpServer = httpServer;
 56             httpResponse.UserToken = userToken;
 57             return httpResponse;
 58         }
 59 
 60         /// <summary>
 61         /// 设置回复内容
 62         /// </summary>
 63         /// <param name="httpResponse"></param>
 64         /// <param name="result"></param>
 65         internal static void SetResult(HttpResponse httpResponse, ActionResult result)
 66         {
 67             httpResponse.Content_Encoding = result.ContentEncoding.EncodingName;
 68             httpResponse.Content_Type = result.ContentType;
 69             httpResponse.Status = result.Status;
 70 
 71             if (result is EmptyResult)
 72             {
 73                 return;
 74             }
 75 
 76             if (result is FileResult)
 77             {
 78                 var f = result as FileResult;
 79 
 80                 httpResponse.SetContent(f.Content);
 81 
 82                 return;
 83             }
 84 
 85             httpResponse.SetContent(result.Content);
 86         }
 87 
 88 
 89         public HttpResponse(string content) : this(content, "UTF-8", "application/json; charset=utf-8", HttpStatusCode.OK)
 90         {
 91 
 92         }
 93 
 94         public HttpResponse(string content, string encoding, string contentType, HttpStatusCode status)
 95         {
 96             this.Content_Encoding = encoding;
 97             this.Content_Type = contentType;
 98             this.Status = status;
 99             this.SetContent(content);
100         }
101 
102         internal HttpResponse SetContent(byte[] content, Encoding encoding = null)
103         {
104             this.Content = content;
105             this.Encoding = encoding != null ? encoding : Encoding.UTF8;
106             this.Content_Length = content.Length.ToString();
107             return this;
108         }
109 
110         internal HttpResponse SetContent(string content, Encoding encoding = null)
111         {
112             //初始化内容
113             encoding = encoding != null ? encoding : Encoding.UTF8;
114             return SetContent(encoding.GetBytes(content), encoding);
115         }
116 
117 
118         public string GetHeader(ResponseHeaderType header)
119         {
120             return base.GetHeader(header);
121         }
122 
123         public void SetHeader(ResponseHeaderType header, string value)
124         {
125             base.SetHeader(header, value);
126         }
127 
128         /// <summary>
129         /// 构建响应头部
130         /// </summary>
131         /// <returns></returns>
132         protected string BuildHeader()
133         {
134             StringBuilder builder = new StringBuilder();
135             builder.Append(Protocols + SPACE + Status.ToNVString() + ENTER);
136             builder.AppendLine("Server: Wenli's Server");
137             builder.AppendLine("Keep-Alive: timeout=20");
138             builder.AppendLine("Date: " + DateTimeHelper.Now.ToFString("r"));
139 
140             if (!string.IsNullOrEmpty(this.Content_Type))
141                 builder.AppendLine("Content-Type:" + this.Content_Type);
142 
143             //支持跨域
144             builder.AppendLine("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
145             builder.AppendLine("Access-Control-Allow-Origin: *");
146             builder.AppendLine("Access-Control-Allow-Headers: Content-Type,X-Requested-With,Accept,yswenli");//可自行增加额外的header
147             builder.AppendLine("Access-Control-Request-Methods: GET, POST, PUT, DELETE, OPTIONS");
148 
149             if (this.Headers != null && this.Headers.Count > 0)
150             {
151                 foreach (var key in Headers.Names)
152                 {
153                     builder.AppendLine($"{key}: {Headers[key]}");
154                 }
155             }
156 
157             return builder.ToString();
158         }
159 
160         /// <summary>
161         /// 生成数据
162         /// </summary>
163         private byte[] ToBytes()
164         {
165             List<byte> list = new List<byte>();
166             //发送响应头
167             var header = BuildHeader();
168             byte[] headerBytes = this.Encoding.GetBytes(header);
169             list.AddRange(headerBytes);
170 
171             //发送空行
172             byte[] lineBytes = this.Encoding.GetBytes(System.Environment.NewLine);
173             list.AddRange(lineBytes);
174 
175             //发送内容
176             list.AddRange(Content);
177 
178             return list.ToArray();
179         }
180 
181 
182         public void Write(string str)
183         {
184             SetContent(str);
185         }
186 
187         public void BinaryWrite(byte[] data)
188         {
189             SetContent(data);
190         }
191 
192         public void Clear()
193         {
194             this.Write("");
195         }
196 
197         public void End()
198         {
199             HttpServer.Replay(UserToken, this.ToBytes());
200             HttpServer.Close(UserToken);
201         }
202 
203 
204 
205     }
206 }

        其次,我们在src/main目录下创建名为webapp的目录,为什么要叫这个名字呢?这其实也是一个约定。并且,我们在webapp目录下创建名为WEB-INF的目录(为什么要叫这个名字呢?这其实也是一个约定)。在WEB-INF目录下创建名为web.xml的文件(为什么要叫这个名字呢?这其实也是一个约定,这句话我说了太多遍了,“约定优于配置”此理念真是百试不爽)。下面是web.xml的主要内容。

5.2创建自定义的Html Helpers工具
http://www.asp.net/learn/mvc/tutorial-09-cs.aspx

View Code

 

5.3用表格来显示数据
http://www.asp.net/learn/mvc/tutorial-11-cs.aspx

  四、HttpServer:这个就是承载webapi的容器;有人说不是有IIS和Apache么?本人想说的是:有self-host方便么?有无需安装,无需配置、随便高性能开跑好么?asp.net core里面都有了这个,没这个就没有逼格....(此处省略一万字),前面还研究tcp、http这个当然不能少了

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns=""
         xmlns:xsi=""
         xsi:schemaLocation=" "
         version="3.1">
    <servlet>
        <servlet-name>restful</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>restful</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

5.4使用TagBuilder扩展Html Helpers
http://www.asp.net/learn/mvc/tutorial-35-cs.aspx

 1 /****************************************************************************
 2 *Copyright (c) 2018 Microsoft All Rights Reserved.
 3 *CLR版本: 4.0.30319.42000
 4 *机器名称:WENLI-PC
 5 *公司名称:Microsoft
 6 *命名空间:SAEA.WebAPI.Http
 7 *文件名: HttpServer
 8 *版本号: V1.0.0.0
 9 *唯一标识:914acb72-d4c4-4fa1-8e80-ce2f83bd06f0
10 *当前的用户域:WENLI-PC
11 *创建人: yswenli
12 *电子邮箱:wenguoli_520@qq.com
13 *创建时间:2018/4/10 13:51:50
14 *描述:
15 *
16 *=====================================================================
17 *修改标记
18 *修改时间:2018/4/10 13:51:50
19 *修改人: yswenli
20 *版本号: V1.0.0.0
21 *描述:
22 *
23 *****************************************************************************/
24 using SAEA.Sockets.Interface;
25 using SAEA.WebAPI.Common;
26 using SAEA.WebAPI.Http.Net;
27 using System;
28 using System.Collections.Generic;
29 using System.IO;
30 using System.Text;
31 
32 namespace SAEA.WebAPI.Http
33 {
34     class HttpServer
35     {
36         ServerSocket _serverSocket;
37 
38         public HttpServer()
39         {
40             _serverSocket = new ServerSocket();
41             _serverSocket.OnRequested += _serverSocket_OnRequested;
42         }
43 
44         public void Start(int port = 39654)
45         {
46             _serverSocket.Start(port);
47         }
48 
49 
50         private void _serverSocket_OnRequested(IUserToken userToken, string htmlStr)
51         {
52             var httpContext = HttpContext.CreateInstance(this, userToken, htmlStr);
53 
54             var response = httpContext.Response;
55 
56             response.End();
57         }
58 
59         internal void Replay(IUserToken userToken, byte[] data)
60         {
61             _serverSocket.Reply(userToken, data);
62         }
63 
64         internal void Close(IUserToken userToken)
65         {
66             _serverSocket.Disconnected(userToken);
67         }
68 
69 
70     }
71 }

 

6.模型
6.1利用实体框架(Entity Framework)创建模型
http://www.asp.net/learn/mvc/tutorial-16-cs.aspx

   五、Controller:为了实现类似于mvc的效果Controller这个大名鼎鼎的当然不能少了,其在C#中使用非常少量的代码即可实现

        然后,我们要在WEB-INF目录下创建一个名为restful-servlet.xml的文件(为什么要叫这个名字呢?这其实也是一个约定,在web.xml中规定的servlet-name加上-servlet约定为servlet配置文件名)。restful-servlet.xml文件的主要内容如下。

6.2利用Linq to Sql创建模型
http://www.asp.net/learn/mvc/tutorial-10-cs.aspx

 1 /****************************************************************************
 2 *Copyright (c) 2018 Microsoft All Rights Reserved.
 3 *CLR版本: 4.0.30319.42000
 4 *机器名称:WENLI-PC
 5 *公司名称:Microsoft
 6 *命名空间:SAEA.WebAPI.Mvc
 7 *文件名: Controller
 8 *版本号: V1.0.0.0
 9 *唯一标识:a303db7d-f83c-4c49-9804-032ec2236232
10 *当前的用户域:WENLI-PC
11 *创建人: yswenli
12 *电子邮箱:wenguoli_520@qq.com
13 *创建时间:2018/4/10 13:58:08
14 *描述:
15 *
16 *=====================================================================
17 *修改标记
18 *修改时间:2018/4/10 13:58:08
19 *修改人: yswenli
20 *版本号: V1.0.0.0
21 *描述:
22 *
23 *****************************************************************************/
24 
25 using SAEA.WebAPI.Http;
26 
27 namespace SAEA.WebAPI.Mvc
28 {
29     /// <summary>
30     /// WebApi控制器
31     /// </summary>
32     public abstract class Controller
33     {
34         public HttpContext HttpContext { get; set; }
35 
36         /// <summary>
37         /// 返回Json
38         /// </summary>
39         /// <param name="data"></param>
40         /// <returns></returns>
41         protected JsonResult Json(object data)
42         {
43             return new JsonResult(data);
44         }
45         /// <summary>
46         /// 自定义内容
47         /// </summary>
48         /// <param name="data"></param>
49         /// <returns></returns>
50         protected ContentResult Content(string data)
51         {
52             return new ContentResult(data);
53         }
54 
55 
56         /// <summary>
57         /// 小文件
58         /// </summary>
59         /// <param name="filePath"></param>
60         /// <returns></returns>
61         protected FileResult File(string filePath)
62         {
63             return new FileResult(filePath);
64         }
65 
66         /// <summary>
67         /// 空结果
68         /// </summary>
69         /// <returns></returns>
70         protected EmptyResult Empty()
71         {
72             return new EmptyResult();
73         }
74     }
75 }

 

7.MVC中的输入验证
7.1简单的数据验证
http://www.asp.net/learn/mvc/tutorial-36-cs.aspx

  六、ActionResult:是mvc里面针对reponse结果进行了一个http格式的封装,本人主要实现了ContentResult、JsonResult、FileResult三个,至于其他的在WebAPI里基本上用不到。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=""
       xmlns:xsi=""
       xmlns:context=""
       xmlns:mvc=""
       xsi:schemaLocation=" ;
    <context:component-scan base-package="com.jsc.dp.controllers"></context:component-scan>
    <mvc:resources mapping="/resources/**" location="/WEB-INF/resources/"></mvc:resources>
    <mvc:annotation-driven/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="suffix" value=".jsp"/>
        <!-- 如果 .jsp 文件中含有 jstl,需要这一行 -->
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
    </bean>
</beans>

7.2利用IDataErrorInfo接口实现验证
9159.com ,http://www.asp.net/learn/mvc/tutorial-37-cs.aspx

 1 /****************************************************************************
 2 *Copyright (c) 2018 Microsoft All Rights Reserved.
 3 *CLR版本: 4.0.30319.42000
 4 *机器名称:WENLI-PC
 5 *公司名称:Microsoft
 6 *命名空间:SAEA.WebAPI.Mvc
 7 *文件名: JsonResult
 8 *版本号: V1.0.0.0
 9 *唯一标识:340c3ef0-2e98-4f25-998f-2bb369fa2794
10 *当前的用户域:WENLI-PC
11 *创建人: yswenli
12 *电子邮箱:wenguoli_520@qq.com
13 *创建时间:2018/4/10 16:48:06
14 *描述:
15 *
16 *=====================================================================
17 *修改标记
18 *修改时间:2018/4/10 16:48:06
19 *修改人: yswenli
20 *版本号: V1.0.0.0
21 *描述:
22 *
23 *****************************************************************************/
24 using SAEA.WebAPI.Common;
25 using System;
26 using System.Collections.Generic;
27 using System.Net;
28 using System.Text;
29 
30 namespace SAEA.WebAPI.Mvc
31 {
32     public class JsonResult : ActionResult
33     {
34         public JsonResult(object model) : this(SerializeHelper.Serialize(model))
35         {
36 
37         }
38         public JsonResult(string json) : this(json, Encoding.UTF8)
39         {
40 
41         }
42 
43         public JsonResult(string json, HttpStatusCode status)
44         {
45             this.Content = json;
46             this.ContentEncoding = Encoding.UTF8;
47             this.ContentType = "application/json; charset=utf-8";
48             this.Status = status;
49         }
50 
51         public JsonResult(string json, Encoding encoding, string contentType = "application/json; charset=utf-8")
52         {
53             this.Content = json;
54             this.ContentEncoding = encoding;
55             this.ContentType = contentType;
56         }
57     }
58 }

7.3如何将验证逻辑封装到一个单独的层
http://www.asp.net/learn/mvc/tutorial-38-cs.aspx

  七、RouteTable:MVC里面有一个相当重要的概念叫约定优先,即为Controller、Action的名称是按某种规则来写编码的,其中将URL与自定义Controller对应起来的缓存映射就是RouteTable,并且作为缓存,也能极大的提升访问性能。当然这里并没有严格按照asp.net mvc里面的routetable来设计,而是根据只是实现webapi,并使用缓存反射结构能来实现的,并且只有约定,没有配置。

        下面是要编写代码spring mvc中的C(Controller)了。在main/java中创建名为com.jac.dp.controllers的包,在此包下新建一个名为AbcController的类,其通过@Controller注解注入进spring框架中。具体的restful代码如下图所示。从图中可以看到,其实现了一个名为getAbcs的方法,使用get用以对get式的 http://localhost:8088/restfulservice/abcs  请求进行响应。还实现了一个名为getAbc的方法,使用get以对get式的 http://localhost:8088/restfulservice/abcs/3  请求进行响应,来获取某一对象,返回给前端的是一Json字符串:{"myInt":3,"myString":"myString"}。名为putAbc的方法,其使用put以对put式的 http://localhost:8088/restfulservice/abcs/3  请求进行响应,来更新某一对象。下图中也展示了,使用工具REST Client来进行http put请求测试的过程与结果,在这里,返回的是一个对象转成的Json字符串,在IDEA中可以看到其Response是:{"myInt":-1,"myString":"post true"},与预想中的一样;同样的post和get也可以如此测试,测试get请求也可以直接使用浏览器执行。 

8.母版页
8.l利用母版页创建布局
http://www.asp.net/learn/mvc/tutorial-12-cs.aspx

  1 /****************************************************************************
  2 *Copyright (c) 2018 Microsoft All Rights Reserved.
  3 *CLR版本: 4.0.30319.42000
  4 *机器名称:WENLI-PC
  5 *公司名称:Microsoft
  6 *命名空间:SAEA.WebAPI.Mvc
  7 *文件名: RouteTable
  8 *版本号: V1.0.0.0
  9 *唯一标识:1ed5d381-d7ce-4ea3-b8b5-c32f581ad49f
 10 *当前的用户域:WENLI-PC
 11 *创建人: yswenli
 12 *电子邮箱:wenguoli_520@qq.com
 13 *创建时间:2018/4/12 10:55:31
 14 *描述:
 15 *
 16 *=====================================================================
 17 *修改标记
 18 *修改时间:2018/4/12 10:55:31
 19 *修改人: yswenli
 20 *版本号: V1.0.0.0
 21 *描述:
 22 *
 23 *****************************************************************************/
 24 using System;
 25 using System.Collections.Generic;
 26 using System.Linq;
 27 using System.Reflection;
 28 using System.Text;
 29 
 30 namespace SAEA.WebAPI.Mvc
 31 {
 32     /// <summary>
 33     /// SAEA.WebAPI路由表
 34     /// </summary>
 35     public static class RouteTable
 36     {
 37         static object _locker = new object();
 38 
 39         static List<Routing> _list = new List<Routing>();
 40 
 41 
 42         /// <summary>
 43         /// 获取routing中的缓存
 44         /// 若不存在则创建
 45         /// </summary>
 46         /// <param name="controllerType"></param>
 47         /// <param name="controllerName"></param>
 48         /// <param name="actionName"></param>
 49         /// <param name="isPost"></param>
 50         /// <returns></returns>
 51         public static Routing TryGet(Type controllerType, string controllerName, string actionName, bool isPost)
 52         {
 53             lock (_locker)
 54             {
 55                 var list = _list.Where(b => b.ControllerName.ToLower() == controllerName.ToLower() && b.ActionName.ToLower() == actionName.ToLower() && b.IsPost == isPost).ToList();
 56 
 57                 if (list == null || list.Count == 0)
 58                 {
 59                     var routing = new Routing()
 60                     {
 61                         ControllerName = controllerName,
 62                         ActionName = actionName,
 63                         IsPost = isPost
 64                     };
 65 
 66                     var actions = controllerType.GetMethods().Where(b => b.Name.ToLower() == actionName.ToLower()).ToList();
 67 
 68                     if (actions == null || actions.Count == 0)
 69                     {
 70                         throw new Exception($"{controllerName}/{actionName}找不到此action!");
 71                     }
 72                     else if (actions.Count > 2)
 73                     {
 74                         throw new Exception($"{controllerName}/{actionName}有多个重复的的action!");
 75                     }
 76                     else
 77                     {                        
 78                         routing.Instance = System.Activator.CreateInstance(controllerType);
 79 
 80                         //类上面的过滤
 81                         var attrs = controllerType.GetCustomAttributes(true);
 82 
 83                         if (attrs != null)
 84                         {
 85                             var attr = attrs.Where(b => b.GetType().BaseType.Name == "ActionFilterAttribute").FirstOrDefault();
 86 
 87                             routing.Atrr = attr;
 88 
 89                         }
 90                         else
 91                         {
 92                             routing.Atrr = null;
 93                         }
 94 
 95                         routing.Action = actions[0];
 96 
 97                         //action上面的过滤
 98                         if (routing.Atrr == null)
 99                         {
100                             attrs = actions[0].GetCustomAttributes(true);
101 
102                             if (attrs != null)
103                             {
104                                 var attr = attrs.Where(b => b.GetType().BaseType.Name == "ActionFilterAttribute").FirstOrDefault();
105 
106                                 routing.Atrr = attr;
107 
108                             }
109                             else
110                             {
111                                 routing.Atrr = null;
112                             }
113                         }
114                     }
115                     _list.Add(routing);
116                     return routing;
117                 }
118                 else if (list.Count > 1)
119                 {
120                     throw new Exception("500");
121                 }
122                 return list.FirstOrDefault();
123             }
124         }
125     }
126 
127 }

8.2如何传送数据给母版页
http://www.asp.net/learn/mvc/tutorial-13-cs.aspx

  在MVC的思想里面ActionFilterAtrribute的这个AOP设计也一直伴随左右,比如记日志、黑名单、权限、验证、限流等等功能,所以路由的时候也会缓存这个。至此一些关键性的地方都已经弄的差不多了,为了更好的了解上面说的这些,下面是vs2017中项目的结构截图:

9159.com 5

9.Action过滤器和Model绑定
9.1理解Action过滤器
http://www.asp.net/learn/mvc/tutorial-13-cs.aspx

9159.com 6

 

10.利用缓存提高MVC性能

  纯粹干净单码,无任何晦涩内容,如果对mvc有一定了解的,这个差不多可以NoNotes,接下来就是按asp.net mvc命名方式,写个测试webapi看看情况,首先还是测试项目结构图:

 

10.1利用输出缓存提高性能
http://www.asp.net/learn/mvc/tutorial-15-cs.aspx

9159.com 7  

        代码编写完成后,需要进行服务发布了,我们使用Tomcat作为服务容器。在IDEA中如何进行Tomcat配置就不多说了,这里提一下Run/Debug Configurations。在Application context中配置成restfulservice才有上面的效果哦。

10.2缓存页上如何更新动态内容
http://www.asp.net/learn/mvc/tutorial-19-cs.aspx

  HomeController里面按asp.net mvc的习惯来编写代码:

11.MVC安全性
11.1使用Form方式验证
http://www.asp.net/learn/mvc/tutorial-17-cs.aspx
11.2使用Windows方式验证
http://www.asp.net/learn/mvc/tutorial-18-cs.aspx
11.3防止JavaScript注入
http://www.asp.net/learn/mvc/tutorial-06-cs.aspx

  1 /****************************************************************************
  2 *Copyright (c) 2018 Microsoft All Rights Reserved.
  3 *CLR版本: 4.0.30319.42000
  4 *机器名称:WENLI-PC
  5 *公司名称:Microsoft
  6 *命名空间:SAEA.WebAPITest.Controllers
  7 *文件名: HomeController
  8 *版本号: V1.0.0.0
  9 *唯一标识:e00bb57f-e3ee-4efe-a7cf-f23db767c1d0
 10 *当前的用户域:WENLI-PC
 11 *创建人: yswenli
 12 *电子邮箱:wenguoli_520@qq.com
 13 *创建时间:2018/4/10 16:43:26
 14 *描述:
 15 *
 16 *=====================================================================
 17 *修改标记
 18 *修改时间:2018/4/10 16:43:26
 19 *修改人: yswenli
 20 *版本号: V1.0.0.0
 21 *描述:
 22 *
 23 *****************************************************************************/
 24 using SAEA.WebAPI.Mvc;
 25 using SAEA.WebAPITest.Attrubutes;
 26 using SAEA.WebAPITest.Model;
 27 
 28 namespace SAEA.WebAPITest.Controllers
 29 {
 30     /// <summary>
 31     /// 测试实例代码
 32     /// </summary>
 33     //[LogAtrribute]
 34     public class HomeController : Controller
 35     {
 36         /// <summary>
 37         /// 日志拦截
 38         /// 内容输出
 39         /// </summary>
 40         /// <returns></returns>
 41         //[Log2Atrribute]
 42         public ActionResult Index()
 43         {
 44             return Content("Hello,I'm SAEA.WebAPI!");
 45         }
 46         /// <summary>
 47         /// 支持基本类型参数
 48         /// json序列化
 49         /// </summary>
 50         /// <param name="id"></param>
 51         /// <returns></returns>
 52         public ActionResult Get(int id)
 53         {
 54             return Json(new { Name = "yswenli", Sex = "男" });
 55         }
 56         /// <summary>
 57         /// 底层对象调用
 58         /// </summary>
 59         /// <returns></returns>
 60         public ActionResult Show()
 61         {
 62             var response = HttpContext.Response;
 63 
 64             response.Content_Type = "text/html; charset=utf-8";
 65 
 66             response.Write("<h3>测试一下那个response对象使用情况!</h3>参考消息网4月12日报道外媒称,法国一架“幻影-2000”战机意外地对本国一家工厂投下了...");
 67 
 68             response.End();
 69 
 70             return Empty();
 71         }
 72 
 73         [HttpGet]
 74         public ActionResult Update(int id)
 75         {
 76             return Content($"HttpGet Update id:{id}");
 77         }
 78         /// <summary>
 79         /// 基本类型参数、实体混合填充
 80         /// </summary>
 81         /// <param name="isFemale"></param>
 82         /// <param name="userInfo"></param>
 83         /// <returns></returns>
 84         [HttpPost]
 85         public ActionResult Update(bool isFemale, UserInfo userInfo = null)
 86         {
 87             return Json(userInfo);
 88         }
 89         [HttpPost]
 90         public ActionResult Test()
 91         {
 92             return Content("httppost test");
 93         }
 94         /// <summary>
 95         /// 文件输出
 96         /// </summary>
 97         /// <returns></returns>
 98         public ActionResult Download()
 99         {
100             return File(HttpContext.Server.MapPath("/Content/Image/c984b2fb80aeca7b15eda8c004f2e0d4.jpg"));
101         }
102     }
103 }

9159.com 8

12.MVC如何测试
12.1创建MVC的单元测试
http://www.asp.net/learn/mvc/tutorial-07-cs.aspx

 

 

13.导航
13.1利用SiteMaps实现站点导航
http://www.asp.net/learn/mvc/tutorial-20-cs.aspx

  增加一个LogAtrribute打印一些内容:

 

14.MVC部署
14.1如何在不同版本的IIS上部署MVC应用
http://www.asp.net/learn/mvc/tutorial-08-cs.aspx

 1 /****************************************************************************
 2 *Copyright (c) 2018 Microsoft All Rights Reserved.
 3 *CLR版本: 4.0.30319.42000
 4 *机器名称:WENLI-PC
 5 *公司名称:Microsoft
 6 *命名空间:SAEA.WebAPITest.Common
 7 *文件名: LogAtrribute
 8 *版本号: V1.0.0.0
 9 *唯一标识:2a261731-b8f6-47de-b2e4-aecf2e0e0c0f
10 *当前的用户域:WENLI-PC
11 *创建人: yswenli
12 *电子邮箱:wenguoli_520@qq.com
13 *创建时间:2018/4/11 13:46:42
14 *描述:
15 *
16 *=====================================================================
17 *修改标记
18 *修改时间:2018/4/11 13:46:42
19 *修改人: yswenli
20 *版本号: V1.0.0.0
21 *描述:
22 *
23 *****************************************************************************/
24 using SAEA.Commom;
25 using SAEA.WebAPI.Http;
26 using SAEA.WebAPI.Mvc;
27 
28 namespace SAEA.WebAPITest.Attrubutes
29 {
30     public class LogAtrribute : ActionFilterAttribute
31     {
32         /// <summary>
33         /// 执行前
34         /// </summary>
35         /// <param name="httpContext"></param>
36         /// <returns>返回值true为继续,false为终止</returns>
37         public override bool OnActionExecuting(HttpContext httpContext)
38         {
39             return true;
40         }
41 
42         /// <summary>
43         /// 执行后
44         /// </summary>
45         /// <param name="httpContext"></param>
46         /// <param name="result"></param>
47         public override void OnActionExecuted(HttpContext httpContext, ActionResult result)
48         {
49             ConsoleHelper.WriteLine($"请求地址:{httpContext.Request.Query},回复内容:{result.Content}");
50         }
51     }
52 }

       

15.综合练习--联系人信息管理系统
15.1创建基本CRUD应用
http://www.asp.net/learn/mvc/tutorial-26-cs.aspx

  program.cs Main中启动一下服务:

  有时候一个理念让人茅塞顿开、醍醐灌顶。“约定优于配置”就是这样的理念。感谢教给我这个理念的人,王某连云港人。9159.com 9 

15.2如何让系统变得更好看
http://www.asp.net/learn/mvc/tutorial-27-cs.aspx

1 MvcApplication mvcApplication = new MvcApplication();
2 
3 mvcApplication.Start();

 

15.3添加表单验证
http://www.asp.net/learn/mvc/tutorial-28-cs.aspx

  最后F5跑起来看看效果:

来自王安琪

15.4让系统实现松耦合
http://www.asp.net/learn/mvc/tutorial-29-cs.aspx

9159.com 10

15.5创建单元测试
http://www.asp.net/learn/mvc/tutorial-30-cs.aspx

  使用Apache ab.exe压测一下性能如何:

15.6测试驱动开发(TDD)
http://www.asp.net/learn/mvc/tutorial-31-cs.aspx

9159.com 11

15.6添加Ajax功能
http://www.asp.net/learn/mvc/tutorial-32-cs.aspx

 

  至此,一个简洁、高效的WebApi就初步完成了!

 

 

转载请标明本文来源:
更多内容欢迎star作者的github:
如果发现本文有什么问题和任何建议,也随时欢迎交流~

 

本文由9159.com发布于编程,转载请注明出处:restful服务性能好、基于约定、松耦合,完全可以

关键词:

上一篇:没有了
下一篇:没有了