/// <summary> /// Entry point for HTTP requests /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task ProcessRequestAsync(HttpContext context) { var form = context.Request.Form; _userAccessor.User = context.User; _request.FilesFromClient = form.Files; var command = new NextApiCommand { Service = form["Service"].FirstOrDefault(), Method = form["Method"].FirstOrDefault() }; var argsString = form["Args"].FirstOrDefault(); command.Args = string.IsNullOrEmpty(argsString) ? null : JsonConvert.DeserializeObject <NextApiJsonArgument[]>(argsString, SerializationUtils.GetJsonConfig()) .Cast <INextApiArgument>() .ToArray(); var result = await _handler.ExecuteCommand(command); if (result is NextApiFileResponse fileResponse) { await context.Response.SendNextApiFileResponse(fileResponse); return; } await context.Response.SendJson(result); }
/// <summary> /// Entry point for HTTP requests /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task ProcessRequestAsync(HttpContext context) { var form = context.Request.Form; _userAccessor.User = context.User; _request.FilesFromClient = form.Files; var command = new NextApiCommand { Service = form["Service"].FirstOrDefault(), Method = form["Method"].FirstOrDefault() }; var serialization = form["Serialization"].FirstOrDefault(); var useMessagePack = serialization == SerializationType.MessagePack.ToString(); if (useMessagePack) { var argsFile = form.Files["Args"]; using var memoryStream = new MemoryStream(); await argsFile.CopyToAsync(memoryStream); var byteArray = memoryStream.ToArray(); var args = MessagePackSerializer.Typeless.Deserialize(byteArray); command.Args = (INextApiArgument[])args; } else { var argsString = form["Args"].FirstOrDefault(); command.Args = string.IsNullOrEmpty(argsString) ? null : JsonConvert.DeserializeObject <NextApiJsonArgument[]>(argsString, SerializationUtils.GetJsonConfig()) .Cast <INextApiArgument>() .ToArray(); } var result = await _handler.ExecuteCommand(command); if (result is NextApiFileResponse fileResponse) { await context.Response.SendNextApiFileResponse(fileResponse); return; } if (useMessagePack) { var resultByteArray = MessagePackSerializer.Typeless.Serialize(result); await context.Response.SendByteArray(resultByteArray); } else { await context.Response.SendJson(result); } }
private async Task <T> InvokeHttp <T>(NextApiCommand command) { var form = NextApiClientUtils.PrepareRequestForm(command); HttpResponseMessage response; try { using var client = await GetHttpClient(); response = await client.PostAsync($"{Url}/http", form); response.EnsureSuccessStatusCode(); } catch (Exception ex) { throw new NextApiException(NextApiErrorCode.HttpError, ex.Message); } // check that response can processed as json if (!response.Content.Headers.ContentType.MediaType.Contains("application/json")) { if (typeof(T) != typeof(NextApiFileResponse)) { throw new NextApiException( NextApiErrorCode.IncorrectRequest, "Please specify correct return type for this request. Use NextApiFileResponse." ); } try { return(await NextApiClientUtils.ProcessNextApiFileResponse(response) as dynamic); } catch (Exception ex) { throw new NextApiException(NextApiErrorCode.HttpError, ex.Message); } } // process as normal nextapi response var data = await response.Content.ReadAsStringAsync(); var result = JsonConvert.DeserializeObject <NextApiResponseJsonWrapper <T> >(data, SerializationUtils.GetJsonConfig()); if (!result.Success) { throw NextApiClientUtils.NextApiException(result.Error); } return(result.Data); }
/// <summary> /// Prepare multipart form for sending via HTTP /// </summary> /// <param name="command"></param> /// <param name="argsSerialization"></param> /// <returns></returns> public static MultipartFormDataContent PrepareRequestForm(NextApiCommand command, SerializationType argsSerialization) { var args = command.Args.Where(arg => arg is NextApiArgument).ToArray(); var form = new MultipartFormDataContent { { new StringContent(command.Service), "Service" }, { new StringContent(command.Method), "Method" } }; switch (argsSerialization) { case SerializationType.Json: { var serArgs = JsonConvert.SerializeObject(args, SerializationUtils.GetJsonConfig()); form.Add(new StringContent(serArgs), "Args"); break; } case SerializationType.MessagePack: { var serArgs = MessagePackSerializer.Typeless.Serialize(args); var argsStream = new MemoryStream(serArgs); form.Add(new StreamContent(argsStream), "Args", "Args"); form.Add(new StringContent(SerializationType.MessagePack.ToString()), "Serialization"); break; } default: throw new Exception($"Unsupported serialization type {argsSerialization}"); } // send files var fileArgs = command.Args.Where(arg => arg is NextApiFileArgument).Cast <NextApiFileArgument>().ToArray(); foreach (var nextApiFileArgument in fileArgs) { var stream = nextApiFileArgument.FileDataStream ?? new FileStream(nextApiFileArgument.FilePath, FileMode.Open); var fileName = nextApiFileArgument.FileName ?? Path.GetFileName(nextApiFileArgument.FilePath) ?? "noname.bin"; var name = nextApiFileArgument.FileId; form.Add(new StreamContent(stream), name, fileName); } return(form); }
/// <summary> /// Entry point of service calls /// </summary> /// <param name="command">Information about service call</param> /// <returns>Response from called service</returns> public async Task <dynamic> ExecuteCommand(NextApiCommand command) { // set current nextapi user _nextApiUserAccessor.User = Context.User; // set current request info _nextApiRequest.ClientContext = Context; _nextApiRequest.HubContext = _hubContext; var result = await _handler.ExecuteCommand(command); if (!(result is NextApiFileResponse)) { return(result); } var error = new NextApiError(NextApiErrorCode.OperationIsNotSupported.ToString(), new Dictionary <string, object>() { { "message", "File operations is not supported over SignalR, use HTTP" } }); return(new NextApiResponse(data: null, error: error, success: false)); }
private async Task <T> InvokeSignalR <T>(NextApiCommand command) { var connection = await GetConnection(); command.Args = command.Args.Where(arg => arg is NextApiArgument).ToArray(); NextApiResponse response; try { response = await connection.InvokeAsync <NextApiResponse>("ExecuteCommand", command); } catch (Exception e) { throw new NextApiException(NextApiErrorCode.SignalRError, e.Message); } if (response.Error != null) { throw NextApiClientUtils.NextApiException(response.Error); } return((T)response.Data); }
/// <summary> /// Resolves parameters for method call from NextApiCommand /// </summary> /// <param name="methodInfo">Information about method</param> /// <param name="command">Information about NextApi call</param> /// <returns>Array of parameters</returns> /// <exception cref="Exception">when parameter is not exist in command</exception> public static object[] ResolveMethodParameters(MethodInfo methodInfo, NextApiCommand command) { var paramValues = new List <object>(); foreach (var parameter in methodInfo.GetParameters()) { var paramName = parameter.Name; var arg = command.Args.Cast <INamedNextApiArgument>().FirstOrDefault(d => d.Name == paramName); switch (arg) { case null when !parameter.IsOptional: throw new Exception($"Parameter with {paramName} is not exist in request"); // adding placeholder for optional param. // See: https://stackoverflow.com/questions/9977719/invoke-a-method-with-optional-params-via-reflection case null: paramValues.Add(Type.Missing); continue; case NextApiJsonArgument nextApiJsonArgument: var argType = parameter.ParameterType; var deserializedValue = nextApiJsonArgument.Value?.ToObject(argType, JsonSerializer.Create(SerializationUtils.GetJsonConfig())); paramValues.Add(deserializedValue); break; case NextApiArgument nextApiArgument: paramValues.Add(nextApiArgument.Value); break; } } return(paramValues.ToArray()); }
/// <summary> /// Prepare multipart form for sending via HTTP /// </summary> /// <param name="command"></param> /// <returns></returns> public static MultipartFormDataContent PrepareRequestForm(NextApiCommand command) { var args = command.Args.Where(arg => arg is NextApiArgument).ToArray(); var form = new MultipartFormDataContent { { new StringContent(command.Service), "Service" }, { new StringContent(command.Method), "Method" }, { new StringContent(JsonConvert.SerializeObject(args, SerializationUtils.GetJsonConfig())), "Args" } }; // send files var fileArgs = command.Args.Where(arg => arg is NextApiFileArgument).Cast <NextApiFileArgument>().ToArray(); foreach (var nextApiFileArgument in fileArgs) { var stream = nextApiFileArgument.FileDataStream ?? new FileStream(nextApiFileArgument.FilePath, FileMode.Open); var fileName = nextApiFileArgument.FileName ?? Path.GetFileName(nextApiFileArgument.FilePath) ?? "noname.bin"; var name = nextApiFileArgument.FileId; form.Add(new StreamContent(stream), name, fileName); } return(form); }
/// <summary> /// Entry point of service calls /// </summary> /// <param name="command">Information about service call</param> /// <returns>Response from called service</returns> /// <exception cref="Exception">When security issues, or service issues</exception> public async Task <INextApiResponse> ExecuteCommand(NextApiCommand command) { _logger.LogDebug("NextApi request received..."); _logger.LogDebug($"NextApi/User ID: {_nextApiUserAccessor.SubjectId}"); _logger.LogDebug($"NextApi/Service: {command.Service}"); _logger.LogDebug($"NextApi/Method: {command.Method}"); _logger.LogDebug( $"NextApi/Args: {(command.Args == null ? "no" : JsonConvert.SerializeObject(command.Args))}"); if (string.IsNullOrWhiteSpace(command.Service)) { return(NextApiServiceHelper.CreateNextApiErrorResponse(NextApiErrorCode.ServiceIsNotFound, "Service name is not provided")); } if (string.IsNullOrWhiteSpace(command.Method)) { return(NextApiServiceHelper.CreateNextApiErrorResponse(NextApiErrorCode.OperationIsNotFound, "Operation name is not provided")); } // translating all to lower case command.Service = command.Service.ToLower(); command.Method = command.Method.ToLower(); if (!_serviceRegistry.TryResolveServiceInfo(command.Service, out var serviceInfo)) { _logger.LogDebug($"NextApi/Result: service is not found."); return(NextApiServiceHelper.CreateNextApiErrorResponse(NextApiErrorCode.ServiceIsNotFound, $"Service with name {command.Service} is not found")); } // service access validation var userAuthorized = _nextApiUserAccessor.User.Identity.IsAuthenticated; if (serviceInfo.RequiresAuthorization && !userAuthorized) { _logger.LogDebug($"NextApi/Result: service available only for authorized users."); return(NextApiServiceHelper.CreateNextApiErrorResponse(NextApiErrorCode.ServiceIsOnlyForAuthorized, "This service available only for authorized users")); } var methodInfo = NextApiServiceHelper.GetServiceMethod(serviceInfo.ServiceType, command.Method); if (methodInfo == null) { _logger.LogDebug($"NextApi/Result: method is not found."); return(NextApiServiceHelper.CreateNextApiErrorResponse(NextApiErrorCode.OperationIsNotFound, $"Method with name {command.Method} is not found in service {command.Service}")); } // method access validation var hasPermission = false; if (serviceInfo.MethodsPermissionInfo.TryGetValue(command.Method, out var permissionName)) { try { hasPermission = await _permissionProvider.HasPermission(_nextApiUserAccessor.User, permissionName); } catch (Exception ex) { _logger.LogError($"NextApi/Error: when checking permissions. {ex.Message}", ex); return(NextApiServiceHelper.CreateNextApiErrorResponse(NextApiErrorCode.Unknown, $"Error when checking permissions. {ex.Message}")); } } if (!hasPermission && !serviceInfo.AllowByDefault) { _logger.LogDebug("NextApi/Result: method is not allowed for current user."); return(NextApiServiceHelper.CreateNextApiErrorResponse(NextApiErrorCode.OperationIsNotAllowed, "This operation is not allowed for current user")); } object[] methodParameters; try { methodParameters = NextApiServiceHelper.ResolveMethodParameters(methodInfo, command); } catch (Exception e) { _logger.LogError($"NextApi/Error: when resolving method params. {e.Message}", e); return(NextApiServiceHelper.CreateNextApiErrorResponse(NextApiErrorCode.IncorrectRequest, $"Error when parsing arguments for method. Please send correct arguments.")); } var serviceInstance = (INextApiService)_serviceProvider.GetService(serviceInfo.ServiceType); try { var result = await NextApiServiceHelper.CallService(methodInfo, serviceInstance, methodParameters); _logger.LogDebug( $@"NextApi/Result: {(result is NextApiFileResponse file ? $"file {file.FileName}" : JsonConvert.SerializeObject(result))}"); return(result is INextApiResponse nextApiResponse ? nextApiResponse : new NextApiResponse(result)); } catch (Exception ex) { _logger.LogError($"NextApi/Error: {ex.GetAllMessagesFromException()}", ex); return(NextApiServiceHelper.CreateNextApiExceptionResponse(ex)); } }
private async Task <T> InvokeHttp <T>(NextApiCommand command) { var httpSerializationType = HttpSerializationType; var form = NextApiClientUtils.PrepareRequestForm(command, httpSerializationType); HttpResponseMessage response; try { using var client = await GetHttpClient(); response = await client.PostAsync($"{Url}/http", form); response.EnsureSuccessStatusCode(); } catch (Exception ex) { throw new NextApiException(NextApiErrorCode.HttpError, ex.Message); } // check that response can processed as json var mediaType = response.Content.Headers.ContentType.MediaType; if (!mediaType.Contains("application/json") && typeof(T) == typeof(NextApiFileResponse)) { try { return(await NextApiClientUtils.ProcessNextApiFileResponse(response) as dynamic); } catch (Exception ex) { throw new NextApiException(NextApiErrorCode.HttpError, ex.Message); } } switch (httpSerializationType) { case SerializationType.Json: { // process as normal nextapi response var data = await response.Content.ReadAsStringAsync(); var result = JsonConvert.DeserializeObject <NextApiResponseJsonWrapper <T> >(data, SerializationUtils.GetJsonConfig()); if (!result.Success) { throw NextApiClientUtils.NextApiException(result.Error); } return(result.Data); } case SerializationType.MessagePack: { var resultByteArray = await response.Content.ReadAsByteArrayAsync(); var result = (NextApiResponse)MessagePackSerializer.Typeless.Deserialize(resultByteArray); if (!result.Success) { throw NextApiClientUtils.NextApiException(result.Error); } return((T)result.Data); } default: throw new Exception($"Unsupported serialization type {HttpSerializationType}"); } }