Achieve their own vernacular series of simple formula webapi mvc framework

Preface: This article is a minimalist style mvc api framework, as the only way to resolve the entry-api, and is not considered here mvc framework, because there is no view layer, after all, most of them belong to separate the front and rear ends, of course, can provide view layer For the sake of the view as text is returned.

github address: https: //github.com/BestHYC/WebAPISolution.git

Demo examples:

 

 

 

aims:

1. parsed for Home / default. 2. httpcontext provide a simple process. 3. There is a simple understanding of .4. A good step by step through frame style, frame style to build their own framework of mvc

The key point is that there are a lot simpler than you think, rare is the implementation of which may extend maintainable and secure authentication exhaustive However predecessors trees descendants cool, we learn just fine.

One: Create the controller objects

Objectives: the factory model, access to all the controller, and create their objects

1.1 defines the interface, and defines the base class inherits all Controller ApiBaseController

    public interface IApiController
    {
    }
    public abstract class ApiBaseController : IApiController
    {
    }

  Create a simulation Controller, which is our own definition of controller, the controller is actually your own implementation

    public class HomeController : ApiBaseController
    {
    }

1.2. By factory mode, call the corresponding control object by name, since the controller does not substantially change, and then find the corresponding name by the controller

   public interface IControllerFactory
    {
        IApiController CreateController(String name);
    }
    public class DefaultControllerFactory : IControllerFactory
    {
        private static List<Type> controllerTypes = new List<Type>();
        static DefaultControllerFactory()
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            foreach (Type type in assembly.GetTypes().Where(type => typeof(IApiController).IsAssignableFrom(type)))
            {
                controllerTypes.Add(type);
            }
        }
        public IApiController CreateController(String name)
        {
            if (name.IndexOf("Controller") == -1) name += "Controller";
            Type controllerType = controllerTypes.FirstOrDefault(c => String.Compare(name, c.Name, true) == 0);
            if (controllerType == null)
            {
                return null;
            }
            return (IApiController)Activator.CreateInstance(controllerType);
        }
    }

ok, so that the controller can take a simple factory pattern.

Two: Now that the controller has been created, then the same situation calls inside the method, currently home / default, it will parse directly into default method

1. The first simple implementation of a method call

 

    public interface ActionInvoker
    {
        void InvokeAction(Object type, String actionName);
    }
    public class DefaultActionInvoker : ActionInvoker
    {
        public void InvokeAction(Object controller, String actionName)
        {
            MethodInfo methodInfo = controller.GetType().GetMethods().First(m => String.Compare(actionName, m.Name, true) == 0);
            methodInfo.Invoke(controller, null);
        }
    }

 

Here is very simple, direct call to the corresponding method name, this is the easiest way to achieve webapi appear in the analytical route,

In fact, the theory is so simple, no other people thought it would be difficult, the next start will make modifications is a step by step to build a simple, but a little sound api

Three: optimized code, do the cache controller and method for

///  <Summary> 
    /// Global IApiController all types of operations are performed by the buffers herein
     /// elsewhere only the type of treatment, such as A / B, is then corresponds AController or A, are elsewhere We handle
     /// Note that here, and only as a cache type of method, no processing is performed and returns the result of the transfer object holding single function
     /// maintain a single path, i.e. a / B wherein a controller 1 only a, B only a method, even if there is overload, but also have to be distinguished by route name
     ///  </ Summary> 
    Internal  static  class ApiControllerActionCache 
    { 
        Private  static the Dictionary <String, the Type> s_controllerTypes = new new the Dictionary < String , the Type > ();
         Private  static the Dictionary <the Type, ActionCache> = s_actionCache new new Dictionary<Type, ActionCache>();
        static ApiControllerActionCache()
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            foreach (Type type in assembly.GetTypes().Where(type => typeof(IApiController).IsAssignableFrom(type)))
            {
                String name = type.Name;
                if(type.GetCustomAttribute<PreRouteAttribute>() != null)
                {
                    name = type.GetCustomAttribute<PreRouteAttribute>().PreRouteName;
                }
                if (S_controllerTypes.ContainsKey (name)) the throw  new new Exception ($ " {} presence of the same name in the name of the route, the route keep unique " ); 
                s_controllerTypes.Add (name, type); 
                s_actionCache.Add (type, new new ActionCache (type )); 
            } 
        } 
        public  static the type getController (String controllername) 
        { 
            IF (s_controllerTypes.ContainsKey (controllername))! the throw  new new Exception ( " not corresponding to the type of route " );
             return s_controllerTypes [controllername]; 
        }
        ///  <Summary> 
        /// Get the corresponding routing method delegate value
         ///  </ Summary> 
        ///  <param name = "controllername"> </ param> 
        ///  <param name = "ActionName"> </ param> 
        ///  <Returns> </ Returns> 
        public  static Func <IApiController, Object [], Object> the GetMethod (the Type Controller, String ActionName) 
        { 
            IF (! s_actionCache.ContainsKey (Controller)) the throw  new new Exception ( " type corresponding to no route " ); 
            ActionCache Cache = s_actionCache [Controller];
             IF ! (Cache.ContainsKey(actionname)) ContainsKey(actionname)) throw new Exception("没有此路由对应的方法");
            return cache.GetMethodInfo(actionname);
        }
    }
    public class ActionCache
    {
        private Dictionary<String, MethodInfo> m_methodinfo;
        private Dictionary<MethodInfo, Func<IApiController, Object[], Object>> m_FuncCache ;
        public MethodInfo this[String name]
        {
            get
            {
                return m_methodinfo[name];
            }
        }
        public Boolean ContainsKey(String name)
        {
            return m_methodinfo.ContainsKey(name);
        }
        private Object m_lock = new Object();
        /// <summary>
        /// 可以考虑延迟加载
        /// </summary>
        /// <param name="type"></param>
        public ActionCache(Type type)
        {
            m_methodinfo = new Dictionary<String, MethodInfo>();
            m_FuncCache = new Dictionary<MethodInfo, Func<IApiController, object[], object>>();
            foreach(MethodInfo info in type.GetMethods())
            {
                String name = info.Name;
                if(info.GetCustomAttribute<RouteAttribute>() != null)
                {
                    name = info.GetCustomAttribute<RouteAttribute>().RouteName;
                }
                if (m_methodinfo.ContainsKey(name)) throw new Exception($"{type.Name}中{name}重复,请保持路径唯一");
                m_methodinfo.Add(name, info);
            }
        }
        /// <Summary> 
        /// Gets the name corresponding to an order through
         ///  </ Summary> 
        ///  <param name = "MethodInfo"> </ param> 
        ///  <Returns> IApiController: performing the method of delivery, Object [] : method parameters, Object return value, void null </ returns> 
        public Func <IApiController, Object [], Object> GetMethodInfo (String methodName) 
        { 
            the MethodInfo MethodInfo = m_methodinfo [methodName];
             IF (! m_FuncCache.ContainsKey (MethodInfo)) 
            { 
                Lock (m_lock) 
                { 
                    IF (! m_FuncCache.ContainsKey(methodInfo))
                    {
                        m_FuncCache.Add(methodInfo, CreateExecutor(methodInfo));
                    }
                }
            }
            return m_FuncCache[methodInfo];
        }
        private Func<Object, Object[], Object> CreateExecutor(MethodInfo methodInfo)
        {
            ParameterExpression target = Expression.Parameter(typeof(Object), "target");
            ParameterExpression arguments = Expression.Parameter(typeof(Object[]), "arguments");
            List<Expression> parameters = new List<Expression>();
            ParameterInfo[] paramInfos = methodInfo.GetParameters();
            for (Int32 i = 0; i < paramInfos.Length; i++)
            {
                ParameterInfo paramInfo = paramInfos[i];
                BinaryExpression getElementByIndex = Expression.ArrayIndex(arguments, Expression.Constant(i));
                UnaryExpression converToParamterType = Expression.Convert(getElementByIndex, paramInfo.ParameterType);
                parameters.Add(converToParamterType);
            }
            UnaryExpression instanceCast = Expression.Convert(target, methodInfo.ReflectedType);
            MethodCallExpression methodCall = Expression.Call(instanceCast, methodInfo, parameters);
            UnaryExpression converToObjectType = Expression.Convert(methodCall, typeof(Object));
            return Expression.Lambda<Func<Object, Object[], Object>>(converToObjectType, target, arguments).Compile();
        }
    }
View Code

 Note: This global controller to do a cache, will not be limited to the parameters passed to determine which method of use, allowing only a single interface exists, namely

1. If the name of the same type in different spaces, must have different characteristics preroute defined,

2. If a class method overloading, have characteristics defined uniquely identified by the route

3. The expression tree is by creating a delegate, the current delivery methods corresponding controller object calls

Above framework is not only belong to a single function call method to achieve, and do the optimization, the next part of the framework to achieve API

Four: API implemented first be defined and the value of the standard transmission protocol,

After 4.1 Information to be transmitted is determined, remove all other additional information are as follows, for the sake of simplicity, first of all to save a string:

Viewed (UrlReferrer), go URL (request interface),

Parameters (a / b? Query = query value resolution in 1) URL request, body stored value (frombody), the request header contains parameters (Headers)

After processing all the requests, the return value information

 

public  class the HttpRequest 
    { 
        ///  <Summary> 
        /// come
         ///  </ Summary> 
        public String {UrlReferrer GET ;}
         ///  <Summary> 
        /// go
         ///  </ Summary> 
        public String {Uri GET ; SET ;}
         ///  <Summary> 
        /// Uri processing request parameter
         ///  </ Summary> 
        public String the QueryParams { GET ; SET ;}
         ///  <Summary> 
        ///Content request
         ///  </ Summary> 
        public String RequestContent { GET ; SET ;}
         ///  <Summary> 
        /// request header parameter
         ///  </ Summary> 
        public String Headers { GET ; SET ;} 
    } 
    public  class the HttpResponse 
    { 
        ///  <Summary> 
        /// content returned
         ///  </ Summary> 
        public String ResponseContent { GET ; SET ;} 
    } 
    public  class the HttpContext 
    {
        public HttpRequest Request { get; }
        public HttpResponse Response { get; }
    }
View Code

 

Here simply for display use, in the late write a MQ middleware time, doing the expansion. HttpContext only show

4.2. Http request to make a unified interface processing

    public class UrlRoutingModule : IRoutingModule
    {
        public void Init(HttpBaseContext context)
        {
            
        }
    }

Five: collected by routing template

5.1 when writing the API, will add a defaultapi matching routes Note: This resolution rules implemented in the MVC

    
 RouteConfig.RegisterRoutes(RouteTable.Routes);
public static class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); } }

5.2. Consider the function and implementation of this template

1. Because there may be multiple default matching and execution is the order of the loading process, so there is a static class collection RouteTable global template routing

2. route and a corresponding default route route values ​​(Route RouteData) and collecting Collection

3.Route achieve routing resolution, and provide RouteData value and provide an implementation

4. Route when parsing the route generated by DefaultRouterHandler to handle for subsequent processing RouteData

5. By analyzing routing DefaultApiHandler, the controller generates a corresponding

6, the current action was analyzed by the controller, and bind route

7. The method of retrieval of the currently executed, obtain the value returned by the object, and return values ​​are processed

FIG substantially procedure call model

 

 6: implementation code: See the address corresponding github

https://github.com/BestHYC/WebAPISolution.git

7. Examples :( ready corresponding json serialized parse assignment can paste, FIG fancy)

 

Guess you like

Origin www.cnblogs.com/yichaohong/p/11579598.html