/// <summary>准备上下文</summary> /// <param name="session"></param> /// <param name="action"></param> /// <param name="args"></param> /// <param name="api"></param> /// <returns></returns> protected virtual ControllerContext Prepare(IApiSession session, String action, Packet args, ApiAction api) { //var enc = Host.Encoder; var enc = session["Encoder"] as IEncoder ?? Host.Encoder; // 当前上下文 var ctx = ControllerContext.Current; if (ctx == null) { ctx = new ControllerContext(); ControllerContext.Current = ctx; } ctx.Action = api; ctx.ActionName = action; ctx.Session = session; ctx.Request = args; // 如果服务只有一个二进制参数,则走快速通道 if (!api.IsPacketParameter) { // 不允许参数字典为空 var dic = args == null || args.Total == 0 ? new NullableDictionary <String, Object>(StringComparer.OrdinalIgnoreCase) : enc.DecodeParameters(action, args); ctx.Parameters = dic; // 准备好参数 var ps = GetParams(api.Method, dic, enc); ctx.ActionParameters = ps; } return(ctx); }
/// <summary>订阅主题</summary> /// <param name="user"></param> /// <param name="session"></param> /// <returns></returns> public Boolean Add(String user, IApiSession session) { if (Subscribers.ContainsKey(user)) { return(false); } var scb = new Subscriber { User = user, Session = session }; Subscribers[user] = scb; var ds = session as IDisposable2; if (ds != null) { ds.OnDisposed += (s, e) => Remove(user); } #if DEBUG var msg = new Message { Sender = user, Body = "上线啦" }; Enqueue(msg); #endif return(true); }
/// <summary>执行过滤器</summary> /// <param name="host"></param> /// <param name="session"></param> /// <param name="msg"></param> /// <param name="issend"></param> internal static void ExecuteFilter(this IApiHost host, IApiSession session, IMessage msg, Boolean issend) { var fs = host.Filters; if (fs.Count == 0) { return; } // 接收时需要倒序 if (!issend) { fs = fs.Reverse().ToList(); } var ctx = new ApiFilterContext { Session = session, Packet = msg.Payload, Message = msg, IsSend = issend }; foreach (var item in fs) { item.Execute(ctx); } msg.Payload = ctx.Packet; }
private IMessage OnProcess(IApiSession session, IMessage msg) { var enc = Encoder; var action = ""; Object result = null; var code = 0; try { if (!ApiHostHelper.Decode(msg, out action, out _, out var args)) { return(null); } result = Handler.Execute(session, action, args); } catch (Exception ex) { ex = ex.GetTrue(); // 支持自定义错误 if (ex is ApiException aex) { code = aex.Code; result = ex?.Message; } else { code = 500; result = ex?.Message; } } // 单向请求无需响应 if (msg is DefaultMessage dm && dm.OneWay) { return(null); } // 编码响应数据包,二进制优先 var pk = result as Packet; if (pk == null) { pk = enc.Encode(action, code, result); } pk = ApiHostHelper.Encode(action, code, pk); // 构造响应消息 var rs = msg.CreateReply(); rs.Payload = pk; if (code > 0 && rs is DefaultMessage dm2) { dm2.Error = true; } return(rs); }
/// <summary> /// Usecase: Investors provides a Purchase Advice document after purchasing a closed loan /// 1. Query for loan /// 2. Acquire Shared Lock /// 3. Create Document /// 4. Upload Attachments /// 5. Assign Attachments to Document /// 6. Release Shared Lock /// </summary> /// <param name="session"></param> /// <returns></returns> private static async Task PurchaseAdvise(IApiSession session) { // 1. Query for loan var loanId = await QueryForLoan(session, Configuration.LoanNumber, Configuration.LoanFolder); // 2. Acquire Shared Lock using (await AcquireSharedLock(session, loanId)) { // 3. Create Document var document = await CreateDocument(session, loanId); /** * We have 2 attachemnts ready to be uploaded in the directory {DIR_OF_THIS_EXE}\res\attachments * "Sample BBT PA.pdf" * "Sample Purchase Advice.pdf" */ var attachmentsDirPath = Path.Combine(Directory.GetCurrentDirectory(), @"res\attachments"); // 4. Upload Attachments var attachmentIds = await UploadAttachments(session, loanId, attachmentsDirPath); // 5. Assign Attachments to Document await AssignAttachmentsToDocument(session, loanId, document.Id, attachmentIds); } // 6. Release Shared Lock }
private Packet ProcessHandler(IApiSession session, Packet pk) { var enc = Encoder; // 这里会导致二次解码,因为解码以后才知道是不是请求 var dic = enc.Decode(pk); var action = ""; Object args = null; if (!enc.TryGet(dic, out action, out args)) { return(null); } Object result = null; var code = 0; try { result = Handler.Execute(session, action, args as IDictionary <String, Object>).Result; } catch (Exception ex) { var aex = ex as ApiException; code = aex != null ? aex.Code : 500; result = ex; } // 编码响应数据包 return(enc.Encode(code, result)); }
private static async Task <ExportJobStatusContract> CreateExportJob(IApiSession session, string loanId, FileAttachmentReferenceContract[] attachments) { var exportJobsApi = session.GetApi <ExportJobsApi>(); // Build the payload to fetch documents using eFolder export job api var exportJobInput = new ExportJobContract() { AnnotationSettings = new AnnotationSettingsContract() { Visibility = new VisibilityType[] { VisibilityType.Internal, VisibilityType.Private, VisibilityType.Public } }, Entities = attachments.Select(attachment => new EFolderEntityRef() { EntityId = attachment.EntityId, EntityType = EFolderEntityType.Attachment }).ToArray(), Source = new EFolderEntityRef() { EntityId = loanId, EntityType = EFolderEntityType.Loan } }; return(await exportJobsApi.CreateExportJob(exportJobInput)); }
/// <summary>处理请求消息。重载、事件、控制器 共三种消息处理方式</summary> /// <param name="session"></param> /// <param name="msg"></param> /// <returns></returns> protected virtual IMessage OnReceive(IApiSession session, IMessage msg) { // 优先调用外部事件 if (Received != null) { var e = new ApiMessageEventArgs { Session = session, Message = msg }; Received(this, e); if (e.Handled) { return(e.Message); } } var pk = msg.Payload; // 如果外部事件未处理,再交给处理器 pk = ProcessHandler(session, pk); // 封装响应消息 var rs = msg.CreateReply(); rs.Payload = pk; return(rs); }
/// <summary>调用</summary> /// <param name="host"></param> /// <param name="session"></param> /// <param name="action">服务操作</param> /// <param name="args">参数</param> /// <param name="flag">标识</param> /// <returns></returns> public static Boolean Invoke(IApiHost host, IApiSession session, String action, Object args, Byte flag = 0) { if (session == null) { return(false); } host.StatSend?.Increment(); // 编码请求 var pk = EncodeArgs(host.Encoder, action, args); // 构造消息 var msg = new DefaultMessage { OneWay = true, Payload = pk, }; if (flag > 0) { msg.Flag = flag; } return(session.Send(msg)); }
private Packet ProcessHandler(IApiSession session, Packet pk) { var enc = Encoder; var action = ""; Object result = null; var code = 0; var seq = -1; try { // 这里会导致二次解码,因为解码以后才知道是不是请求 var dic = enc.Decode(pk); // 请求响应,由code决定 if (dic.ContainsKey("code")) { return(null); } Object args = null; //if (!enc.TryGet(dic, out action, out args)) return null; Object obj = null; if (!dic.TryGetValue("action", out obj)) { return(null); } // 参数可能不存在 dic.TryGetValue("args", out args); action = obj + ""; // 针对Http前端Json,可能带有序列号 if (dic.TryGetValue("seq", out obj)) { seq = obj.ToInt(); } result = Handler.Execute(session, action, args as IDictionary <String, Object>); } catch (Exception ex) { ex = ex.GetTrue(); var aex = ex as ApiException; code = aex != null ? aex.Code : 500; result = ex?.Message; } // 编码响应数据包 //return enc.Encode(code, result); if (seq >= 0) { return(enc.Encode(new { action, code, result, seq })); } return(enc.Encode(new { action, code, result })); }
private IMessage OnProcess(IApiSession session, IMessage msg) { var enc = Encoder; var action = ""; Object result = null; var code = 0; try { if (!enc.Decode(msg, out action, out _, out var args)) { return(null); } result = OnProcess(session, action, args); } catch (Exception ex) { ex = ex.GetTrue(); if (ShowError) { WriteLog("{0}", ex); } // 支持自定义错误 if (ex is ApiException aex) { code = aex.Code; result = ex?.Message; } else { code = 500; result = ex?.Message; } } // 单向请求无需响应 if (msg.OneWay) { return(null); } //// 编码响应数据包,二进制优先 //if (!(result is Packet pk)) pk = enc.Encode(action, code, result); //pk = enc.Encode(action, code, pk); //// 构造响应消息 //var rs = msg.CreateReply(); //rs.Payload = pk; //if (code > 0) rs.Error = true; var rs = enc.CreateResponse(msg, action, code, result); return(rs); }
/// <summary>处理消息</summary> /// <param name="session"></param> /// <param name="msg"></param> /// <returns></returns> IMessage IApiHost.Process(IApiSession session, IMessage msg) { if (msg.Reply) { return(null); } StatReceive?.Increment(); return(OnProcess(session, msg)); }
/// <summary>准备上下文,可以借助Token重写Session会话集合</summary> /// <param name="session"></param> /// <param name="action"></param> /// <param name="args"></param> /// <param name="api"></param> /// <param name="msg">消息内容,辅助数据解析</param> /// <returns></returns> protected virtual ControllerContext Prepare(IApiSession session, String action, Packet args, ApiAction api, IMessage msg) { //var enc = Host.Encoder; var enc = session["Encoder"] as IEncoder ?? Host.Encoder; // 当前上下文 var ctx = ControllerContext.Current; if (ctx == null) { ctx = new ControllerContext(); ControllerContext.Current = ctx; } ctx.Action = api; ctx.ActionName = action; ctx.Session = session; ctx.Request = args; // 如果服务只有一个二进制参数,则走快速通道 if (!api.IsPacketParameter) { // 不允许参数字典为空 var dic = args == null || args.Total == 0 ? new NullableDictionary <String, Object>(StringComparer.OrdinalIgnoreCase) : enc.DecodeParameters(action, args, msg); ctx.Parameters = dic; session.Parameters = dic; // 令牌,作为参数或者http头传递 if (dic.TryGetValue("Token", out var token)) { session.Token = token + ""; } if (session.Token.IsNullOrEmpty() && msg is HttpMessage hmsg && hmsg.Headers != null) { // post、package、byte三种情况将token 写入请求头 if (hmsg.Headers.TryGetValue("x-token", out var token2)) { session.Token = token2; } else if (hmsg.Headers.TryGetValue("Authorization", out token2)) { session.Token = token2.TrimStart("Bearer "); } } // 准备好参数 var ps = GetParams(api.Method, dic, enc); ctx.ActionParameters = ps; } return(ctx); }
/// <summary>创建控制器实例</summary> /// <param name="host"></param> /// <param name="session"></param> /// <param name="api"></param> /// <returns></returns> public static Object CreateController(this IApiHost host, IApiSession session, ApiAction api) { var controller = api.Controller; if (controller != null) { return(controller); } controller = api.Type.CreateInstance(); return(controller); }
private static Task <IDisposable> AcquireSharedLock(IApiSession session, string loanId) { var resourceLockApi = session.GetApi <ResourceLocksApi>(); var resourceLockApiInput = new ResourceLockContract() { LockType = ResourceLockType.NGSharedLock, Resource = new EncompassEntityRef() { EntityId = loanId, EntityType = EncompassEntityType.Loan } }; return(resourceLockApi.CreateResourceLock(resourceLockApiInput)); }
/// <summary>处理消息</summary> /// <param name="session"></param> /// <param name="msg"></param> /// <returns></returns> IMessage IApiHost.Process(IApiSession session, IMessage msg) { if (msg.Reply) return null; var st = StatProcess; var sw = st.StartCount(); try { return OnProcess(session, msg); } finally { st.StopCount(sw); } }
/// <summary>创建控制器实例</summary> /// <param name="host"></param> /// <param name="session"></param> /// <param name="api"></param> /// <returns></returns> public static Object CreateController(this IApiHost host, IApiSession session, ApiAction api) { var controller = api.Controller; if (controller != null) { return(controller); } var att = api.Type?.GetCustomAttribute <ApiAttribute>(true); if (att != null && att.IsReusable) { var ts = session["Controller"] as IDictionary <Type, Object>; if (ts == null) { session["Controller"] = ts = new NullableDictionary <Type, Object>(); // 析构时销毁所有从属控制器 var sd = session as IDisposable2; if (sd != null) { sd.OnDisposed += (s, e) => { foreach (var item in ts) { item.Value.TryDispose(); } } } ; } controller = ts[api.Type]; if (controller == null) { controller = ts[api.Type] = api.Type.CreateInstance(); } return(controller); } controller = api.Type.CreateInstance(); return(controller); } }
private static async Task <DocumentContract> CreateDocument(IApiSession session, string loanId) { var documentsApi = session.GetApi <DocumentsApi>(); var documents = await documentsApi.AddDocuments( loanId, new DocumentContract[] { new DocumentContract() { Title = "Purchase Advice", Description = "Purchase Advice Documents" } }, view : "entity"); return(documents.FirstOrDefault()); }
private static Task <AttachmentUploadDataContract> CreateAttachmentUploadUrl(IApiSession session, string loanId, string fileName, long fileSizeInBytes, string contentType) { var attachmentsApi = session.GetApi <AttachmentsApi>(); return(attachmentsApi.GenerateAttachmentUploadUrl( loanId, new AttachmentUploadInputContract() { File = new AttachmentUploadInputContract.FileData() { ContentType = contentType, Name = fileName, Size = fileSizeInBytes }, Title = fileName })); }
/// <summary>处理消息</summary> /// <param name="session"></param> /// <param name="msg"></param> /// <returns></returns> IMessage IApiHost.Process(IApiSession session, IMessage msg) { if (msg.Reply) { return(null); } // 过滤器 this.ExecuteFilter(session, msg, false); var rs = OnReceive(session, msg); // 过滤器 this.ExecuteFilter(session, rs, true); return(rs); }
/// <summary> /// Usecase: Lenders need to export documents from the eFolder after a loan has closed /// 1. Query for Attachments /// 2. Create Export Job /// 3. Poll for status /// 4. Download and save merged file /// </summary> /// <param name="session"></param> /// <returns></returns> private static async Task ExportAttachments(IApiSession session) { var loanId = await QueryForLoan(session, Configuration.LoanNumber, Configuration.LoanFolder); // 1.Query for Attachments var attachments = await GetLatestDocumentAttachmentRefs(session, loanId); // 2. Create Export Job var jobStatus = await CreateExportJob(session, loanId, attachments); // 3. Poll for status jobStatus = await PollExportStatus(session, jobStatus.JobId); // 4. Download and save merged file var downloadFilePath = Path.Combine(Directory.GetCurrentDirectory(), @"res\downloads", Path.GetRandomFileName() + ".pdf"); await DownloadMergedAttachments(session, jobStatus, downloadFilePath); }
private static async Task <string> QueryForLoan(IApiSession session, string loanNumber, string loanFolder) { var loanPipelineApi = session.GetApi <LoanPipelineApi>(); var pipelineItems = await loanPipelineApi.QueryLoanPipeline(new LoanPipelineQueryContract() { Fields = new List <string>() { "LoanId" }, Filter = new QueryCriterionContract() { Operator = BinaryOperator.And, Terms = new QueryCriterionContract[] { new QueryCriterionContract() { CanonicalName = "Loan.LoanNumber", Value = loanNumber, MatchType = MatchType.Exact }, new QueryCriterionContract() { CanonicalName = "Loan.LoanFolder", Value = loanFolder, MatchType = MatchType.Exact } } } }); // Get the loanId from the pipeline items var loanId = pipelineItems.FirstOrDefault()?.LoanId; if (string.IsNullOrEmpty(loanId)) { // Loan not found, exiting the program!! Environment.Exit(1); } return(loanId); }
/// <summary>准备上下文,可以借助Token重写Session会话集合</summary> /// <param name="session"></param> /// <param name="action"></param> /// <param name="args"></param> /// <param name="api"></param> /// <param name="msg"></param> /// <returns></returns> protected override ControllerContext Prepare(IApiSession session, String action, Packet args, ApiAction api, IMessage msg) { var ctx = base.Prepare(session, action, args, api, msg); var token = session.Token; if (!token.IsNullOrEmpty()) { // 第一用户数据是本地字典,用于记录是否启用了第二数据 if (session is ApiNetSession ns && ns.Items["Token"] + "" != token) { var key = GetKey(token); // 采用哈希结构。内存缓存用并行字段,Redis用Set ns.Items2 = Cache.GetDictionary <Object>(key); ns.Items["Token"] = token; } } return(ctx); }
/// <summary>处理消息</summary> /// <param name="session"></param> /// <param name="msg"></param> /// <returns></returns> IMessage IApiHost.Process(IApiSession session, IMessage msg) { if (msg.Reply) { return(null); } //StatReceive?.Increment(); var st = StatProcess; //var sw = st == null ? 0 : Stopwatch.GetTimestamp(); var sw = st.StartCount(); try { return(OnProcess(session, msg)); } finally { //if (st != null) st.Increment(1, (Stopwatch.GetTimestamp() - sw) / 10); st.StopCount(sw); } }
public SessionBasedApiBase(IApiSession session, IApiClient apiClient) : base(apiClient) { _session = ArgumentChecks.IsNotNull(session, nameof(session)); }
public DocumentsApi(IApiSession session, IApiClient apiClient) : base(session, apiClient) { }
/// <summary> /// /// </summary> /// <param name="session"></param> public Devices(IApiSession session) { deviceSession = session; }
/// <summary>调用</summary> /// <param name="host"></param> /// <param name="session"></param> /// <param name="resultType">结果类型</param> /// <param name="action">服务操作</param> /// <param name="args">参数</param> /// <param name="flag">标识</param> /// <returns></returns> public static async Task <Object> InvokeAsync(IApiHost host, IApiSession session, Type resultType, String action, Object args, Byte flag) { if (session == null) { return(null); } host.StatSend?.Increment(); // 编码请求 var enc = host.Encoder; var pk = EncodeArgs(enc, action, args); // 构造消息 var msg = new DefaultMessage { Payload = pk, }; if (flag > 0) { msg.Flag = flag; } var rs = await session.SendAsync(msg); if (rs == null) { return(null); } // 特殊返回类型 if (resultType == typeof(IMessage)) { return(rs); } //if (resultType == typeof(Packet)) return rs.Payload; if (!Decode(rs, out var act, out var code, out var data)) { throw new InvalidOperationException(); } // 是否成功 if (code != 0) { throw new ApiException(code, data.ToStr()); } if (data == null) { return(null); } if (resultType == typeof(Packet)) { return(data); } // 解码结果 var result = enc.Decode(action, data); if (resultType == typeof(Object)) { return(result); } // 返回 return(enc.Convert(result, resultType)); }
/// <summary>执行</summary> /// <param name="session"></param> /// <param name="action"></param> /// <param name="args"></param> /// <returns></returns> public Object Execute(IApiSession session, String action, Packet args) { var api = session.FindAction(action); if (api == null) { throw new ApiException(404, "无法找到名为[{0}]的服务!".F(action)); } // 全局共用控制器,或者每次创建对象实例 var controller = session.CreateController(api); if (controller == null) { throw new ApiException(403, "无法创建名为[{0}]的服务!".F(api.Name)); } if (controller is IApi capi) { capi.Session = session; } var enc = Host.Encoder; IDictionary <String, Object> ps = null; // 上下文 var ctx = new ControllerContext { Controller = controller, Action = api, ActionName = action, Session = session, Request = args, }; // 当前上下文 ControllerContext.Current = ctx; // 如果服务只有一个二进制参数,则走快速通道 var fast = api.IsPacketParameter && api.IsPacketReturn; if (!fast) { // 不允许参数字典为空 var dic = args == null || args.Total == 0 ? new NullableDictionary <String, Object>(StringComparer.OrdinalIgnoreCase) : enc.Decode(action, args) as IDictionary <String, Object>; ctx.Parameters = dic; // 准备好参数 ps = GetParams(api.Method, dic, enc); ctx.ActionParameters = ps; } Object rs = null; try { // 执行动作前的过滤器 if (controller is IActionFilter filter) { filter.OnActionExecuting(ctx); rs = ctx.Result; } // 执行动作 if (rs == null) { if (fast) { var func = api.Method.As <Func <Packet, Packet> >(controller); rs = func(args); } else { // 特殊处理参数和返回类型都是Packet的服务 rs = controller.InvokeWithParams(api.Method, ps as IDictionary); } ctx.Result = rs; } } catch (ThreadAbortException) { throw; } catch (Exception ex) { //rs = OnException(ctx, ex); ctx.Exception = ex.GetTrue(); } finally { // 执行动作后的过滤器 if (controller is IActionFilter filter) { filter.OnActionExecuted(ctx); rs = ctx.Result; } ControllerContext.Current = null; if (ctx.Exception != null && !ctx.ExceptionHandled) { throw ctx.Exception; } } //// 二进制优先通道 //if (api.IsPacketReturn && rs is Packet pk) return pk; return(rs); }
/// <summary>新会话。服务端收到新连接,客户端每次连接或断线重连后,可用于做登录</summary> /// <param name="session">会话</param> /// <param name="state">状态。客户端ISocketClient</param> public virtual void OnNewSession(IApiSession session, Object state) { }