public ActionSelectorCacheItem(HttpControllerDescriptor controllerDescriptor) { Contract.Assert(controllerDescriptor != null); // Initialize the cache entirely in the ctor on a single thread. _controllerDescriptor = controllerDescriptor; MethodInfo[] allMethods = _controllerDescriptor.ControllerType.GetMethods(BindingFlags.Instance | BindingFlags.Public); MethodInfo[] validMethods = Array.FindAll(allMethods, IsValidActionMethod); _combinedCandidateActions = new CandidateAction[validMethods.Length]; for (int i = 0; i < validMethods.Length; i++) { MethodInfo method = validMethods[i]; ReflectedHttpActionDescriptor actionDescriptor = new ReflectedHttpActionDescriptor(_controllerDescriptor, method); _combinedCandidateActions[i] = new CandidateAction { ActionDescriptor = actionDescriptor }; HttpActionBinding actionBinding = actionDescriptor.ActionBinding; // Building an action parameter name mapping to compare against the URI parameters coming from the request. Here we only take into account required parameters that are simple types and come from URI. _actionParameterNames.Add( actionDescriptor, actionBinding.ParameterBindings .Where (binding => !binding.Descriptor.IsOptional && TypeHelper.CanConvertFromString(binding.Descriptor.ParameterType) && binding.WillReadUri()) .Select(binding => binding.Descriptor.Prefix ?? binding.Descriptor.ParameterName).ToArray()); } _combinedActionNameMapping = _combinedCandidateActions .Select(c => c.ActionDescriptor) .ToLookup(actionDesc => actionDesc.ActionName, StringComparer.OrdinalIgnoreCase); }
private CandidateAction[] GetInitialCandidateList(HttpControllerContext controllerContext, bool ignoreVerbs = false) { // Initial candidate list is determined by: // - Direct route? // - {action} value? // - ignore verbs? string actionName; HttpMethod incomingMethod = controllerContext.Request.Method; IHttpRouteData routeData = controllerContext.RouteData; Contract.Assert(routeData.GetSubRoutes() == null, "Should not be called on a direct route"); CandidateAction[] candidates; if (routeData.Values.TryGetValue(RouteValueKeys.Action, out actionName)) { // We have an explicit {action} value, do traditional binding. Just lookup by actionName HttpActionDescriptor[] actionsFoundByName = _standardActions.StandardActionNameMapping[actionName].ToArray(); // Throws HttpResponseException with NotFound status because no action matches the Name if (actionsFoundByName.Length == 0) { throw new HttpResponseException(CreateActionNotFoundResponse(controllerContext, actionName)); } CandidateAction[] candidatesFoundByName = new CandidateAction[actionsFoundByName.Length]; for (int i = 0; i < actionsFoundByName.Length; i++) { candidatesFoundByName[i] = new CandidateAction { ActionDescriptor = actionsFoundByName[i] }; } if (ignoreVerbs) { candidates = candidatesFoundByName; } else { candidates = FilterIncompatibleVerbs(incomingMethod, candidatesFoundByName); } } else { if (ignoreVerbs) { candidates = _standardActions.StandardCandidateActions; } else { // No direct routing or {action} parameter, infer it from the verb. candidates = FindActionsForVerb(incomingMethod, _standardActions.CacheListVerbs, _standardActions.StandardCandidateActions); } } return(candidates); }
// This method lazy-initializes the data needed for action selection. This is a safe race-condition. This is // done because we don't know whether or not an action/controller is attribute routed until after attribute // routes are added. private void InitializeStandardActions() { if (_standardActions != null) { return; } StandardActionSelectionCache standardActions = new StandardActionSelectionCache(); if (_controllerDescriptor.IsAttributeRouted()) { // The controller has an attribute route; no actionsByVerb are accessible via standard routing. standardActions.StandardCandidateActions = new CandidateAction[0]; } else { // The controller does not have an attribute route; some actionsByVerb may be accessible via standard // routing. List <CandidateAction> standardCandidateActions = new List <CandidateAction>(); for (int i = 0; i < _combinedCandidateActions.Length; i++) { CandidateAction candidate = _combinedCandidateActions[i]; // We know that this cast is safe before we created all of the action descriptors for standard actions ReflectedHttpActionDescriptor action = (ReflectedHttpActionDescriptor)candidate.ActionDescriptor; // Allow standard routes access inherited actionsByVerb or actionsByVerb without Route attributes. if (action.MethodInfo.DeclaringType != _controllerDescriptor.ControllerType || !candidate.ActionDescriptor.IsAttributeRouted()) { standardCandidateActions.Add(candidate); } } standardActions.StandardCandidateActions = standardCandidateActions.ToArray(); } standardActions.StandardActionNameMapping = standardActions.StandardCandidateActions .Select(c => c.ActionDescriptor) .ToLookup(actionDesc => actionDesc.ActionName, StringComparer.OrdinalIgnoreCase); // Bucket the action descriptors by common verbs. int len = _cacheListVerbKinds.Length; standardActions.CacheListVerbs = new CandidateAction[len][]; for (int i = 0; i < len; i++) { standardActions.CacheListVerbs[i] = FindActionsForVerbWorker(_cacheListVerbKinds[i], standardActions.StandardCandidateActions); } _standardActions = standardActions; }
private string DebuggerToString() { StringBuilder sb = new StringBuilder(); sb.Append(CandidateAction.DebuggerToString()); if (CombinedParameterNames.Count > 0) { sb.Append(", Params ="); foreach (string param in CombinedParameterNames) { sb.AppendFormat(" {0}", param); } } return(sb.ToString()); }
public CandidateActionWithParams(CandidateAction candidateAction, ISet <string> parameters, IHttpRouteData routeDataSource) { CandidateAction = candidateAction; CombinedParameterNames = parameters; RouteDataSource = routeDataSource; }