static void SendJsonResponse(ServiceHandlerRequestContext context, object value) { var response = context.HttpResponse; response.ContentType = "application/json; charset=utf-8"; JsonSerializer serializer = new JsonSerializer(); if (context.ServiceInstanceConfiguration.JsonFormatMode == JsonFormatModes.CamelCase) { serializer.ContractResolver = CamelCaseNamingStrategy; } else if (context.ServiceInstanceConfiguration.JsonFormatMode == JsonFormatModes.SnakeCase) { serializer.ContractResolver = SnakeCaseNamingStrategy; } #if DEBUG serializer.Formatting = Formatting.Indented; #endif using (var sw = new StreamWriter(response.Body)) { using (JsonWriter writer = new JsonTextWriter(sw)) { serializer.Serialize(writer, value); } } }
/// <summary> /// Main entry point method that handles processing the active request /// </summary> /// <returns></returns> public async Task ProcessRequest() { var context = new ServiceHandlerRequestContext() { HttpRequest = HttpRequest, HttpResponse = HttpResponse, HttpContext = HttpContext, ServiceInstanceConfiguration = ServiceInstanceConfiguration, MethodContext = MethodContext, Url = new ServiceHandlerRequestContextUrl() { Url = HttpRequest.GetDisplayUrl(), UrlPath = HttpRequest.Path.Value, QueryString = HttpRequest.QueryString, HttpMethod = HttpRequest.Method.ToUpper() } }; try { if (context.ServiceInstanceConfiguration.HttpsMode == ControllerHttpsMode.RequireHttps && HttpRequest.Scheme != "https") { throw new UnauthorizedAccessException(Resources.ServiceMustBeAccessedOverHttps); } if (ServiceInstanceConfiguration.OnAuthorize != null) { if (!await ServiceInstanceConfiguration.OnAuthorize(context)) { throw new UnauthorizedAccessException("Not authorized to access this request"); } } if (ServiceInstanceConfiguration.OnBeforeMethodInvoke != null) { await ServiceInstanceConfiguration.OnBeforeMethodInvoke(context); } await ExecuteMethod(context); ServiceInstanceConfiguration.OnAfterMethodInvoke?.Invoke(context); if (string.IsNullOrEmpty(context.ResultJson)) { context.ResultJson = JsonSerializationUtils.Serialize(context.ResultValue); } SendJsonResponse(context, context.ResultValue); } catch (Exception ex) { var error = new ErrorResponse(ex); SendJsonResponse(context, error); } }
/// <summary> /// /// </summary> /// <param name="handlerContext"></param> /// <returns></returns> async Task ExecuteMethod(ServiceHandlerRequestContext handlerContext) { var serviceConfig = ServiceHandlerConfiguration.Current; var methodToInvoke = handlerContext.MethodContext.MethodInfo; var serviceType = handlerContext.ServiceInstanceConfiguration.ServiceType; var httpVerb = handlerContext.HttpRequest.Method; if (httpVerb == "OPTIONS" && serviceConfig.Cors.UseCorsPolicy) { // emty response - ASP.NET will provide CORS headers via applied policy handlerContext.HttpResponse.StatusCode = StatusCodes.Status204NoContent; return; } // Let DI create the Service instance var inst = HttpContext.RequestServices.GetService(serviceType); if (inst == null) { throw new InvalidOperationException(string.Format(Resources.UnableToCreateTypeInstance, serviceType)); } var principal = HttpContext.User; UserPrincipalHelper.AddPrincipal(inst, principal); if (MethodContext.AuthorizationRoles != null && MethodContext.AuthorizationRoles.Count > 0) { ValidateRoles(MethodContext.AuthorizationRoles, principal); } try { var parameterList = GetMethodParameters(handlerContext); if (!handlerContext.MethodContext.IsAsync) { handlerContext.ResultValue = methodToInvoke.Invoke(inst, parameterList); } else { handlerContext.ResultValue = await(dynamic) methodToInvoke.Invoke(inst, parameterList); } } catch (Exception ex) { throw new InvalidOperationException(string.Format(Resources.UnableToExecuteMethod, methodToInvoke.Name, ex.Message)); } finally { UserPrincipalHelper.RemovePrincipal(inst); } }
public static async Task SendJsonResponseAsync(ServiceHandlerRequestContext context, object value) { var response = context.HttpResponse; response.ContentType = "application/json; charset=utf-8"; var options = new JsonSerializerOptions(); if (context.ServiceInstanceConfiguration.JsonFormatMode == JsonFormatModes.CamelCase) { options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; } //else if (context.ServiceInstanceConfiguration.JsonFormatMode == JsonFormatModes.SnakeCase) // serializer.ContractResolver = SnakeCaseNamingStrategy; #if DEBUG options.WriteIndented = true; #endif var inputType = value.GetType(); await JsonSerializer.SerializeAsync(response.Body, value, inputType, options); }
/// <summary> /// Retrieve parameters from body and URL parameters /// </summary> /// <param name="handlerContext"></param> /// <returns></returns> private object[] GetMethodParameters(ServiceHandlerRequestContext handlerContext) { // parameter parsing var parameterList = new object[] { }; object result = null; // simplistic - no parameters or single body post parameter var paramInfos = handlerContext.MethodContext.MethodInfo.GetParameters(); if (paramInfos.Length > 1) { throw new ArgumentNullException(string.Format( Resources.OnlySingleParametersAreAllowedOnServiceMethods, MethodContext.MethodInfo.Name)); } // if there is a parameter create and de-serialize, then add url parameters if (paramInfos.Length == 1) { var parm = paramInfos[0]; // First Deserialize from body if any JsonSerializer serializer = new JsonSerializer(); // there's always 1 parameter object parameterData = null; if (HttpRequest.ContentLength == null || HttpRequest.ContentLength < 1) { // if no content create an empty one parameterData = ReflectionUtils.CreateInstanceFromType(parm.ParameterType); } else { using (var sw = new StreamReader(HttpRequest.Body)) using (JsonReader reader = new JsonTextReader(sw)) { parameterData = serializer.Deserialize(reader, parm.ParameterType); } } // Map named URL parameters to properties if (RouteData != null) { foreach (var kv in RouteData.Values) { var prop = parm.ParameterType.GetProperty(kv.Key, BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.IgnoreCase); if (prop != null) { try { var val = ReflectionUtils.StringToTypedValue(kv.Value as string, prop.PropertyType); ReflectionUtils.SetProperty(parameterData, kv.Key, val); } catch { throw new InvalidOperationException( string.Format("Unable set parameter from URL segment for property: {0}", kv.Key)); } } } } parameterList = new[] { parameterData }; } return(parameterList); }
/// <summary> /// Main entry point method that handles processing the active request /// </summary> /// <returns></returns> public async Task ProcessRequest() { var context = new ServiceHandlerRequestContext { HttpRequest = HttpRequest, HttpResponse = HttpResponse, HttpContext = HttpContext, ServiceInstanceConfiguration = ServiceInstanceConfiguration, MethodContext = MethodContext, Url = new ServiceHandlerRequestContextUrl { Url = HttpRequest.GetDisplayUrl(), UrlPath = HttpRequest.Path.Value, QueryString = HttpRequest.QueryString, HttpMethod = HttpRequest.Method.ToUpper() } }; try { if (context.ServiceInstanceConfiguration.HttpsMode == ControllerHttpsMode.RequireHttps && HttpRequest.Scheme != "https") { throw new UnauthorizedAccessException(Resources.ServiceMustBeAccessedOverHttps); } if (ServiceInstanceConfiguration.OnAuthorize != null) { if (!await ServiceInstanceConfiguration.OnAuthorize(context)) { throw new UnauthorizedAccessException("Not authorized to access this request"); } } if (ServiceInstanceConfiguration.OnBeforeMethodInvoke != null) { await ServiceInstanceConfiguration.OnBeforeMethodInvoke(context); } await ExecuteMethod(context); ServiceInstanceConfiguration.OnAfterMethodInvoke?.Invoke(context); if (context.ResultValue is IFileResponse fileResponse) { // This is a special case in which we stream the file back low level (side-stepping any kind of JSON serialization) context.HttpResponse.ContentType = fileResponse.ContentType; context.HttpResponse.Headers.Add("Content-Disposition", $"inline; filename=\"{fileResponse.FileName.Trim()}\""); context.HttpResponse.Headers.Add("x-powered-by", "CODE Framework - codeframework.io"); await context.HttpResponse.Body.WriteAsync(fileResponse.FileBytes, 0, fileResponse.FileBytes.Length); } else { if (string.IsNullOrEmpty(context.ResultJson)) { var options = new JsonSerializerOptions(); if (context.ServiceInstanceConfiguration.JsonFormatMode == JsonFormatModes.CamelCase) { options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; } #if DEBUG options.WriteIndented = true; #endif var inputType = context.ResultValue.GetType(); context.ResultJson = JsonSerializer.Serialize(context.ResultValue, inputType, options); context.HttpResponse.Headers.Add("x-powered-by", "CODE Framework - codeframework.io"); } await SendJsonResponseAsync(context, context.ResultValue); } } catch (Exception ex) { var error = new ErrorResponse(ex); await SendJsonResponseAsync(context, error); } }
/// <summary> /// Retrieve parameters from body and URL parameters /// </summary> /// <param name="handlerContext"></param> /// <returns></returns> private async Task <object[]> GetMethodParametersAsync(ServiceHandlerRequestContext handlerContext) { // parameter parsing var parameterList = new object[] { }; // simplistic - no parameters or single body post parameter var paramInfos = handlerContext.MethodContext.MethodInfo.GetParameters(); if (paramInfos.Length > 1) { throw new ArgumentNullException(string.Format(Resources.OnlySingleParametersAreAllowedOnServiceMethods, MethodContext.MethodInfo.Name)); } // if there is a parameter create and de-serialize, then add url parameters if (paramInfos.Length != 1) { return(parameterList); } var parameter = paramInfos[0]; // First Deserialize from body if any // there's always 1 parameter object parameterData; if (HttpRequest.ContentLength == null || HttpRequest.ContentLength < 1) { parameterData = ObjectHelper.CreateInstanceFromType(parameter.ParameterType); // if no content create an empty one } else { parameterData = await JsonSerializer.DeserializeAsync(HttpRequest.Body, parameter.ParameterType); } // We map all parameters passed as named parameters in the URL to their respective properties foreach (var key in handlerContext.HttpRequest.Query.Keys) { var property = parameter.ParameterType.GetProperty(key, BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.IgnoreCase); if (property == null) { continue; } try { var urlParameterValue = handlerContext.HttpRequest.Query[key].ToString(); var parameterValue = UrlParameterToValue(urlParameterValue, property.PropertyType); ObjectHelper.SetPropertyValue(parameterData, key, parameterValue); } catch { throw new InvalidOperationException($"Unable set parameter from URL segment for property: {key}"); } } // Map inline URL parameters defined in the route to properties. // Note: Since this is done after the named parameters above, parameters that are part of the route definition win out over simple named parameters if (RouteData != null) { foreach (var(key, value) in RouteData.Values) { var property = parameter.ParameterType.GetProperty(key, BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.IgnoreCase); if (property == null) { continue; } try { var parameterValue = UrlParameterToValue(value as string, property.PropertyType); ObjectHelper.SetPropertyValue(parameterData, key, parameterValue); } catch { throw new InvalidOperationException($"Unable set parameter from URL segment for property: {key}"); } } } parameterList = new[] { parameterData }; return(parameterList); }