• 自行实现高性能MVC


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

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

      设计的编码思路就是仿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等,下面就一一来解决这个问题。

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

     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 }

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

     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         }

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

     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         }

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

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

      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 }
    View Code

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

     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 }

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

     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 }

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

     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 }

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

      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 }

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

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

      

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

      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 }

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

     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 }

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

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

      最后F5跑起来看看效果:

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

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

    转载请标明本文来源:http://www.cnblogs.com/yswenli/p/8858669.html
    更多内容欢迎star作者的github:https://github.com/yswenli/SAEA
    如果发现本文有什么问题和任何建议,也随时欢迎交流~

  • 相关阅读:
    Pikachu漏洞平台---XSS(Cross-Site Scripting) 跨站脚本
    Pikachu漏洞练习平台(暴力破解模块)
    DVWA-- 不安全得验证码Insecure CAPTCHA
    DVWA-文件上传 File Upload
    DVWA之CSRF(跨站请求伪造)
    绑定方法与非绑定方法
    XML,面向对象基础
    configparser ,subprocess , xlrd ,xlwt 模块
    logging ,re 模块
    random,json,pickle,hashlib,shutil,hmac,shelve 模块
  • 原文地址:https://www.cnblogs.com/yswenli/p/8858669.html
Copyright © 2020-2023  润新知