一、背景
因為需要做金蝶ERP的二次開發,金蝶ERP的開放性真是不錯,但是二次開發金蝶一般使用引用BOS.dll的方式,這個dll對newtonsoft.json.dll這個庫是強引用,必須要用4.0版本,而asp.net mvc的webapi client對newtonsoft.json.dll的最低版本是6.0.這樣就不能用熟悉的webapi client開發了。金蝶ERP據說支持無dll引用方式,標准的webapi方式,但官方支持有限,網上的文檔也有限且參考意義不大。所以最后把目光聚集在自建簡單httpserver上,最好是用.net 4作為運行時。在此感謝“C#基於websocket-sharp實現簡易httpserver(封裝)”作者的分享,了解到這個開源組件。
二、定制和修改
- 1、修改“注入控制器到IOC容器”的方法適應現有項目的解決方案程序集划分
- 2、修改“響應HttpPost請求”時方法參數解析和傳遞方式,增加了“FromBoby”自定義屬性來標記參數是個數據傳輸對象。
- 3、擴展了返回值類型,增加了HtmlResult和ImageResult等類型
代碼:
1)、修改“注入控制器到IOC容器”的方法
/// <summary> /// 注入控制器到IOC容器 /// </summary> /// <param name="builder"></param> public void InitController(ContainerBuilder builder) { Assembly asb = Assembly.Load("Business.Controller"); if (asb != null) { var typesToRegister = asb.GetTypes() .Where(type => !string.IsNullOrEmpty(type.Namespace)) .Where(type => type.BaseType == typeof(ApiController) || type.BaseType.Name.Equals("K3ErpBaseController")); if (typesToRegister.Count() > 0) { foreach (var item1 in typesToRegister) { builder.RegisterType(item1).PropertiesAutowired(); foreach (var item2 in item1.GetMethods()) { IExceptionFilter _exceptionFilter = null; foreach (var item3 in item2.GetCustomAttributes(true)) { Attribute temp = (Attribute)item3; Type type = temp.GetType().GetInterface(typeof(IExceptionFilter).Name); if (typeof(IExceptionFilter) == type) { _exceptionFilter = item3 as IExceptionFilter; } } MAction mAction = new MAction() { RequestRawUrl = @"/" + item1.Name.Replace("Controller", "") + @"/" + item2.Name, Action = item2, TypeName = item1.GetType().Name, ControllerType = item1, ParameterInfo = item2.GetParameters(), ExceptionFilter = _exceptionFilter }; dict.Add(mAction.RequestRawUrl, mAction); } } } } container = builder.Build(); }
2)、修改“響應HttpPost請求”時方法參數解析和傳遞方式,增加了“FromBoby”自定義屬性;增加了HtmlResult和ImageResult等類型
/// <summary> /// 響應HttpPost請求 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Httpsv_OnPost(object sender, HttpRequestEventArgs e) { MAction requestAction = new MAction(); string content = string.Empty; try { byte[] binaryData = new byte[e.Request.ContentLength64]; content = Encoding.UTF8.GetString(GetBinaryData(e, binaryData)); string action_name = string.Empty; if (e.Request.RawUrl.Contains("?")) { int index = e.Request.RawUrl.IndexOf("?"); action_name = e.Request.RawUrl.Substring(0, index); } else action_name = e.Request.RawUrl; if (dict.ContainsKey(action_name)) { requestAction = dict[action_name]; object first_para_obj; object[] para_values_objs = new object[requestAction.ParameterInfo.Length]; //沒有參數,默認為空對象 if (requestAction.ParameterInfo.Length == 0) { first_para_obj = new object(); } else { int para_count = requestAction.ParameterInfo.Length; for (int i = 0; i < para_count; i++) { var para = requestAction.ParameterInfo[i]; if (para.GetCustomAttributes(typeof(FromBodyAttribute), false).Any()) { //反序列化Json對象成數據傳輸對象 var dto_object = para.ParameterType; para_values_objs[i] = JsonConvert.DeserializeObject(content, dto_object); } else { //參數含有FromBodyAttribute的只能有一個,且必須是第一個 int index = e.Request.RawUrl.IndexOf("?"); if (e.Request.RawUrl.Contains("&")) { bool existsFromBodyParameter = false; foreach (var item in requestAction.ParameterInfo) { if (item.GetCustomAttributes(typeof(FromBodyAttribute), false).Any()) { existsFromBodyParameter = true; break; } } string[] url_para = e.Request.RawUrl.Substring(index + 1).Split("&".ToArray(), StringSplitOptions.RemoveEmptyEntries); if (existsFromBodyParameter) para_values_objs[i] = url_para[i - 1].Replace(para.Name + "=", ""); else para_values_objs[i] = url_para[i].Replace(para.Name + "=", ""); } else { para_values_objs[i] = e.Request.RawUrl.Substring(index + 1).Replace(para.Name + "=", ""); } } } } var action = container.Resolve(requestAction.ControllerType); var action_result = requestAction.Action.Invoke(action, para_values_objs); if (action_result == null) { e.Response.StatusCode = 204; } else { e.Response.StatusCode = 200; if (requestAction.Action.ReturnType.Name.Equals("ApiActionResult")) { e.Response.ContentType = "application/json"; byte[] buffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(action_result)); e.Response.WriteContent(buffer); } if (requestAction.Action.ReturnType.Name.Equals("agvBackMessage")) { e.Response.ContentType = "application/json"; byte[] buffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(action_result)); e.Response.WriteContent(buffer); } else if (requestAction.Action.ReturnType.Name.Equals("HtmlResult")) { HtmlResult result = (HtmlResult)action_result; e.Response.ContentType = result.ContentType; byte[] buffer = result.Encoder.GetBytes(result.Content); e.Response.WriteContent(buffer); } else if (requestAction.Action.ReturnType.Name.Equals("ImageResult")) { ImageResult apiResult = (ImageResult)action_result; e.Response.ContentType = apiResult.ContentType; byte[] buffer = Convert.FromBase64String(apiResult.Base64Content.Split(",".ToArray(), StringSplitOptions.RemoveEmptyEntries).LastOrDefault()); e.Response.WriteContent(buffer); } else { byte[] buffer = Encoding.UTF8.GetBytes(action_result.ToString()); e.Response.WriteContent(buffer); } } } else { e.Response.StatusCode = 404; } } catch (Exception ex) { if (requestAction.ExceptionFilter == null) { byte[] buffer = Encoding.UTF8.GetBytes(ex.Message + ex.StackTrace); e.Response.WriteContent(buffer); e.Response.StatusCode = 500; } else { if (requestAction.ExceptionFilter != null) { if (ex.InnerException != null) { requestAction.ExceptionFilter.OnException(ex.InnerException, e); } else { requestAction.ExceptionFilter.OnException(ex, e); } } } } }
三、體驗概述
- 1、穩,可用性很高,簡單直接。
- 2、使用之后對mvc的底層理解提高很多。
- 3、對Autofac等IOC容器有了新的認識,項目里大量使用了屬性注入方式來解除耦合。