/// <summary> /// Called when the dialog is started and pushed onto the dialog stack. /// </summary> /// <param name="dc">The <see cref="DialogContext"/> for the current turn of conversation.</param> /// <param name="options">Optional, initial information to pass to the dialog.</param> /// <param name="cancellationToken">A cancellation token that can be used by other objects /// or threads to receive notice of cancellation.</param> /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> public override async Task <DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default(CancellationToken)) { if (options is CancellationToken) { throw new ArgumentException($"{nameof(options)} cannot be a cancellation token"); } if (Disabled != null && Disabled.GetValue(dc.State)) { return(await dc.EndDialogAsync(cancellationToken : cancellationToken).ConfigureAwait(false)); } // This is injected for testing, and should not be used for anything else. var client = dc.Context.TurnState.Get <HttpClient>(); var disposeHttpClient = false; if (client == null) { disposeHttpClient = true; //Note: this should also be analyzed once we start using HttpClientFactory. #pragma warning disable CA2000 // Dispose objects before losing scope client = new HttpClient(); #pragma warning restore CA2000 // Dispose objects before losing scope } try { JToken instanceBody = null; if (Body != null) { var(body, err) = Body.TryGetValue(dc.State); if (err != null) { throw new ArgumentException(err); } instanceBody = (JToken)JToken.FromObject(body).DeepClone(); } var instanceHeaders = Headers == null ? null : Headers.ToDictionary(kv => kv.Key, kv => kv.Value.GetValue(dc.State)); var(instanceUrl, instanceUrlError) = Url.TryGetValue(dc.State); if (instanceUrlError != null) { throw new ArgumentException(instanceUrlError); } // Bind each string token to the data in state instanceBody = instanceBody?.ReplaceJTokenRecursively(dc.State); using HttpRequestMessage request = new HttpRequestMessage(new System.Net.Http.HttpMethod(Method.ToString()), instanceUrl); // Set headers if (instanceHeaders != null) { foreach (var unit in instanceHeaders) { request.Headers.TryAddWithoutValidation(unit.Key, unit.Value); } } request.Headers.TryAddWithoutValidation("user-agent", "Mozilla/5.0"); dynamic traceInfo = new JObject(); traceInfo.request = new JObject(); traceInfo.request.method = Method.ToString(); traceInfo.request.url = instanceUrl; HttpResponseMessage response = null; string contentType = ContentType?.GetValue(dc.State) ?? "application/json"; switch (Method) { case HttpMethod.POST: if (instanceBody == null) { response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false); } else { using var postContent = new StringContent(instanceBody.ToString(), Encoding.UTF8, contentType); traceInfo.request.content = instanceBody.ToString(); request.Content = postContent; traceInfo.request.headers = JObject.FromObject(request.Content.Headers.ToDictionary(t => t.Key, t => (object)t.Value?.FirstOrDefault())); response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false); } break; case HttpMethod.PATCH: if (instanceBody == null) { response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false); } else { using var patchContent = new StringContent(instanceBody.ToString(), Encoding.UTF8, contentType); request.Content = patchContent; traceInfo.request.content = instanceBody.ToString(); traceInfo.request.headers = JObject.FromObject(request.Content.Headers.ToDictionary(t => t.Key, t => (object)t.Value?.FirstOrDefault())); response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false); } break; case HttpMethod.PUT: if (instanceBody == null) { response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false); } else { using var putContent = new StringContent(instanceBody.ToString(), Encoding.UTF8, contentType); traceInfo.request.content = instanceBody.ToString(); traceInfo.request.headers = JObject.FromObject(request.Content.Headers.ToDictionary(t => t.Key, t => (object)t.Value?.FirstOrDefault())); request.Content = putContent; response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false); } break; case HttpMethod.DELETE: response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false); break; case HttpMethod.GET: response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false); break; } var requestResult = new Result(response.Headers) { StatusCode = (int)response.StatusCode, ReasonPhrase = response.ReasonPhrase, }; object content = (object)await response.Content.ReadAsStringAsync().ConfigureAwait(false); switch (ResponseType.GetValue(dc.State)) { case ResponseTypes.Activity: var activity = JsonConvert.DeserializeObject <Activity>((string)content); requestResult.Content = JObject.FromObject(activity); await dc.Context.SendActivityAsync(activity, cancellationToken : cancellationToken).ConfigureAwait(false); break; case ResponseTypes.Activities: var activities = JsonConvert.DeserializeObject <Activity[]>((string)content); requestResult.Content = JArray.FromObject(activities); await dc.Context.SendActivitiesAsync(activities, cancellationToken : cancellationToken).ConfigureAwait(false); break; case ResponseTypes.Json: // Try set with JOjbect for further retrieving try { content = JToken.Parse((string)content); } #pragma warning disable CA1031 // Do not catch general exception types (just stringify the content if we can't parse the content). catch #pragma warning restore CA1031 // Do not catch general exception types { content = content.ToString(); } requestResult.Content = content; break; case ResponseTypes.Binary: // Try to resolve binary data var bytes = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false); requestResult.Content = bytes; break; case ResponseTypes.None: default: break; } traceInfo.response = JObject.FromObject(requestResult); // Write Trace Activity for the http request and response values await dc.Context.TraceActivityAsync("HttpRequest", (object)traceInfo, valueType : "Microsoft.HttpRequest", label : Id).ConfigureAwait(false); if (ResultProperty != null) { dc.State.SetValue(ResultProperty.GetValue(dc.State), requestResult); } // return the actionResult as the result of this operation return(await dc.EndDialogAsync(result : requestResult, cancellationToken : cancellationToken).ConfigureAwait(false)); } finally { if (disposeHttpClient) { client.Dispose(); } } }