예제 #1
0
        /// <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);
        }
예제 #2
0
        /// <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);
            }
        }
예제 #3
0
        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);
        }
예제 #4
0
        /// <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);
        }
예제 #5
0
        /// <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));
        }
예제 #6
0
        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);
        }
예제 #7
0
        /// <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());
        }
예제 #8
0
        /// <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);
        }
예제 #9
0
        /// <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));
            }
        }
예제 #10
0
        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}");
            }
        }