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 }
四、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
如果发现本文有什么问题和任何建议,也随时欢迎交流~