/// <summary> /// Processes the specified block action. /// </summary> /// <param name="controller">The API controller that initiated this action.</param> /// <param name="block">The block.</param> /// <param name="actionName">Name of the action.</param> /// <param name="actionParameters">The action parameters.</param> /// <param name="bodyParameters">The posted body parameters.</param> /// <returns></returns> /// <exception cref="ArgumentNullException">actionName /// or /// actionData</exception> internal static IHttpActionResult InvokeAction(ApiControllerBase controller, Blocks.IRockBlockType block, string actionName, Dictionary <string, JToken> actionParameters, JToken bodyParameters) { // Parse the body content into our normal parameters. if (bodyParameters != null) { try { // Parse any posted parameter data, existing query string // parameters take precedence. foreach (var kvp in bodyParameters.ToObject <Dictionary <string, JToken> >()) { actionParameters.AddOrIgnore(kvp.Key, kvp.Value); } } catch { return(new BadRequestErrorMessageResult("Invalid parameter data.", controller)); } } // // Find the action they requested. First search by name // and then further filter by any method constraint attributes. // var actions = block.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public) .Where(m => m.GetCustomAttribute <Blocks.BlockActionAttribute>()?.ActionName == actionName) .ToList(); if (actions.Count == 0) { return(new NotFoundResult(controller)); } var action = FindBestActionForParameters(actions, actionParameters); if (action == null) { // This is an actual configuration error, so throw an error. throw new AmbiguousMatchException("The request matched multiple actions."); } var methodParameters = action.GetParameters(); var parameters = new List <object>(); // // Go through each parameter and convert it to the proper type. // for (int i = 0; i < methodParameters.Length; i++) { // Check if this parameter is requesting it's content from the body. if (methodParameters[i].GetCustomAttribute <FromBodyAttribute>() != null) { if (bodyParameters != null) { parameters.Add(bodyParameters.ToObject(methodParameters[i].ParameterType)); } else if (methodParameters[i].IsOptional) { parameters.Add(Type.Missing); } else { return(new BadRequestErrorMessageResult($"Parameter '{methodParameters[i].Name}' is required.", controller)); } continue; } var key = actionParameters.Keys.SingleOrDefault(k => k.ToLowerInvariant() == methodParameters[i].Name.ToLower()); if (key != null) { try { // // If the target type is nullable and the action parameter is an empty // string then consider it null. A GET query cannot have null values. // if (Nullable.GetUnderlyingType(methodParameters[i].ParameterType) != null) { if (actionParameters[key].Type == JTokenType.String && actionParameters[key].ToString() == string.Empty) { parameters.Add(null); continue; } } parameters.Add(actionParameters[key].ToObject(methodParameters[i].ParameterType)); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); return(new BadRequestErrorMessageResult($"Parameter type mismatch for '{methodParameters[i].Name}'.", controller)); } } else if (methodParameters[i].IsOptional) { parameters.Add(Type.Missing); } else { return(new BadRequestErrorMessageResult($"Parameter '{methodParameters[i].Name}' is required.", controller)); } } object result; try { result = action.Invoke(block, parameters.ToArray()); } catch (TargetInvocationException ex) { ExceptionLogService.LogApiException(ex.InnerException, controller.Request, GetPerson(controller, null)?.PrimaryAlias); result = new BlockActionResult(HttpStatusCode.InternalServerError, GetMessageForClient(ex)); } catch (Exception ex) { ExceptionLogService.LogApiException(ex, controller.Request, GetPerson(controller, null)?.PrimaryAlias); result = new BlockActionResult(HttpStatusCode.InternalServerError, GetMessageForClient(ex)); } // // Handle the result type. // if (result is IHttpActionResult httpActionResult) { return(httpActionResult); } else if (result is BlockActionResult actionResult) { var isErrorStatusCode = ( int )actionResult.StatusCode >= 400; if (isErrorStatusCode && actionResult.Content is string) { return(new NegotiatedContentResult <HttpError>(actionResult.StatusCode, new HttpError(actionResult.Content.ToString()), controller)); } else if (actionResult.Error != null) { return(new NegotiatedContentResult <HttpError>(actionResult.StatusCode, new HttpError(actionResult.Error), controller)); } else if (actionResult.Content is HttpContent httpContent) { var response = controller.Request.CreateResponse(actionResult.StatusCode); response.Content = httpContent; return(new ResponseMessageResult(response)); } else if (actionResult.ContentClrType != null) { var genericType = typeof(System.Web.Http.Results.NegotiatedContentResult <>).MakeGenericType(actionResult.ContentClrType); return(( IHttpActionResult )Activator.CreateInstance(genericType, actionResult.StatusCode, actionResult.Content, controller)); } else { return(new StatusCodeResult(actionResult.StatusCode, controller)); } } else if (action.ReturnType == typeof(void)) { return(new OkResult(controller)); } else { return(new OkNegotiatedContentResult <object>(result, controller)); } }
/// <summary> /// Processes the specified block action. /// </summary> /// <param name="block">The block.</param> /// <param name="verb">The HTTP Method Verb that was used for the request.</param> /// <param name="actionName">Name of the action.</param> /// <param name="actionParameters">The action parameters.</param> /// <returns></returns> /// <exception cref="ArgumentNullException"> /// actionName /// or /// actionData /// </exception> private IHttpActionResult InvokeAction(Blocks.IRockBlockType block, string verb, string actionName, Dictionary <string, JToken> actionParameters) { MethodInfo action; // // Find the action they requested. // action = block.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public) .SingleOrDefault(m => m.GetCustomAttribute <Blocks.BlockActionAttribute>()?.ActionName == actionName); if (action == null) { return(NotFound()); } var methodParameters = action.GetParameters(); var parameters = new List <object>(); // // Go through each parameter and convert it to the proper type. // for (int i = 0; i < methodParameters.Length; i++) { var key = actionParameters.Keys.SingleOrDefault(k => k.ToLowerInvariant() == methodParameters[i].Name.ToLower()); if (key != null) { try { // // If the target type is nullable and the action parameter is an empty // string then consider it null. A GET query cannot have null values. // if (Nullable.GetUnderlyingType(methodParameters[i].ParameterType) != null) { if (actionParameters[key].Type == JTokenType.String && actionParameters[key].ToString() == string.Empty) { parameters.Add(null); continue; } } parameters.Add(actionParameters[key].ToObject(methodParameters[i].ParameterType)); } catch { return(BadRequest($"Parameter type mismatch for '{methodParameters[i].Name}'.")); } } else if (methodParameters[i].IsOptional) { parameters.Add(Type.Missing); } else { return(BadRequest($"Parameter '{methodParameters[i].Name}' is required.")); } } object result; try { result = action.Invoke(block, parameters.ToArray()); } catch (TargetInvocationException ex) { ExceptionLogService.LogApiException(ex.InnerException, Request, GetPersonAlias()); result = new Rock.Blocks.BlockActionResult(HttpStatusCode.InternalServerError); } catch (Exception ex) { ExceptionLogService.LogApiException(ex, Request, GetPersonAlias()); result = new Rock.Blocks.BlockActionResult(HttpStatusCode.InternalServerError); } // // Handle the result type. // if (result is IHttpActionResult) { return(( IHttpActionResult )result); } else if (result is Rock.Blocks.BlockActionResult actionResult) { if (actionResult.Error != null) { return(Content(actionResult.StatusCode, new HttpError(actionResult.Error))); } else if (actionResult.Content is HttpContent httpContent) { var response = Request.CreateResponse(actionResult.StatusCode); response.Content = httpContent; return(new System.Web.Http.Results.ResponseMessageResult(response)); } else if (actionResult.ContentClrType != null) { var genericType = typeof(System.Web.Http.Results.NegotiatedContentResult <>).MakeGenericType(actionResult.ContentClrType); return(( IHttpActionResult )Activator.CreateInstance(genericType, actionResult.StatusCode, actionResult.Content, this)); } else { return(StatusCode(actionResult.StatusCode)); } } else if (action.ReturnType == typeof(void)) { return(Ok()); } else { return(Ok(result)); } }