• WebApi Controller 分类


    前言

          写这篇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
    AdviseController MvcApplication4.Controllers.ContractUs /apix/ContactUs/Advise
    ProductController MvcApplication4.Controllers.ContractUs /apix/ContactUs/Product
    FinancialController MvcApplication4.Controllers.Products.Enterprise /apixx/Products/Enterprise/Financial
    OfficeController MvcApplication4.Controllers.Products.Enterprise /apixx/Products/Enterprise/Office
    PuzController MvcApplication4.Controllers.Products.Game /apixx/Products/Game/Puz
    RpzController MvcApplication4.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/

  • 相关阅读:
    关于效应量
    sc 与 net 命令
    隐函数求导
    关于向量空间的基本性质,与子空间的最最基本性质
    生成随机数(C++)
    关于 setw() 函数(C++)
    关于 加减乘除 基本运算的性质
    为什么文件无法用wps打开,甚至wps.exe本身都无法打开?
    with open()函数中,如何在文件名设置中引用变量(python)
    pygame 运行心理学问卷
  • 原文地址:https://www.cnblogs.com/sword-successful/p/4945807.html
Copyright © 2020-2023  润新知