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還有一些其它想法和設計會未加入其中,后續會繼續修繕。且還存在一些未解決的問題,歡迎您能貢獻您的智慧和意見。
