问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

如何改写WebApi部分默认规则

发布网友 发布时间:2022-04-22 05:38

我来回答

1个回答

热心网友 时间:2023-07-12 17:53

如何改写WebApi部分默认规则

如何改写WebApi部分默认规则为什么要改最近公司在推广SOA框架,第一次正经接触这种技术(之前也有但还是忽略掉吧),感觉挺好,就想自己也折腾一下,实现一个简单的SOA框架
用过mvc进行开发,印象之中WebApi和Mvc好像是一样的,带着这样的预设开始玩WebApi,然后*得找不到着北。
*的原因,是Mvc和WebApi在细节上差别还是有点大,例如:
在Mvc中,一个Controller中的所有公共方法一般情况下可以响应POST方法,而WebApi中不行在Mvc中,一个Action方法中的参数即可来自Url,也可以来自Form,而WebApi中不是这样,具体的规则好像是除非你在参数中加了[FromBody],否则这个参数永远也无法从Form中获取这是这两种技术我知道的最大的差别,其他的没发现或者说是没注意,也有可能这些差别是因为我不会用,毕竟接触WebApi时间不长。如果我有些地方说错了,请指正。
就这两个不同点,我查了很多资料,也没有办法解决,第一个还好,加个特性就行了,第二个的话好像就算加了[FromBody]也还是不行,感觉就是一堆*。接着,既然这么多让我不爽的地方,那我就来改造它吧。
改造的目标,有以下几个:
不再*控制器必须以Controller结尾,其实这个并不是必须,只是被*着确实不太舒服所有方法可以响应所有的请求方法,如果存在方法名相同的方法,那么才需要特性来区分Action中的参数优先从Url中获取,再从Body中获取,从Body中获取的时候,优先假设Body中的数据是表单参数,若不是则将Body中的数据当作json或xml数据进行获取定下了目标之后,感觉微软为什么要这样设计WebApi呢,或许它有它的道理。
目标好定,做起来真是头大,一开始想参考公司的SOA框架的实现,但因为我用了OWIN技术来进行宿主,而看了公司的框架好像不是用的这个,总之就是看了半天没看懂应该从哪个地方开始,反而是越看越糊,毕竟不是完全一样的技术,所以还是自己弄吧。
OK,废话了这么多,进入正题吧。首先来一个链接,没了这个文章我就不可能改造成功:http://www.cnblogs.com/beginor/archive/2012/03/22/2411496.html
OWIN宿主其实这个网上很多,我主要是为了贴代码,不然的话下面几小节写不下去
[assembly: OwinStartup(typeof(Startup))]//这句是在IIS宿主的时候使用的,作用是.Net会查找Startup类来启动整个服务 namespace Xinchen.SOA.Server { public class Startup { public void Configuration(IAppBuilder appBuilder) { HttpConfiguration config = new HttpConfiguration(); config.Routes.MapHttPRoute( name: "DefaultApi", routeTemplate: "{controller}/{action}" ); config.Services.Add(typeof(ValueProviderFactory), new MyValueProviderFactory());//自定义参数查找,实现第三个目标 config.Services.Replace(typeof(IHttpControllerSelector), new ControllerSelector(config));//自定义控制器查找,实现第一个目标 config.Services.Replace(typeof(IHttpActionSelector), new HttpActionSelector());//自定义Action查找,实现第二个目标 appBuilder.UseWebApi(config); } } } 省略了部分不太重要的代码,Services.Add和Replace从字面就能明白是什么意思,但我没有试过是否必须要像上面那样写才行
对控制器的*public class ControllerSelector : IHttpControllerSelector { HttpConfiguration _config; IDictionary<string, HttpControllerDescriptor> _desriptors = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase); public ControllerSelector(HttpConfiguration config) { _config = config; } void InitControllers() { if (_desriptors.Count <= 0) { lock (_desriptors) { if (_desriptors.Count <= 0) { var assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.GlobalAssemblyCache && !x.IsDynamic); var controllerTypes = new List<Type>(); foreach (var ass in assemblies) { controllerTypes.AddRange(ass.GetExportedTypes().Where(x => typeof(ApiController).IsAssignableFrom(x))); } var descriptors = new Dictionary<string, HttpControllerDescriptor>(); foreach (var controllerType in controllerTypes) { var descriptor = new HttpControllerDescriptor(_config, controllerType.Name, controllerType); _desriptors.Add(descriptor.ControllerName, descriptor); } } } } } public IDictionary<string, HttpControllerDescriptor> GetControllerMapping() { InitControllers(); return _desriptors; } public System.Web.Http.Controllers.HttpControllerDescriptor SelectController(System.Net.Http.HttpRequestMessage request) { InitControllers(); var routeData = request.GetRouteData(); var controllerName = Convert.ToString(routeData.Values.Get("controller")); if (string.IsNullOrWhiteSpace(controllerName)) { throw new ArgumentException(string.Format("没有在路由信息中找到controller")); } return _desriptors.Get(controllerName); } } 这个其实比较简单,测试中WebApi好像没调用GetControllerMapping方法,直接调用了SelectController方法,最后一个方法中有两个Get方法调用,Get只是把从字典获取值的TryGetValue功能给封装了一下,InitControllers方法是从当前所有的程序集中找继承了ApiController的类,找到之后缓存起来。这段代码整体比较简单。
对Action的*public class HttpActionSelector : IHttpActionSelector { public ILookup<string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor) { var methods = controllerDescriptor.ControllerType.GetMethods(); var result = new List<HttpActionDescriptor>(); foreach (var method in methods) { var descriptor = new ReflectedHttpActionDescriptor(controllerDescriptor, method); result.Add(descriptor); } return result.ToLookup(x => x.ActionName); } public HttpActionDescriptor SelectAction(HttpControllerContext controllerContext) { var actionDescriptor = new ReflectedHttpActionDescriptor(); var routeData = controllerContext.RouteData; object action = string.Empty; if (!routeData.Values.TryGetValue("action", out action)) { throw new HttpResponseException(controllerContext.Request.CreateErrorResponse(System.Net.HttpStatusCode.NotFound, "在路由中未找到action")); } string actionName = action.ToString().ToLower(); var methods = controllerContext.ControllerDescriptor.ControllerType.GetMethods().Where(x => x.Name.ToLower() == actionName); var count = methods.Count(); MethodInfo method = null; switch (count) { case 0: throw new HttpResponseException(controllerContext.Request.CreateErrorResponse(System.Net.HttpStatusCode.NotFound, "在控制器" + controllerContext.ControllerDescriptor.ControllerName + "中未找到名为" + actionName + "的方法")); case 1: method = methods.FirstOrDefault();
break; default: var httpMethod = controllerContext.Request.Method; var filterdMethods = methods.Where(x => { var verb = x.GetCustomAttribute<AcceptVerbsAttribute>(); if (verb == null) { throw new HttpResponseException(controllerContext.Request.CreateErrorResponse(System.Net.HttpStatusCode.NotFound, "在控制器" + controllerContext.ControllerDescriptor.ControllerName + "中找到多个名为" + actionName + "的方法,请考虑为这些方法加上AcceptVerbsAttribute特性")); } return verb.HttpMethods.Contains(httpMethod); }); if (filterdMethods.Count() > 1) { throw new HttpResponseException(controllerContext.Request.CreateErrorResponse(System.Net.HttpStatusCode.NotFound, "在控制器" + controllerContext.ControllerDescriptor.ControllerName + "中找到多个名为" + actionName + "的方法,并且这些方法的AcceptVerbsAttribute都含有" + httpMethod.ToString() + ",发生重复")); } else if (filterdMethods.Count() <= 0) { throw new HttpResponseException(controllerContext.Request.CreateErrorResponse(System.Net.HttpStatusCode.NotFound, "在控制器" + controllerContext.ControllerDescriptor.ControllerName + "中找到多个名为" + actionName + "的方法,但没有方法被配置为可以响应" + httpMethod.ToString() + "请求")); } method = filterdMethods.FirstOrDefault();
break; } return new ReflectedHttpActionDescriptor(controllerContext.ControllerDescriptor, method); } } GetActionMapping方法很简单,从控制器类型中找到所有的Action方法并返回
SelectAction方法相对复杂,其实就是第二个目标的逻辑,代码看起来比较多其实并有很难的地方。
对Action的参数的*这一块比较难,我试了很久才成功,而且还有坑
public class ActionValueBinder : DefaultActionValueBinder { protected override HttpParameterBinding GetParameterBinding(HttpParameterDescriptor parameter) { ParameterBindingAttribute parameterBinderAttribute = parameter.ParameterBinderAttribute; if (parameterBinderAttribute == null) { ParameterBindingRulesCollection parameterBindingRules = parameter.Configuratio
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
RDL是什么意思 报税软件是什么意思 某公司的财务报表管理软件属于 报表系统指的是什么? 怎么玩真三最好?我是新手选什么英雄最好? 真三新手玩什么英雄好? 最近玩魔兽真三,想请问一下高手,初期用什么英雄最好,新手攻略 新手玩真三选什么英雄好啊 ...离泡还有一段距离的地方好几片红,红的地方又疼又痒的,怎么办... 眼角出了好多小水疱.医生说是疱疹.怎么办 webapi怎样获取post请求的数据 webapi token和basic的区别 WebAPI与传统的WebService有哪些不同 怎样操作WebAPI接口 小明和机灵什么的解决了这个问题 众多明星抖机灵频翻车,暴露低劣德行,却还认为自... 越来越多的人,从iPhone换成了华为是什么原因? 在华为 工作是怎样一番体验 华为手机有哪些功能很实用,但是大家都不太知道? 为什么新买的华为机灵盒不会用? 华为手机重启光标不动 ,没反应 华为手机怎么跟机灵盒电视投影? 华为荣耀8手机,开启了移动数据却无法上网怎么办?? 当年的华为太子,叛逃后被任正非怒砸4亿“围剿”,如... 键盘膜会不会损伤屏幕? 侧后玻璃膜贴炫彩变色的,会不会违法? 侧后玻璃膜贴炫彩变色的,需不需备案? 用来做手机炫彩贴的3M白色打印膜是什么型号呀。后... 联想笔记本电脑g410贴纸尺寸应该是多少 笔记本屏幕保护膜怎么弄下来 net core webapi 怎么样 怎样保证对外作为公共接口的webapi的安全性 桌面程序怎样通过webapi获取json数据 webapi接口访问验证是否登陆的解决方案! 用MVC WebApi写接口,网闸上了之后,方法访问不到... 百度地图WebAPI查出的结果为0是怎么回事 java有没有.net webapi 用于商用的百度地图API如何收费 跪求计算机信息管理毕业论文 空调制冷液怎么加?多久加一次? http://you.video.sina.com.cn/api/sinawebApi/out... 空调如何加液 空调冬天如何加液 大家帮忙推荐个游戏格斗的可以玩2P的 《阳光体育之歌》这一个节目的主持词 如何创建一个HTTP post的webapi IPhone8plus能升级到13.61版本吗? 汽车空调制冷液怎么加 法国电影《心动的感觉》片尾曲英文歌名是什么 心动的感觉 歌词