WebApi Controller 分类

本文介绍了在WebApi项目中进行控制器分类的方法,包括解决多个GET/POST方法的问题、自定义控制器选择器等,并提供了实现清晰项目结构的具体步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

      写这篇WebApi Controller分类一是把Contrller分类的解决方法说一说,再顺便把遇到的问题和解决方法聊一聊。 说实话第一次在项目中使用WebApi感觉非常的不顺手。

 

遇到的问题和解决方法

     1、多个Get方法。

         Get请求方式是用的最多,而且多处需要重载,很好解决,只需根据参数重载方法即可,如果比较懒的话连路由规则都不需要定义了,直接Get?a=XXX&b=XXX。

     2、多个Post方法。

         如果按照WebApi官方的规则,那么一个WebApi是不能包含多个Post请求的, 至今我也没找到类似Get重载处理Post重载一样的方法,后来我发现有人建议直接用Action

  名称区分(eg. InsertPerson  InsertProduct),然后添加HttpPost属性,在前台直接调用,那么我考虑如果这样用的话是不是又违背Restful的设计思想呢?

     3、当遇到Delete请求时调用Delete接口始终没有调成功。

         一开始我定义Delete接口让前端调用(ajax发送delete请求),一直报405,后来查了一把,说是需要Web.config配置,又是开启IIS DELETE和PUT请求,几种方法全试了最后还是没搞定,无奈之下我改用了POST请求, PUT请求压根没有用。   在群里询问了一把原来他们是在cs中用HttpClient中调用。

     4、Post多个参数。

        官方建议是使用FromBody标签,多个参数用Dto封装。 自定义Post参数绑定类,继承HttpParameterBinding。然后在Application_Start()中注册:GlobalConfiguration.Configuration.ParameterBindingRules.Insert(0, SimplePostVariableParameterBinding.HookupParameterBinding);

 

Controller分类

     Controller分类主要是为了解决项目过大以后Controller只有一级编目,项目结构上会显的非常凌乱,而且会遇到 ApiController文件同名无法解决的问题,提起Controller分类的话大家首先会想到Area, 但实际应用你会发现那真的是为MVC量身打造的,丝毫没有提WebApi考虑,比如新建Area时文件夹会自动创建Controller、Model、 View, 这些都是不需要的。 那么解决方法就是自己根据项目业务逻辑需要创建Controller编目,最重要的就是自定义Controller Select让WebApi框架找到你定义的文件结构和ApiController【后面会附带代码】。

 

1、这是我Demo里的Controller结构,包含一个一级编目和二级编目(怎么样,看起来结构一下就清晰多了?)

 

 

2、我把用到的Url汇总成一张表格

Controller名称命名空间url
AdviseControllerMvcApplication4.Controllers.ContractUs/apix/ContactUs/Advise
ProductControllerMvcApplication4.Controllers.ContractUs/apix/ContactUs/Product
FinancialControllerMvcApplication4.Controllers.Products.Enterprise/apixx/Products/Enterprise/Financial
OfficeControllerMvcApplication4.Controllers.Products.Enterprise/apixx/Products/Enterprise/Office
PuzControllerMvcApplication4.Controllers.Products.Game/apixx/Products/Game/Puz
RpzControllerMvcApplication4.Controllers.Products.Game/apixx/Products/Game/Rpg

    url里包含前缀apix和apixx,参考博客园一朋友的写法,另外我发现,如果一级编目和二级编目里的前缀如果写成一样是行不通的,会报No Controller was selected to handle this request,所以你需要根据你的编目结构不同而指定不同的前缀,url后就是Area和Category规则。

 

3、创建完Controller编目结构后就是创建路由规则了(重要)

     在这里指定你的接口前缀 apix,apixx

config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

//只有一级编目
config.Routes.MapHttpRoute(
                name:"AreaApi",
                routeTemplate:"apix/{area}/{controller}/{id}",
                defaults:new {id = RouteParameter.Optional});

 //包含二级编目
config.Routes.MapHttpRoute(
                name: "AreaCategoryApi",
                routeTemplate: "apix/{area}/{category}/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional });

 

4、指定完路由规则,如果让WebApi认识你的接口呢? 自定义Controller Selector,继承自DefaultHttpControllerSelector, 对了,创建完自定义Controller Selector后别忘了在Application_Start()中注册,否则也是无效的:

 GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector),new ClassifiedHttpControllerSelector(GlobalConfiguration.Configuration));

private const string AREA_ROUTE_VARIABLE_NAME = "area";
        private const string CATEGORY_ROUTE_VARIABLE_NAME = "category";
        private const string THE_FIX_CONTROLLER_FOLDER_NAME = "Controllers";

        private readonly HttpConfiguration m_configuration;
        private readonly Lazy<ConcurrentDictionary<string, Type>> m_apiControllerTypes;

        public ClassifiedHttpControllerSelector(HttpConfiguration configuration)
            : base(configuration)
        {
            m_configuration = configuration;
            m_apiControllerTypes = new Lazy<ConcurrentDictionary<string, Type>>(GetAllControllerTypes);
        }

        public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            return GetApiController(request);
        }

        private static string GetRouteValueByName(HttpRequestMessage request, string strRouteName)
        {
            IHttpRouteData data = request.GetRouteData();
            if (data.Values.ContainsKey(strRouteName))
            {
                return data.Values[strRouteName] as string;
            }
            return null;
        }

        private static ConcurrentDictionary<string, Type> GetAllControllerTypes()
        {
            Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
            Dictionary<string, Type> types = assemblies.SelectMany(a => a.GetTypes().Where(t => !t.IsAbstract && t.Name.EndsWith(ControllerSuffix, StringComparison.OrdinalIgnoreCase) && typeof(IHttpController).IsAssignableFrom(t))).ToDictionary(t => t.FullName, t => t);
            return new ConcurrentDictionary<string, Type>(types);
        }

        private HttpControllerDescriptor GetApiController(HttpRequestMessage request)
        {
            string strAreaName = GetRouteValueByName(request, AREA_ROUTE_VARIABLE_NAME);
            string strCategoryName = GetRouteValueByName(request, CATEGORY_ROUTE_VARIABLE_NAME);
            string strControllerName = GetControllerName(request);
            Type type;
            try
            {
                type = GetControllerType(strAreaName, strCategoryName, strControllerName);
            }
            catch (Exception)
            {
                return null;
            }
            return new HttpControllerDescriptor(m_configuration, strControllerName, type);
        }

        private Type GetControllerType(string areaName, string categoryName, string controllerName)
        {
            IEnumerable<KeyValuePair<string, Type>> query = m_apiControllerTypes.Value.AsEnumerable();
            string strControllerSearchingName;
            if (string.IsNullOrEmpty(areaName))
            {
                strControllerSearchingName = THE_FIX_CONTROLLER_FOLDER_NAME + "." + controllerName;
            }
            else
            {
                if (string.IsNullOrEmpty(categoryName))
                {
                    strControllerSearchingName = THE_FIX_CONTROLLER_FOLDER_NAME + "." + areaName + "." + controllerName;
                }
                else
                {
                    strControllerSearchingName = THE_FIX_CONTROLLER_FOLDER_NAME + "." + areaName + "." + categoryName + "." + controllerName;
                }
            }
            return query.Where(x => x.Key.IndexOf(strControllerSearchingName, StringComparison.OrdinalIgnoreCase) != -1).Select(x => x.Value).Single();
        }

 

总结

     找到了解决方法后,接下来就是重构项目了,对于Delete和Put请求一直还是想解决,还请大家指点指点。  代码下载 http://pan.baidu.com/s/1bnHQItx

 

参考资料

1、http://www.cnblogs.com/guogangj/archive/2013/03/11/2950084.html

2、http://blogs.infosupport.com/asp-net-mvc-4-rc-getting-webapi-and-areas-to-play-nicely/

 

MVC WebApi 用户权限验证及授权DEMO 前言:Web 用户的身份验证,及页面操作权限验证是B/S系统的基础功能,一个功能复杂的业务应用系统,通过角色授权来控制用户访问,本文通过Form认证,Mvc的Controller基类及Action的权限验证来实现Web系统登录,Mvc前端权限校验以及WebApi服务端的访问校验功能。 1 Web Form认证介绍 Web应用的访问方式因为是基于浏览器的Http地址请求,所以需要验证用户身份的合法性。目前常见的方式是Form认证,其处理逻辑描述如下: 1) 用户首先要在登录页面输入用户名和密码,然后登录系统,获取合法身份的票据,再执行后续业务处理操作; 2) 用户在没有登录的情况下提交Http页面访问请求,如果该页面不允许匿名访问,则直接跳转到登录页面; 3) 对于允许匿名访问的页面请求,系统不做权限验证,直接处理业务数据,并返回给前端; 4) 对于不同权限要求的页面Action操作,系统需要校验用户角色,计算权限列表,如果请求操作在权限列表中,则正常访问,如果不在权限列表中,则提示“未授权的访问操作”到异常处理页面。 2 WebApi 服务端Basic 方式验证 WebApi服务端接收访问请求,需要做安全验证处理,验证处理步骤如下: 1) 如果是合法的Http请求,在Http请求头中会有用户身份的票据信息,服务端会读取票据信息,并校验票据信息是否完整有效,如果满足校验要求,则进行业务数据的处理,并返回给请求发起方; 2) 如果没有票据信息,或者票据信息不是合法的,则返回“未授权的访问”异常消息给前端,由前端处理此异常。 3 登录及权限验证流程 1) 用户打开浏览器,并在地址栏中输入页面请求地址,提交; 2) 浏览器解析Http请求,发送到Web服务器;Web服务器验证用户请求,首先判断是否有登录的票据信息; 3) 用户没有登录票据信息,则跳转到登录页面; 4) 用户输入用户名和密码信息; 5) 浏览器提交登录表单数据给Web服务器; 6) Web服务需要验证用户名和密码是否匹配,发送api请求给api服务器; 7) api用户账户服务根据用户名,读取存储在数据库中的用户资料,判断密码是否匹配; 7.1)如果用户名和密码不匹配,则提示密码错误等信息,然该用户重新填写登录资料; 7.2)如果验证通过,则保存用户票据信息; 8) 接第3步,如果用户有登录票据信息,则跳转到用户请求的页面; 9) 验证用户对当前要操作的页面或页面元素是否有权限操作,首先需要发起api服务请求,获取用户的权限数据; 10). api用户权限服务根据用户名,查找该用户的角色信息,并计算用户权限列表,封装为Json数据并返回; 11). 当用户有权限操作页面或页面元素时,跳转到页面,并由页面Controller提交业务数据处理请求到api服务器; 如果用户没有权限访问该页面或页面元素时,则显示“未授权的访问操作”,跳转到系统异常处理页面。 12). api业务服务处理业务逻辑,并将结果以Json 数据返回; 13). 返回渲染后的页面给浏览器前端,并呈现业务数据到页面; 14). 用户填写业务数据,或者查找业务数据; 15). 当填写或查找完业务数据后,用户提交表单数据; 16). 浏览器脚本提交get,post等请求给web服务器,由web服务器再次解析请求操作,重复步骤2的后续流程; 17). 当api服务器验证用户身份是,没有可信用户票据,系统提示“未授权的访问操作”,跳转到系统异常处理页面。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值