/// <summary> /// 通用 API 请求方法 /// </summary> /// <param name="apiName">API 名称</param> /// <param name="apiParams">API 参数</param> /// <returns>API 返回的数据</returns> public dynamic Request(string apiName, object apiParams) { using (SdkDebug.WriteLineAndIndent("> 请求鉴权服务 API")) { return(DoRequest(apiName, apiParams)); } }
/// <summary> /// 对信道服务器使用 POST 方法推送到业务服务器的报文进行处理 /// </summary> /// <param name="handler">客户使用的信道服务处理实例,用于处理解析之后的包</param> private void HandlePost(ITunnelHandler handler, TunnelHandleOptions options) { using (SdkDebug.WriteLineAndIndent($"> 收到信道服务器 POST 过来的报文 (Time: {DateTime.Now.ToString("HH:mm:ss")})")) { DoHandlePost(handler); } }
/// <summary> /// 通用 API 请求方法 /// </summary> /// <param name="api">API 名称</param> /// <param name="data">API 参数</param> /// <returns>API 返回的数据</returns> public dynamic Request(string path, object data, bool emitSkey = false) { using (SdkDebug.WriteLineAndIndent("> 请求信道服务 API")) { return(DoRequest(path, data, emitSkey)); } }
private dynamic DoRequest(string apiName, object apiParams) { string responseContent; // 请求授权服务器,获取返回报文 try { string requestContent = BuildRequestBody(apiName, apiParams); DateTime start = DateTime.Now; responseContent = Http.Post(APIEndpoint, BuildRequestBody(apiName, apiParams)); DateTime end = DateTime.Now; TimeSpan cost = end - start; using (SdkDebug.WriteLineAndIndent($"POST {APIEndpoint} (Time: {start.ToString("HH:mm:ss")}, Cost: {cost.TotalMilliseconds}ms)")) { using (SdkDebug.WriteLineAndIndent("Requset:")) { SdkDebug.WriteLine(requestContent); } using (SdkDebug.WriteLineAndIndent("Response:")) { SdkDebug.WriteLine(responseContent); } } } catch (Exception error) { using (SdkDebug.WriteLineAndIndent($"POST {APIEndpoint} (ERROR)")) { SdkDebug.WriteLine(error); } throw new HttpRequestException($"请求鉴权 API 失败,网络异常或鉴权服务器错误:{error.Message}", error); } // 解析返回报文 try { dynamic body = JsonConvert.DeserializeObject(responseContent); if (body.returnCode != 0) { throw new AuthorizationAPIException($"鉴权服务调用失败:#{body.returnCode} - ${body.returnMessage}") { Code = body.returnCode }; } return(body.returnData); } catch (JsonException e) { throw new JsonException("鉴权服务器响应格式错误,无法解析 JSON 字符串", e); } catch (Exception e) { throw e; } }
public EmitResult EmitMessage(IEnumerable <string> tunnelIds, string messageType, object messageContent = null) { using (SdkDebug.WriteLineAndIndent("> 发送消息")) { SdkDebug.WriteLine($"> 消息类型:{messageType}"); return(EmitPacket(tunnelIds, "message", new { type = messageType, content = messageContent })); } }
/// <summary> /// 发送数据包到信道服务器 /// </summary> /// <param name="tunnelIds">指定数据包需要送到的信道 ID 列表</param> /// <param name="packetType">指定数据包的类型(message/close)</param> /// <param name="packetContent">指定数据包的内容</param> /// <returns>返回数据包发送结果,可能会包含不可用信道的列表</returns> /// <exception cref="EmitException">网络不可用或者信道服务器不可用</exception> public EmitResult EmitPacket(IEnumerable <string> tunnelIds, string packetType, object packetContent) { if (tunnelIds.Count() == 0) { return new EmitResult() { TunnelIvalidInfos = new List <TunnelInvalidInfo>() } } ; var data = new object[] { new { type = packetType, tunnelIds, content = packetContent == null ? null : JsonConvert.SerializeObject(packetContent) } }; using (SdkDebug.WriteLineAndIndent("> 发送数据包")) { SdkDebug.WriteLine($"> 信道 ID 列表:[{tunnelIds}]"); SdkDebug.WriteLine($"> 包类型: {packetType}"); try { var emitResult = Request("/ws/push", data); IEnumerable <string> invalidTunnels; if (emitResult?.invalidTunnelIds == null) { invalidTunnels = new List <string>(); } else { invalidTunnels = (emitResult?.invalidTunnelIds as JArray).ToList().Select(x => x.Value <string>()); } return(new EmitResult() { TunnelIvalidInfos = invalidTunnels.Select(tunnelId => new TunnelInvalidInfo() { TunnelId = tunnelId, Type = TunnelInvalidType.TunnelHasClosed }) }); } catch (Exception error) { throw new EmitException($"网络不可用或者信道服务器不可用:{error.Message}", error); } } }
/// <remarks> /// 对于推送过来的报文,我们这样进行处理: /// 1. 读取报文内容 /// 2. 解析报文内容成 JSON /// 3. 检查报文签名,如果失败,则忽略报文 /// 4. 解析报文所携带包数据(解析成功直接响应) /// 5. 根据包类型(connect/message/close)给到客户指定的处理实例进行处理 /// </remarks> private void DoHandlePost(ITunnelHandler handler) { #region 1. 读取报文内容 string requestContent = null; try { using (Stream stream = Request.Body) { using (StreamReader reader = new StreamReader(stream, Encoding.UTF8)) { requestContent = reader.ReadToEnd(); } } using (SdkDebug.WriteLineAndIndent("> 读取报文内容:成功")) { SdkDebug.WriteLine(requestContent); } } catch (Exception error) { using (SdkDebug.WriteLineAndIndent("> 读取报文内容:失败")) { SdkDebug.WriteLine(error); } throw new Exception("读取报文失败", error); } #endregion #region 2. 读取报文内容成 JSON 并保存在 body 变量中 var body = new { data = "{encode data}", dataEncode = "json", signature = string.Empty }; try { body = JsonConvert.DeserializeAnonymousType(requestContent, body); SdkDebug.WriteLine("> 解析报文内容:成功"); } catch (JsonException ex) { using (SdkDebug.WriteLineAndIndent("> 解析报文内容:失败")) { SdkDebug.WriteLine(ex); } Response.WriteJson(new { code = 9001, message = "Cant not parse the request body: invalid json" }); return; } #endregion #region 3. 检查报文签名 string data = body.data; string signature = body.signature; string computedSignature = (data + TunnelClient.Key).HashSha1(); if (computedSignature != signature) { using (SdkDebug.WriteLineAndIndent("> 检查签名:失败")) { SdkDebug.WriteLine($"报文签名:{signature},计算结果:{computedSignature},tcKey: {TunnelClient.Key}"); } Response.WriteJson(new { code = 9003, message = "Bad Request - 签名错误" }); return; } using (SdkDebug.WriteLineAndIndent("> 检查签名:成功")) { SdkDebug.WriteLine(signature); } #endregion #region 4. 解析报文中携带的包数据 var packet = new { tunnelId = string.Empty, type = string.Empty, content = string.Empty }; try { packet = JsonConvert.DeserializeAnonymousType(data, packet); } catch (JsonException ex) { using (SdkDebug.WriteLineAndIndent("> 解析包数据:失败")) { SdkDebug.WriteLine(ex); } Response.WriteJson(new { code = 9004, message = "Bad Request - 无法解析的数据包" }); return; } using (SdkDebug.WriteLineAndIndent("> 解析包数据:成功")) { SdkDebug.WriteLine($"tunnelId = {packet.tunnelId}"); SdkDebug.WriteLine($"type = {packet.type}"); SdkDebug.WriteLine($"content = {packet.content}"); } Response.WriteJson(new { code = 0, message = "OK" }); #endregion #region 5. 交给客户处理实例处理报文 var tunnel = Tunnel.GetById(packet.tunnelId); try { using (SdkDebug.WriteLineAndIndent("> 处理数据包:开始")) { switch (packet.type) { case "connect": handler.OnTunnelConnect(tunnel); break; case "message": handler.OnTunnelMessage(tunnel, new TunnelMessage(packet.content)); break; case "close": handler.OnTunnelClose(tunnel); break; } } SdkDebug.WriteLine("> 处理数据包:完成"); } catch (Exception ex) { using (SdkDebug.WriteLineAndIndent("> 处理数据包:发生异常")) { SdkDebug.WriteLine(ex); } } #endregion }
private dynamic DoRequest(string path, object data, bool emitSkey) { string url = TunnelServerUrl + path; string responseContent; // 请求信道服务器,获取返回报文 try { string requestContent = BuildRequestContent(data, emitSkey); DateTime start = DateTime.Now; responseContent = Http.Post(url, requestContent); DateTime end = DateTime.Now; TimeSpan cost = end - start; using (SdkDebug.WriteLineAndIndent($"POST {url} (Time: {start.ToString("HH:mm:ss")}, Cost: {cost.TotalMilliseconds}ms)")) { using (SdkDebug.WriteLineAndIndent("Requset:")) { SdkDebug.WriteLine(requestContent); } using (SdkDebug.WriteLineAndIndent("Response:")) { SdkDebug.WriteLine(responseContent); } } } catch (Exception error) { using (SdkDebug.WriteLineAndIndent($"POST {url} (ERROR)")) { SdkDebug.WriteLine(error); } throw new HttpRequestException("请求信道 API 失败,网络异常或鉴权服务器错误", error); } // 解析返回报文 try { var bodyShape = new { code = 0, message = "OK", data = "{}" }; var body = JsonConvert.DeserializeAnonymousType(responseContent, bodyShape, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Include }); if (body.code != 0) { throw new Exception($"信道服务调用失败:#{body.code} - ${body.message}"); } return(body.data == null ? null : JsonConvert.DeserializeObject(body.data)); } catch (JsonException e) { throw new JsonException("信道服务器响应格式错误,无法解析 JSON 字符串", e); } catch (Exception e) { throw e; } }