自行实现高性能MVC WebAPI
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的框架,此时正好也可以用上
/****************************************************************************
*Copyright (c) 2018 Microsoft All Rights Reserved.
*CLR版本: 4.0.30319.42000
*机器名称:WENLI-PC
*公司名称:Microsoft
*命名空间:SAEA.WebAPI.Http.Net
*文件名: ServerSocket
*版本号: V1.0.0.0
*唯一标识:ab912b9a-c7ed-44d9-8e48-eef0b6ff86a2
*当前的用户域:WENLI-PC
*创建人: yswenli
*电子邮箱:wenguoli_520@qq.com
*创建时间:2018/4/8 17:11:15
*描述:
*
*=====================================================================
*修改标记
*修改时间:2018/4/8 17:11:15
*修改人: yswenli
*版本号: V1.0.0.0
*描述:
*
*****************************************************************************/
using SAEA.Sockets.Core;
using SAEA.Sockets.Interface;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text; namespace SAEA.WebAPI.Http.Net
{
class ServerSocket : BaseServerSocket
{
public event Action<IUserToken, string> OnRequested; public ServerSocket(int bufferSize = * , int count = ) : base(new HContext(), bufferSize, true, count)
{ } protected override void OnReceiveBytes(IUserToken userToken, byte[] data)
{
HCoder coder = (HCoder)userToken.Coder; coder.GetRequest(data, (result) =>
{
OnRequested?.Invoke(userToken, result);
});
} public void Reply(IUserToken userToken, byte[] data)
{
base.Send(userToken, data);
base.Disconnected(userToken);
}
}
}
二、Http:这个是个应用协议,本人了解下来至少有3个版本,完全熟悉的话估计没个半年都搞不定;但是只需要关键,比如说http1.1的工作模式、传输格式、常见异常code、常见mime类型、js跨域支持等,这些基本能覆盖绝大部分日常场景,至于更多的那些细枝末节的理它作甚,本人的做法就是用Chrome的开发人员工具来查看相关network详情,这样的话就可以清楚http这个协议的具体编码解码了。
public void GetRequest(byte[] data, Action<string> onUnpackage)
{
lock (_locker)
{
var str = Encoding.UTF8.GetString(data); var index = str.IndexOf(ENDSTR); if (index > -)
{
var s = str.Substring(, index); _result.Append(s); onUnpackage.Invoke(_result.ToString()); _result.Clear(); if (str.Length > index + )
{
_result.Append(str.Substring(index + ));
}
}
else
{
_result.Append(str);
}
}
}
经过分析后http的内容格式其实就是字符回车分隔,再加上一些约定生成的分隔符bound完成的。
public HttpRequest(Stream stream)
{
this._dataStream = stream;
var data = GetRequestData(_dataStream);
var rows = Regex.Split(data, Environment.NewLine); //Request URL & Method & Version
var first = Regex.Split(rows[], @"(\s+)")
.Where(e => e.Trim() != string.Empty)
.ToArray();
if (first.Length > ) this.Method = first[];
if (first.Length > )
{
this.Query = first[]; if (this.Query.Contains("?"))
{
var qarr = this.Query.Split("?");
this.URL = qarr[];
this.Params = GetRequestParameters(qarr[]);
}
else
{
this.URL = this.Query;
} var uarr = this.URL.Split("/"); if (long.TryParse(uarr[uarr.Length - ], out long id))
{
this.URL = this.URL.Substring(, this.URL.LastIndexOf("/"));
this.Params.Set("id", id.ToString());
}
}
if (first.Length > ) this.Protocols = first[]; //Request Headers
this.Headers = GetRequestHeaders(rows); //Request "GET"
if (this.Method == "GET")
{
this.Body = GetRequestBody(rows);
} //Request "POST"
if (this.Method == "POST")
{
this.Body = GetRequestBody(rows);
var contentType = GetHeader(RequestHeaderType.ContentType);
var isUrlencoded = contentType == @"application/x-www-form-urlencoded";
if (isUrlencoded) this.Params = GetRequestParameters(this.Body);
}
}
看到上面,有人肯定会说你这个传文件咋办?一个呢本人这个是针对webapi;另外一个,如真有这个场景,可以用Chrome的开发人员工具来查看相关network详情,也可以使用httpanalyzerstd、httpwatch等众多工具分析下,其实也就是使用了一些约定的分隔符bound完成,每个浏览器还不一样,有兴趣的完全可以自行扩展一个。
三、Reponse这个是webapi服务端相当重要的一个组件,本人也是尽可能方便并且按尽量按asp.net mvc的命名来实现,另外这里加入支持js跨域所需大部分场景heads,如果还有特殊的heads,完全可以自已添加。
/****************************************************************************
*Copyright (c) 2018 Microsoft All Rights Reserved.
*CLR版本: 4.0.30319.42000
*机器名称:WENLI-PC
*公司名称:Microsoft
*命名空间:SAEA.WebAPI.Http
*文件名: HttpResponse
*版本号: V1.0.0.0
*唯一标识:2e43075f-a43d-4b60-bee1-1f9107e2d133
*当前的用户域:WENLI-PC
*创建人: yswenli
*电子邮箱:wenguoli_520@qq.com
*创建时间:2018/4/8 16:46:40
*描述:
*
*=====================================================================
*修改标记
*修改时间:2018/4/8 16:46:40
*修改人: yswenli
*版本号: V1.0.0.0
*描述:
*
*****************************************************************************/
using SAEA.Commom;
using SAEA.Sockets.Interface;
using SAEA.WebAPI.Http.Base;
using SAEA.WebAPI.Mvc;
using System.Collections.Generic;
using System.Net;
using System.Text; namespace SAEA.WebAPI.Http
{
public class HttpResponse : BaseHeader
{
public HttpStatusCode Status { get; set; } = HttpStatusCode.OK; public byte[] Content { get; private set; } internal HttpServer HttpServer { get; set; } internal IUserToken UserToken { get; set; }
/// <summary>
/// 创建一个HttpRequest实例
/// </summary>
/// <param name="httpServer"></param>
/// <param name="userToken"></param>
/// <param name="stream"></param>
/// <returns></returns>
internal static HttpResponse CreateInstance(HttpServer httpServer, IUserToken userToken)
{
HttpResponse httpResponse = new HttpResponse("");
httpResponse.HttpServer = httpServer;
httpResponse.UserToken = userToken;
return httpResponse;
} /// <summary>
/// 设置回复内容
/// </summary>
/// <param name="httpResponse"></param>
/// <param name="result"></param>
internal static void SetResult(HttpResponse httpResponse, ActionResult result)
{
httpResponse.Content_Encoding = result.ContentEncoding.EncodingName;
httpResponse.Content_Type = result.ContentType;
httpResponse.Status = result.Status; if (result is EmptyResult)
{
return;
} if (result is FileResult)
{
var f = result as FileResult; httpResponse.SetContent(f.Content); return;
} httpResponse.SetContent(result.Content);
} public HttpResponse(string content) : this(content, "UTF-8", "application/json; charset=utf-8", HttpStatusCode.OK)
{ } public HttpResponse(string content, string encoding, string contentType, HttpStatusCode status)
{
this.Content_Encoding = encoding;
this.Content_Type = contentType;
this.Status = status;
this.SetContent(content);
} internal HttpResponse SetContent(byte[] content, Encoding encoding = null)
{
this.Content = content;
this.Encoding = encoding != null ? encoding : Encoding.UTF8;
this.Content_Length = content.Length.ToString();
return this;
} internal HttpResponse SetContent(string content, Encoding encoding = null)
{
//初始化内容
encoding = encoding != null ? encoding : Encoding.UTF8;
return SetContent(encoding.GetBytes(content), encoding);
} public string GetHeader(ResponseHeaderType header)
{
return base.GetHeader(header);
} public void SetHeader(ResponseHeaderType header, string value)
{
base.SetHeader(header, value);
} /// <summary>
/// 构建响应头部
/// </summary>
/// <returns></returns>
protected string BuildHeader()
{
StringBuilder builder = new StringBuilder();
builder.Append(Protocols + SPACE + Status.ToNVString() + ENTER);
builder.AppendLine("Server: Wenli's Server");
builder.AppendLine("Keep-Alive: timeout=20");
builder.AppendLine("Date: " + DateTimeHelper.Now.ToFString("r")); if (!string.IsNullOrEmpty(this.Content_Type))
builder.AppendLine("Content-Type:" + this.Content_Type); //支持跨域
builder.AppendLine("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
builder.AppendLine("Access-Control-Allow-Origin: *");
builder.AppendLine("Access-Control-Allow-Headers: Content-Type,X-Requested-With,Accept,yswenli");//可自行增加额外的header
builder.AppendLine("Access-Control-Request-Methods: GET, POST, PUT, DELETE, OPTIONS"); if (this.Headers != null && this.Headers.Count > )
{
foreach (var key in Headers.Names)
{
builder.AppendLine($"{key}: {Headers[key]}");
}
} return builder.ToString();
} /// <summary>
/// 生成数据
/// </summary>
private byte[] ToBytes()
{
List<byte> list = new List<byte>();
//发送响应头
var header = BuildHeader();
byte[] headerBytes = this.Encoding.GetBytes(header);
list.AddRange(headerBytes); //发送空行
byte[] lineBytes = this.Encoding.GetBytes(System.Environment.NewLine);
list.AddRange(lineBytes); //发送内容
list.AddRange(Content); return list.ToArray();
} public void Write(string str)
{
SetContent(str);
} public void BinaryWrite(byte[] data)
{
SetContent(data);
} public void Clear()
{
this.Write("");
} public void End()
{
HttpServer.Replay(UserToken, this.ToBytes());
HttpServer.Close(UserToken);
} }
}
四、HttpServer:这个就是承载webapi的容器;有人说不是有IIS和Apache么?本人想说的是:有self-host方便么?有无需安装,无需配置、随便高性能开跑好么?asp.net core里面都有了这个,没这个就没有逼格....(此处省略一万字),前面还研究tcp、http这个当然不能少了
/****************************************************************************
*Copyright (c) 2018 Microsoft All Rights Reserved.
*CLR版本: 4.0.30319.42000
*机器名称:WENLI-PC
*公司名称:Microsoft
*命名空间:SAEA.WebAPI.Http
*文件名: HttpServer
*版本号: V1.0.0.0
*唯一标识:914acb72-d4c4-4fa1-8e80-ce2f83bd06f0
*当前的用户域:WENLI-PC
*创建人: yswenli
*电子邮箱:wenguoli_520@qq.com
*创建时间:2018/4/10 13:51:50
*描述:
*
*=====================================================================
*修改标记
*修改时间:2018/4/10 13:51:50
*修改人: yswenli
*版本号: V1.0.0.0
*描述:
*
*****************************************************************************/
using SAEA.Sockets.Interface;
using SAEA.WebAPI.Common;
using SAEA.WebAPI.Http.Net;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text; namespace SAEA.WebAPI.Http
{
class HttpServer
{
ServerSocket _serverSocket; public HttpServer()
{
_serverSocket = new ServerSocket();
_serverSocket.OnRequested += _serverSocket_OnRequested;
} public void Start(int port = )
{
_serverSocket.Start(port);
} private void _serverSocket_OnRequested(IUserToken userToken, string htmlStr)
{
var httpContext = HttpContext.CreateInstance(this, userToken, htmlStr); var response = httpContext.Response; response.End();
} internal void Replay(IUserToken userToken, byte[] data)
{
_serverSocket.Reply(userToken, data);
} internal void Close(IUserToken userToken)
{
_serverSocket.Disconnected(userToken);
} }
}
五、Controller:为了实现类似于mvc的效果Controller这个大名鼎鼎的当然不能少了,其在C#中使用非常少量的代码即可实现
/****************************************************************************
*Copyright (c) 2018 Microsoft All Rights Reserved.
*CLR版本: 4.0.30319.42000
*机器名称:WENLI-PC
*公司名称:Microsoft
*命名空间:SAEA.WebAPI.Mvc
*文件名: Controller
*版本号: V1.0.0.0
*唯一标识:a303db7d-f83c-4c49-9804-032ec2236232
*当前的用户域:WENLI-PC
*创建人: yswenli
*电子邮箱:wenguoli_520@qq.com
*创建时间:2018/4/10 13:58:08
*描述:
*
*=====================================================================
*修改标记
*修改时间:2018/4/10 13:58:08
*修改人: yswenli
*版本号: V1.0.0.0
*描述:
*
*****************************************************************************/ using SAEA.WebAPI.Http; namespace SAEA.WebAPI.Mvc
{
/// <summary>
/// WebApi控制器
/// </summary>
public abstract class Controller
{
public HttpContext HttpContext { get; set; } /// <summary>
/// 返回Json
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
protected JsonResult Json(object data)
{
return new JsonResult(data);
}
/// <summary>
/// 自定义内容
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
protected ContentResult Content(string data)
{
return new ContentResult(data);
} /// <summary>
/// 小文件
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
protected FileResult File(string filePath)
{
return new FileResult(filePath);
} /// <summary>
/// 空结果
/// </summary>
/// <returns></returns>
protected EmptyResult Empty()
{
return new EmptyResult();
}
}
}
六、ActionResult:是mvc里面针对reponse结果进行了一个http格式的封装,本人主要实现了ContentResult、JsonResult、FileResult三个,至于其他的在WebAPI里基本上用不到。
/****************************************************************************
*Copyright (c) 2018 Microsoft All Rights Reserved.
*CLR版本: 4.0.30319.42000
*机器名称:WENLI-PC
*公司名称:Microsoft
*命名空间:SAEA.WebAPI.Mvc
*文件名: JsonResult
*版本号: V1.0.0.0
*唯一标识:340c3ef0-2e98-4f25-998f-2bb369fa2794
*当前的用户域:WENLI-PC
*创建人: yswenli
*电子邮箱:wenguoli_520@qq.com
*创建时间:2018/4/10 16:48:06
*描述:
*
*=====================================================================
*修改标记
*修改时间:2018/4/10 16:48:06
*修改人: yswenli
*版本号: V1.0.0.0
*描述:
*
*****************************************************************************/
using SAEA.WebAPI.Common;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text; namespace SAEA.WebAPI.Mvc
{
public class JsonResult : ActionResult
{
public JsonResult(object model) : this(SerializeHelper.Serialize(model))
{ }
public JsonResult(string json) : this(json, Encoding.UTF8)
{ } public JsonResult(string json, HttpStatusCode status)
{
this.Content = json;
this.ContentEncoding = Encoding.UTF8;
this.ContentType = "application/json; charset=utf-8";
this.Status = status;
} public JsonResult(string json, Encoding encoding, string contentType = "application/json; charset=utf-8")
{
this.Content = json;
this.ContentEncoding = encoding;
this.ContentType = contentType;
}
}
}
七、RouteTable:MVC里面有一个相当重要的概念叫约定优先,即为Controller、Action的名称是按某种规则来写编码的,其中将URL与自定义Controller对应起来的缓存映射就是RouteTable,并且作为缓存,也能极大的提升访问性能。当然这里并没有严格按照asp.net mvc里面的routetable来设计,而是根据只是实现webapi,并使用缓存反射结构能来实现的,并且只有约定,没有配置。
/****************************************************************************
*Copyright (c) 2018 Microsoft All Rights Reserved.
*CLR版本: 4.0.30319.42000
*机器名称:WENLI-PC
*公司名称:Microsoft
*命名空间:SAEA.WebAPI.Mvc
*文件名: RouteTable
*版本号: V1.0.0.0
*唯一标识:1ed5d381-d7ce-4ea3-b8b5-c32f581ad49f
*当前的用户域:WENLI-PC
*创建人: yswenli
*电子邮箱:wenguoli_520@qq.com
*创建时间:2018/4/12 10:55:31
*描述:
*
*=====================================================================
*修改标记
*修改时间:2018/4/12 10:55:31
*修改人: yswenli
*版本号: V1.0.0.0
*描述:
*
*****************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text; namespace SAEA.WebAPI.Mvc
{
/// <summary>
/// SAEA.WebAPI路由表
/// </summary>
public static class RouteTable
{
static object _locker = new object(); static List<Routing> _list = new List<Routing>(); /// <summary>
/// 获取routing中的缓存
/// 若不存在则创建
/// </summary>
/// <param name="controllerType"></param>
/// <param name="controllerName"></param>
/// <param name="actionName"></param>
/// <param name="isPost"></param>
/// <returns></returns>
public static Routing TryGet(Type controllerType, string controllerName, string actionName, bool isPost)
{
lock (_locker)
{
var list = _list.Where(b => b.ControllerName.ToLower() == controllerName.ToLower() && b.ActionName.ToLower() == actionName.ToLower() && b.IsPost == isPost).ToList(); if (list == null || list.Count == )
{
var routing = new Routing()
{
ControllerName = controllerName,
ActionName = actionName,
IsPost = isPost
}; var actions = controllerType.GetMethods().Where(b => b.Name.ToLower() == actionName.ToLower()).ToList(); if (actions == null || actions.Count == )
{
throw new Exception($"{controllerName}/{actionName}找不到此action!");
}
else if (actions.Count > )
{
throw new Exception($"{controllerName}/{actionName}有多个重复的的action!");
}
else
{
routing.Instance = System.Activator.CreateInstance(controllerType); //类上面的过滤
var attrs = controllerType.GetCustomAttributes(true); if (attrs != null)
{
var attr = attrs.Where(b => b.GetType().BaseType.Name == "ActionFilterAttribute").FirstOrDefault(); routing.Atrr = attr; }
else
{
routing.Atrr = null;
} routing.Action = actions[]; //action上面的过滤
if (routing.Atrr == null)
{
attrs = actions[].GetCustomAttributes(true); if (attrs != null)
{
var attr = attrs.Where(b => b.GetType().BaseType.Name == "ActionFilterAttribute").FirstOrDefault(); routing.Atrr = attr; }
else
{
routing.Atrr = null;
}
}
}
_list.Add(routing);
return routing;
}
else if (list.Count > )
{
throw new Exception("");
}
return list.FirstOrDefault();
}
}
} }
在MVC的思想里面ActionFilterAtrribute的这个AOP设计也一直伴随左右,比如记日志、黑名单、权限、验证、限流等等功能,所以路由的时候也会缓存这个。至此一些关键性的地方都已经弄的差不多了,为了更好的了解上面说的这些,下面是vs2017中项目的结构截图:
纯粹干净单码,无任何晦涩内容,如果对mvc有一定了解的,这个差不多可以NoNotes,接下来就是按asp.net mvc命名方式,写个测试webapi看看情况,首先还是测试项目结构图:
HomeController里面按asp.net mvc的习惯来编写代码:
/****************************************************************************
*Copyright (c) 2018 Microsoft All Rights Reserved.
*CLR版本: 4.0.30319.42000
*机器名称:WENLI-PC
*公司名称:Microsoft
*命名空间:SAEA.WebAPITest.Controllers
*文件名: HomeController
*版本号: V1.0.0.0
*唯一标识:e00bb57f-e3ee-4efe-a7cf-f23db767c1d0
*当前的用户域:WENLI-PC
*创建人: yswenli
*电子邮箱:wenguoli_520@qq.com
*创建时间:2018/4/10 16:43:26
*描述:
*
*=====================================================================
*修改标记
*修改时间:2018/4/10 16:43:26
*修改人: yswenli
*版本号: V1.0.0.0
*描述:
*
*****************************************************************************/
using SAEA.WebAPI.Mvc;
using SAEA.WebAPITest.Attrubutes;
using SAEA.WebAPITest.Model; namespace SAEA.WebAPITest.Controllers
{
/// <summary>
/// 测试实例代码
/// </summary>
//[LogAtrribute]
public class HomeController : Controller
{
/// <summary>
/// 日志拦截
/// 内容输出
/// </summary>
/// <returns></returns>
//[Log2Atrribute]
public ActionResult Index()
{
return Content("Hello,I'm SAEA.WebAPI!");
}
/// <summary>
/// 支持基本类型参数
/// json序列化
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public ActionResult Get(int id)
{
return Json(new { Name = "yswenli", Sex = "男" });
}
/// <summary>
/// 底层对象调用
/// </summary>
/// <returns></returns>
public ActionResult Show()
{
var response = HttpContext.Response; response.Content_Type = "text/html; charset=utf-8"; response.Write("<h3>测试一下那个response对象使用情况!</h3>参考消息网4月12日报道外媒称,法国一架“幻影-2000”战机意外地对本国一家工厂投下了..."); response.End(); return Empty();
} [HttpGet]
public ActionResult Update(int id)
{
return Content($"HttpGet Update id:{id}");
}
/// <summary>
/// 基本类型参数、实体混合填充
/// </summary>
/// <param name="isFemale"></param>
/// <param name="userInfo"></param>
/// <returns></returns>
[HttpPost]
public ActionResult Update(bool isFemale, UserInfo userInfo = null)
{
return Json(userInfo);
}
[HttpPost]
public ActionResult Test()
{
return Content("httppost test");
}
/// <summary>
/// 文件输出
/// </summary>
/// <returns></returns>
public ActionResult Download()
{
return File(HttpContext.Server.MapPath("/Content/Image/c984b2fb80aeca7b15eda8c004f2e0d4.jpg"));
}
}
}
增加一个LogAtrribute打印一些内容:
/****************************************************************************
*Copyright (c) 2018 Microsoft All Rights Reserved.
*CLR版本: 4.0.30319.42000
*机器名称:WENLI-PC
*公司名称:Microsoft
*命名空间:SAEA.WebAPITest.Common
*文件名: LogAtrribute
*版本号: V1.0.0.0
*唯一标识:2a261731-b8f6-47de-b2e4-aecf2e0e0c0f
*当前的用户域:WENLI-PC
*创建人: yswenli
*电子邮箱:wenguoli_520@qq.com
*创建时间:2018/4/11 13:46:42
*描述:
*
*=====================================================================
*修改标记
*修改时间:2018/4/11 13:46:42
*修改人: yswenli
*版本号: V1.0.0.0
*描述:
*
*****************************************************************************/
using SAEA.Commom;
using SAEA.WebAPI.Http;
using SAEA.WebAPI.Mvc; namespace SAEA.WebAPITest.Attrubutes
{
public class LogAtrribute : ActionFilterAttribute
{
/// <summary>
/// 执行前
/// </summary>
/// <param name="httpContext"></param>
/// <returns>返回值true为继续,false为终止</returns>
public override bool OnActionExecuting(HttpContext httpContext)
{
return true;
} /// <summary>
/// 执行后
/// </summary>
/// <param name="httpContext"></param>
/// <param name="result"></param>
public override void OnActionExecuted(HttpContext httpContext, ActionResult result)
{
ConsoleHelper.WriteLine($"请求地址:{httpContext.Request.Query},回复内容:{result.Content}");
}
}
}
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
如果发现本文有什么问题和任何建议,也随时欢迎交流~
自行实现高性能MVC WebAPI的更多相关文章
- 线程安全使用(四) [.NET] 简单接入微信公众号开发:实现自动回复 [C#]C#中字符串的操作 自行实现比dotcore/dotnet更方便更高性能的对象二进制序列化 自已动手做高性能消息队列 自行实现高性能MVC WebAPI 面试题随笔 字符串反转
线程安全使用(四) 这是时隔多年第四篇,主要是因为身在东软受内网限制,好多文章就只好发到东软内部网站,懒的发到外面,现在一点点把在东软写的文章给转移出来. 这里主要讲解下CancellationT ...
- 自行实现高性能MVC
wcf虽然功能多.扩展性强但是也面临配置忒多,而且restful的功能相当怪异,并且目前没法移植.asp.net core虽然支持webapi,但是功能也相对繁多.配置复杂.就没有一个能让码农们安安心 ...
- ASP.NET MVC WebApi 返回数据类型序列化控制(json,xml) 用javascript在客户端删除某一个cookie键值对 input点击链接另一个页面,各种操作。 C# 往线程里传参数的方法总结 TCP/IP 协议 用C#+Selenium+ChromeDriver 生成我的咕咚跑步路线地图 (转)值得学习百度开源70+项目
ASP.NET MVC WebApi 返回数据类型序列化控制(json,xml) 我们都知道在使用WebApi的时候Controller会自动将Action的返回值自动进行各种序列化处理(序列化为 ...
- ASP.NET Core MVC/WebAPi 模型绑定探索
前言 相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用 ...
- mvc+webapi 单元测试
1.前言 现在这个项目已经有阶段性的模块完成了,所以就想着对这些模块进行单元测试,以保证项目的代码的质量.首先虽然标题是mvc+webapi实质上我只是对mvc进行的测试.用的时候vs的unit te ...
- 让Asp.net mvc WebAPI 支持OData协议进行分页查询操作
这是我在用Asp.net mvc WebAPI 支持 OData协议 做分页查询服务时的 个人拙笔. 代码已经开发到oschina上.有兴趣的朋友可以看看,欢迎大家指出不足之处. 看过了园子里的几篇关 ...
- MVC WebAPI 三层分布式框架开发
版权声明:本文为博主原创文章,未经博主允许不得转载. 前言:SOA(面向服务的架构)是目前企业应用开发过程中普遍采用的技术,基于MVC WebAPI三层分布式框架开发,以此适用于企业信息系统的业务处理 ...
- 启用 mvc webapi 的 session功能可用
默认 mvc webapi 不开启 session 会话支持 所以需要修改配置,在 Global 开启 session 支持 如下: 1.重写 init() 方法 public override vo ...
- .net和java和谐相处之安卓客户端+.net asp.net mvc webapi 2
作为没有花很多时间转java,把java当C#用的我,在做服务器端程序的时候,自然不想考虑java web,java需要学的框架太多了,看了一下Java Servlet,始终没有编码的冲动.经过几天的 ...
随机推荐
- Python中四种样式的99乘法表
1.常规型. #常规型 i=1 while i<=9: j=1 while j<=i: print(''%d*%d=%2d''%(i,j,i*j),end='') i+=1 #等号只是用来 ...
- 数据库 --> SQL 和 NoSQL 的区别
SQL 和 NoSQL 的区别 一.概念 SQL (Structured Query Language) 数据库,指关系型数据库.主要代表:SQL Server,Oracle,MySQL(开源), ...
- Hibernate学习(2)- hibernate.cfg.xml详解
1:主配置文件主要分为三部分: 注意:通常情况下,一个session-factory节点代表一个数据库: 1.1:第一部分 数据库连接部分,注意"hibernate.connection.d ...
- 实现Windows程序的更新
实现Windows程序的更新 一.使用枚举避免不合理的赋值 1.使用枚举的好处: 使用常量类中Student类中加入一个特别属性,StudentGender,而且这个属性只能接受两个有效值," ...
- 庖丁解牛Linux内核学习笔记(1)--计算机是如何工作的
存储程序计算机模型 冯诺依曼体系结构 冯诺依曼体系结构是存储程序计算机,什么叫存储程序计算机?从硬件角度说,假设有cpu和内存,两者通过总线连接,在cpu内部有一个寄存器叫ip(instruction ...
- 软件工程网络15团队作业1——团队组队&展示
Deadline: 2018-3-25 10:00PM,以提交至班级博客时间为准. 申请开通团队博客,并将团队博客地址发表在本次随笔的评论中 团队展示 根据5-6人的组队要求,每个队伍创建团队博客并发 ...
- C语言第七次作业
一.PTA实验作业 题目1:求整数序列中出现次数最多的数 1.本题PTA提交列表 2.设计思路 定义一个整型数组a[1001],i,j 为循环变量,N,定义数组b[1001]={0} 输入N for( ...
- 如何查看与更改python的工作目录?
在编写<机器学习实战>第二章kNN代码时遇到问题,即在自己编写好模块后,使用ipython进行import时,出现以下错误: 可知若想找到该模块,需将工作目录改变到当前文件(模块py文件) ...
- NOIP2017 列队
https://www.luogu.org/problemnew/show/P3960 p<=500 50分 模拟 每个人的出队只会影响当前行和最后一列 p<=500,有用的行只有500行 ...
- 微信小程序轮播图
swiper标签 <!--index.wxml--> <swiper class="swiper" indicator-dots="true" ...