private async Task HandlePostRequest(HttpContext context, Metadata metadata, Metadata.Command command) { object[] args; string content; var serializer = new PcaJsonSerializer(metadata); // Read body using (var ms = new MemoryStream()) { await context.Request.Body.CopyToAsync(ms); content = Encoding.UTF8.GetString(ms.ToArray()); } // Get arguments from body string contentType = context.Request.Headers["Content-Type"]; if (contentType != null && contentType.Contains("application/x-www-form-urlencoded")) { args = GetArgsFromQuery(context, content, command); } else if (contentType != null && contentType.Contains("application/json")) { JObject jsonBody = content == null || content.Trim() == "" ? JObject.Parse("{}") : JObject.Parse(content); args = GetArgsFromJObject(context, jsonBody, command, serializer); } else { throw new ServiceException(HttpStatusCode.UnsupportedMediaType, "Allowed Content-Types: application/x-www-form-urlencoded, application/json"); } await ExecuteMethodAsync(context, serializer, command, args); }
public void Configure(Metadata metadata, Metadata.Command command) { this.serializer = new PcaJsonSerializer(metadata); this.metadata = metadata; this.command = command; this.commandInvokerDelegate = BuildCommandInvoker(); this.supportsCancellation = // IObservable<T> command.MethodInfo.ReturnType.IsGenericType && typeof(IObservable <>).IsAssignableFrom(command.MethodInfo.ReturnType.GetGenericTypeDefinition()) || // or one of the parameters is a CancellationToken command.MethodInfo.GetParameters().Any(par => par.ParameterType == typeof(CancellationToken)); }
/// <summary> /// Execute the command, whether it is a sync or async method. /// Async methods are awaited. /// </summary> /// <param name="context">The <see cref="HttpContext"/> of the HTTP-request</param> /// <param name="serializer">A <see cref="PcaJsonSerializer"/> which is used to convert the JSON to values and vice versa according to metadata/versioning</param> /// <param name="command">The command being executed</param> /// <param name="args">The command's arguments</param> /// <returns></returns> private async Task ExecuteMethodAsync(HttpContext context, PcaJsonSerializer serializer, Metadata.Command command, object[] args) { var instance = context.RequestServices.GetService(command.Service.Type); object result = command.MethodInfo.Invoke(instance, args); JToken json = null; if (result != null) { if (result is Task task) { await task; Type resultType = result.GetType(); if (resultType.IsGenericType && command.ReturnType.Type != typeof(void)) // Task<T> { object taskResult = resultType.GetProperty("Result").GetValue(task); json = serializer.Serialize(taskResult, command.ReturnType); } } else { json = serializer.Serialize(result, command.ReturnType); } } if (!context.Response.HasStarted && context.Response.StatusCode < 300) { if (json != null) { context.Response.StatusCode = 200; await context.Response.WriteAsync(json.ToString()); } else { context.Response.StatusCode = 200; await context.Response.WriteAsync(""); } } }
/// <summary> /// Returns a list of arguments in order to call the <c>command</c> /// </summary> /// <param name="query">A JSON-object whose properties are the parameters of the command to execute</param> /// <param name="command">The command being executed</param> /// <param name="serializer">A <see cref="PcaJsonSerializer"/> which is used to convert the JSON to values according to metadata/versioning</param> /// <returns>An array of <c>object</c> containing the command's arguments</returns> private object[] GetArgsFromJObject(HttpContext context, JObject query, Metadata.Command command, PcaJsonSerializer serializer) { Func <string, int, JToken> tokenMapper = (name, index) => query[name]; return(serializer.DeserializeArgumentList(tokenMapper, command.Parameters, null, parMeta => { if (parMeta.ParameterType.Type == typeof(HttpContext)) { return context; } else { throw new ServiceException(HttpStatusCode.InternalServerError, $"Unsupported platform specific parameter: {parMeta.Name}"); } })); }