zoukankan      html  css  js  c++  java
  • QuickWebApi:使用Lambada方式,完成对WebApi的开发和调用。

    QuickWebApi

    目的:使用Lambada方式,完成对WebApi的开发和调用。


    缘由:为了解耦服务和展现,将越来越多的使用WebApi提供各种服务;随着服务的细化,WebApi的接口将越来越多,成百上千。如何方便的管理和调用规模庞大的WebApi接口成了开发者头疼的问题。

    设计:通过自定义的QuickWebApiAttribute来对业务接口进行规范和说明,并生成配置文件;可以通过修改配置文件,达成对WebApi的地址的调整而不用修改代码。

    效果:除了重新搭建的系统可以使用外,对于一些其它语言(如java等)提供的webapi,只需要定义出相应的业务接口,便可以使用

    原理:通过HttpClient类,使用Json作为请求和响应的数据格式实现。并通过定义相应的delegate,完成Lambada方式的调用,目前delegate只支持三个参数以内的接口(且参数类型目前仅支持int,long,string),如果超过三个参数或者其他的复杂类型,则应通过自定义对象的方式完成。

        public delegate IHttpActionResult apiaction();
        public delegate IHttpActionResult apiaction_l(long args);
        public delegate IHttpActionResult apiaction_ll(long args1, long args2);
        public delegate IHttpActionResult apiaction_li(long args1, int arg2);
        public delegate IHttpActionResult apiaction_ls(long args1, string args2);
        public delegate IHttpActionResult apiaction_i(int args1);
        public delegate IHttpActionResult apiaction_ii(int args1, int args2);
        public delegate IHttpActionResult apiaction_is(int args1, string args2);
        public delegate IHttpActionResult apiaction_il(int args1, long args2);
        public delegate IHttpActionResult apiaction_si(string args1, int args2);
        public delegate IHttpActionResult apiaction_ss(string args1, string args2);
        public delegate IHttpActionResult apiaction_sl(string args1, long args2);
        public delegate IHttpActionResult apiaction_sss(string args1, string args2, string args3);
        public delegate IHttpActionResult apiaction_o<treq>(treq data) where treq : class,new();
    

     对于delegate的Lambada化和参数的传递解析实现:

            result<tresp> _invoke_data<treq>(Expression exp, treq data) where treq : class
            {
                var method = ((exp as UnaryExpression).Operand as MethodCallExpression);
                string code = ((method.Object as ConstantExpression).Value as MethodInfo).Name;
    
                foreach (var m in method.Arguments)
                {
                    if (m.Type == typeof(T))
                    {
                        var attr = m.Type.GetCustomAttribute<QuickWebApiAttribute>();
                        if (attr != null)
                        {
                            return new invoker(build_server(attr.service)).Excute<tresp>(code, data);
                        }
                    }
                }
                return new result<tresp>(-1, "未能找到合适的api定义");
            }
    

     如果参数不是对象(需要注意的是,虽然处理了对DataTime类型的处理,但原则上不应出现该类型参数),则:

            result<tresp> _invoke(Expression exp, params object[] args)
            {
                var method = ((exp as UnaryExpression).Operand as MethodCallExpression);
                string code = ((method.Object as ConstantExpression).Value as MethodInfo).Name;
    
                foreach (var m in method.Arguments)
                {
                    if (m.Type == typeof(T))
                    {
                        var attr = m.Type.GetCustomAttribute<QuickWebApiAttribute>();
                        StringBuilder sb = new StringBuilder();
                        var pis = m.Type.GetMethod(code).GetParameters();
    
                        for (int i = 0; i < pis.Length; i++)
                        {
                            sb.AppendFormat("{0}={1}&", pis[i].Name, args[i] is DateTime ? ((DateTime)args[i]).ToString("yyyy-MM-dd HH:mm:ss") : args[i]);
                        }
    
                        if (attr != null)
                        {
                            return new invoker(build_server(attr.service)).Excute<tresp>(code, sb.ToString());
                        }
                    }
                }
                return new result<tresp>(-1, "未能找到合适的api定义");
            }
    

     以上是WebApi访问Lambada化的核心代码块。如需了解更多,在文章结尾有我的源码地址连接。

    接下来介绍一下QuickWebApi在实际开发过程中的使用,包括接口服务端和接口调用端的样例代码。

    服务业务接口及模型设计:

        //数据模型
        public class customer
        {
            public int id { get; set; }
            public long timestamp { get; set; }
            public string name { get; set; }
            public int age { get; set; }
            public DateTime birthday { get; set; }
            public bool state { get; set; }
        }
        //业务接口动态库声明
        [assembly: QuickWebApiDll("customer", "http://localhost:11520")]
        //业务接口声明,接口中的返回值为IHttpActionResult,以便更好的处理HTTP响应
        [QuickWebApi("customer", "api/customer_service", "用户管理")]
        public interface icustomer
        {
            [QuickWebApi(MethodType.HTTPGET, "用户列表", "列举用户信息")]
            IHttpActionResult list();
            [QuickWebApi(MethodType.HTTPGET)]
            IHttpActionResult info(int customerid);
            [QuickWebApi(MethodType.HTTPPOST)]
            IHttpActionResult update(int id, string name);
            [QuickWebApi(MethodType.HTTPDEL)]
            IHttpActionResult del(int id);
            [QuickWebApi(MethodType.HTTPPUT)]
            IHttpActionResult save(customer customer);
        }
        //业务接口实现,设置路由机制
        [Route("api/customer_service/{action}/")]
        public class customerController : ApiController, icustomer
        {
            [HttpGet]
            public IHttpActionResult list()
            {
                return Ok(new result(0, null, DB.customers.ToList().Count, DB.customers.ToList()));
            }
    
            [HttpGet]
            public IHttpActionResult info(int customerid)
            {
                return Ok(new result(DB.customers.SingleOrDefault(c => c.id == customerid)));
            }
    
            [HttpPost]
            public IHttpActionResult update(int id, string name)
            {
                var cust = DB.customers.SingleOrDefault(c => c.id == id);
                cust.name = name;
                return Ok(new result());
            }
    
            [HttpDelete]
            public IHttpActionResult del(int id)
            {
                DB.customers.RemoveAll(c => c.id == id);
                return Ok(new result());
            }
    
            [HttpPut]
            public IHttpActionResult save(customer customer)
            {
                DB.customers.RemoveAll(c => c.id == customer.id);
                DB.customers.Add(customer);
                return Ok(new result());
            }
        }
        
    

    调用端相应的配置和加载:

        //在服务启动时调用,一般在global中
        webapifactory.Instance.Build_Apis();
        webapifactory.Instance.Load_Apis();
    
        //配置文件格式(*.xml)如下(每个业务接口生成一个独立的配置文件,便于维护):
        <?xml version="1.0" encoding="utf-16"?>
        <ArrayOfWebApiNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <WebApiNode>
            <Name>用户管理</Name>
            <Title>QuickWebApi.Sample.Apis</Title>
            <Version>1.0.0.0</Version>
            <Service>customer</Service>
            <Route>api/customer_service</Route>
            <Uri>http://localhost:11520</Uri>
            <Actions>
              <WebApiMethod>
                <Method>HTTPGET</Method>
                <Action>list</Action>
                <Name>用户列表</Name>
                <Comment>列举用户信息</Comment>
              </WebApiMethod>
              <WebApiMethod>
                <Method>HTTPGET</Method>
                <Action>info</Action>
              </WebApiMethod>
              <WebApiMethod>
                <Method>HTTPPOST</Method>
                <Action>update</Action>
              </WebApiMethod>
              <WebApiMethod>
                <Method>HTTPDEL</Method>
                <Action>del</Action>
              </WebApiMethod>
              <WebApiMethod>
                <Method>HTTPPUT</Method>
                <Action>save</Action>
              </WebApiMethod>
            </Actions>
          </WebApiNode>
        </ArrayOfWebApiNode>
    
        //并同时生产简单的业务接口描述文件(*.txt):
        001,用户管理:QuickWebApi.Sample.Apis-1.0.0.0-
        001,http://localhost:11520/api/customer_service/list,用户列表,列举用户信息
        002,http://localhost:11520/api/customer_service/info,,
        005,http://localhost:11520/api/customer_service/update,,
        006,http://localhost:11520/api/customer_service/del,,
        008,http://localhost:11520/api/customer_service/save,,
    

       调用端使用方式

        public class HomeController : Controller
        {
            public object customers()
            {
                var ret = new webapi<icustomer>().invoke(i => i.list);
                return ret;
            }
            public JsonResult customer_list()
            {
                var ret = new webapi<icustomer, List<customer>>().invoke(i => i.list);
                List<object> custs = new List<object>();
                foreach (var cust in ret.data)
                {
                    custs.Add(new { id = cust.id, name = cust.name, age = cust.age });
                }
                return Json(custs, JsonRequestBehavior.AllowGet);
            }
            public object info()
            {
                var ret = new webapi<icustomer>().invoke(i => i.info, 4);
                return ret;
            }
            public object update()
            {
                var ret = new webapi<icustomer>().invoke(i => i.update, 3, "new name");
                return ret;
            }
            public object save()
            {
                var cust = new customer() { id = 3, name = "new name", age = 22, timestamp = DateTime.Now.Ticks, birthday = DateTime.Now.AddYears(-10) };
                var ret = new webapi<icustomer>().invoke(i => i.save, cust);
                return ret;
            }
            public object delete()
            {
                var ret = new webapi<icustomer>().invoke(i => i.del, 4);
                return ret;
            }
        }
    

    其它说明及注意事项:

        //invoke方法的返回结果的数据结构如下:
        public class result<T> where T : class, new()
        {
            //如果通讯不正常,则为返回的HTTP状态吗;否则为服务端返回的状态吗,默认值为0(正常)
            public int errcode { get; set; }    
            //服务端返回的信息
            public string errmsg { get; set; }
            //服务端返回的复杂数据结构,通过Json进行传输
            public T data { get; set; }
            //如果需要返回整型
            public long id { get; set; }
            //服务器端时间
            public DateTime time { get; set; }
         }
         //如果页面上通过ajax直接调用WebApi接口,则需要在服务端注册JsonFormatter,则ajax可以直接得到result的json格式:
            public static void Register(HttpConfiguration config)
            {
                // Web API configuration and services
                webapifactory.Instance.Register_JsonFormatter(config);
    
                // Web API routes
                config.MapHttpAttributeRoutes();
    
                config.Routes.MapHttpRoute(
                    name: "DefaultApi",
                    routeTemplate: "api/{controller}/{id}",
                    defaults: new { id = RouteParameter.Optional }
                );
    
            }
    

    感谢您的阅读。源码
    目前QuickWebApi还有一些其它想法和设计会未加入其中,后续会继续修缮。且还存在一些未解决的问题,欢迎您能贡献您的智慧和意见。

  • 相关阅读:
    C# FromBase64String 解码换行问题
    Func与Action
    C# 委托及各种写法
    DNS解析过程和域名收敛、域名发散、SPDY应用
    基础数据类型长度
    C#静态常量和动态常量的区别
    dynamic和var的区别
    ADO.NET中的五个主要对象
    Linux 守护进程创建原理及简易方法
    利用/dev/urandom文件创建随机数
  • 原文地址:https://www.cnblogs.com/winhu/p/5548875.html
Copyright © 2011-2022 走看看