protected Parameter GetPrimitiveParameter(ControllerParameterDescriptor apiParam) { var netType = apiParam.ParameterType; var ramlType = MapParam(netType); return(GetParameter(apiParam, ramlType, netType)); }
private ControllerActionDescriptor CreateActionDescriptor(string httpMethod, string routeTemplate, MethodInfo methodInfo, string controllerName, GrainKeyParamterInfo grainKey) { var descriptor = new ControllerActionDescriptor(); descriptor.SetProperty(new ApiDescriptionActionData()); descriptor.ControllerTypeInfo = methodInfo.DeclaringType.GetTypeInfo(); descriptor.MethodInfo = methodInfo; descriptor.FilterDescriptors = descriptor.MethodInfo.GetCustomAttributes <ProducesResponseTypeAttribute>() .Select((filter) => new FilterDescriptor(filter, FilterScope.Action)) .ToList(); descriptor.RouteValues = new Dictionary <string, string> { { "controller", controllerName } }; descriptor.ActionConstraints = new List <IActionConstraintMetadata>(); if (httpMethod != null) { descriptor.ActionConstraints.Add(new HttpMethodActionConstraint(new[] { httpMethod })); } descriptor.Parameters = new List <ParameterDescriptor>(); List <ParameterInfo> ParameterInfos = descriptor.MethodInfo.GetParameters().ToList(); if (grainKey.ParameterType != typeof(Guid)) { ParameterInfos.Insert(0, grainKey); } foreach (var parameterInfo in ParameterInfos) { var parameterDescriptor = new ControllerParameterDescriptor { Name = parameterInfo.Name, ParameterType = parameterInfo.ParameterType, ParameterInfo = parameterInfo, BindingInfo = new BindingInfo() { BinderModelName = parameterInfo.Name, BindingSource = BindingSource.Query, BinderType = parameterInfo.ParameterType } }; if (parameterInfo.ParameterType.CanHaveChildren()) { parameterDescriptor.BindingInfo.BindingSource = BindingSource.Body; } if (parameterInfo is GrainKeyParamterInfo) { routeTemplate += "/{" + grainKey.Name + "}"; parameterDescriptor.BindingInfo.BindingSource = BindingSource.Path; } descriptor.Parameters.Add(parameterDescriptor); } ; descriptor.AttributeRouteInfo = new AttributeRouteInfo { Template = routeTemplate }; return(descriptor); }
private bool TryMatch(IList <ParameterDescriptor> parameters, IList <string> availableKeys, ODataOptionalParameter optionalWrapper) { // use the parameter name to match. foreach (var p in parameters) { string parameterName = p.Name.ToLowerInvariant(); if (availableKeys.Contains(parameterName)) { continue; } ControllerParameterDescriptor cP = p as ControllerParameterDescriptor; if (cP != null && optionalWrapper != null) { if (cP.ParameterInfo.IsOptional && optionalWrapper.OptionalParameters.Any(o => o.Name.ToLowerInvariant() == parameterName)) { continue; } } return(false); } return(true); }
public ActionArgument(ControllerParameterDescriptor descriptor, object value) { Guard.EnsureIsNotNull(descriptor, nameof(descriptor)); Descriptor = descriptor; Value = value; DefaultValue = descriptor.ParameterInfo.DefaultValue; }
public IActionResult CallByName([FromBody] JObject body) { Guard.NotNull(body, nameof(body)); try { if (!this.rpcSettings.Server) { return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.MethodNotAllowed, "Method not allowed", "Method not allowed when RPC is disabled.")); } StringComparison ignoreCase = StringComparison.InvariantCultureIgnoreCase; string methodName = (string)body.GetValue("methodName", ignoreCase); ControllerActionDescriptor actionDescriptor = null; if (!this.GetActionDescriptors()?.TryGetValue(methodName, out actionDescriptor) ?? false) { throw new Exception($"RPC method '{ methodName }' not found."); } // Prepare the named parameters that were passed via the query string in the order that they are expected by SendCommand. List <ControllerParameterDescriptor> paramInfos = actionDescriptor.Parameters.OfType <ControllerParameterDescriptor>().ToList(); var paramsAsObjects = new object[paramInfos.Count]; for (int i = 0; i < paramInfos.Count; i++) { ControllerParameterDescriptor pInfo = paramInfos[i]; bool hasValue = body.TryGetValue(pInfo.Name, ignoreCase, out JToken jValue); if (hasValue && !jValue.HasValues) { paramsAsObjects[i] = jValue.ToString(); } else { paramsAsObjects[i] = pInfo.ParameterInfo.DefaultValue?.ToString(); } } RPCRequest request = new RPCRequest(methodName, paramsAsObjects); // Build RPC request object. RPCResponse response = this.SendRPCRequest(request); // Throw error if any. if (response?.Error?.Message != null) { throw new Exception(response.Error.Message); } // Return a Json result from the API call. return(this.Json(response?.Result)); } catch (Exception e) { this.logger.LogError("Exception occurred: {0}", e.ToString()); return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString())); } }
public ApiParameterMeta(ControllerParameterDescriptor descriptor) { Check.NotNull(descriptor, nameof(descriptor)); this.ParameterName = descriptor.Name; //this.ParameterType = descriptor.ParameterType; this.IsOptional = descriptor.ParameterInfo.IsOptional; this.DefaultValue = descriptor.ParameterInfo.DefaultValue; }
private string GetParameterName(ControllerParameterDescriptor parameter) { var routeAttr = parameter.ParameterInfo.GetCustomAttribute <FromRouteAttribute>(); var queryAttr = parameter.ParameterInfo.GetCustomAttribute <FromQueryAttribute>(); var headerAttr = parameter.ParameterInfo.GetCustomAttribute <FromHeaderAttribute>(); var formAttr = parameter.ParameterInfo.GetCustomAttribute <FromFormAttribute>(); return(routeAttr?.Name ?? queryAttr?.Name ?? headerAttr?.Name ?? formAttr?.Name ?? parameter.Name); }
public ParameterFilterContext( ApiParameterDescription apiParameterDescription, ControllerParameterDescriptor controllerParameterDescriptor, ISchemaRegistry schemaRegistry) { ApiParameterDescription = apiParameterDescription; ControllerParameterDescriptor = controllerParameterDescriptor; SchemaRegistry = schemaRegistry; }
public async Task BindModelAsync_ForParameter_UsesValidationFromParameter_WhenDerivedModelIsSet() { // Arrange var method = GetType().GetMethod(nameof(TestMethodWithAttributes), BindingFlags.NonPublic | BindingFlags.Instance); var parameter = method.GetParameters()[0]; var parameterDescriptor = new ControllerParameterDescriptor { ParameterInfo = parameter, Name = parameter.Name, }; var actionContext = GetControllerContext(); var modelMetadataProvider = new TestModelMetadataProvider(); var model = new DerivedPerson { DerivedProperty = "SomeValue" }; var modelBindingResult = ModelBindingResult.Success(model); var parameterBinder = new ParameterBinder( modelMetadataProvider, Mock.Of <IModelBinderFactory>(), new DefaultObjectValidator( modelMetadataProvider, new[] { TestModelValidatorProvider.CreateDefaultProvider() }, new MvcOptions()), _optionsAccessor, NullLoggerFactory.Instance); var modelMetadata = modelMetadataProvider.GetMetadataForParameter(parameter); var modelBinder = CreateMockModelBinder(modelBindingResult); // Act var result = await parameterBinder.BindModelAsync( actionContext, modelBinder, CreateMockValueProvider(), parameterDescriptor, modelMetadata, value : null); // Assert Assert.True(result.IsModelSet); Assert.Same(model, result.Model); Assert.False(actionContext.ModelState.IsValid); Assert.Collection( actionContext.ModelState, kvp => { Assert.Equal(parameter.Name, kvp.Key); var error = Assert.Single(kvp.Value.Errors); Assert.Equal("Always Invalid", error.ErrorMessage); }); }
private static ParameterDescriptor CreateParameterDescriptor(ParameterModel parameterModel) { var parameterDescriptor = new ControllerParameterDescriptor() { Name = parameterModel.ParameterName, ParameterType = parameterModel.ParameterInfo.ParameterType, BindingInfo = parameterModel.BindingInfo, ParameterInfo = parameterModel.ParameterInfo, }; return(parameterDescriptor); }
public ApiParameterMeta(ControllerParameterDescriptor descriptor) { if (descriptor == null) { throw new ArgumentNullException(nameof(descriptor)); } ParameterName = descriptor.Name; ParameterType = descriptor.ParameterType; IsOptional = descriptor.ParameterInfo.IsOptional; DefaultValue = descriptor.ParameterInfo.DefaultValue; }
/// <summary> /// 嘗試取得相應的模型綁定器 /// </summary> /// <param name="context">綁定內容</param> /// <returns>模型綁定器實例</returns> public IModelBinder GetBinder(ModelBinderProviderContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (context.Metadata.IsComplexType) { var propName = context.Metadata.PropertyName; var propInfo = context.Metadata.ContainerType?.GetProperty(propName); var attribute = propInfo ?.GetCustomAttributes(typeof(FromJsonAttribute), false) ?.FirstOrDefault(); // 直接將整個FormData綁定至單一複雜類型的參數上 if (propName != null && propInfo != null && attribute != null) { return(new MultipartJsonModelBinderProvider()); } if (context?.Metadata?.BinderType?.GetCustomAttribute <FromJsonAttribute>() != null) { return(new MultipartJsonModelBinderProvider()); } var visited = context.GetType().GetProperty("Visited", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); ControllerParameterDescriptor cpd = null; foreach (var item in (IEnumerable)visited.GetValue(context)) { var temp = item.GetType().GetProperty("Key").GetValue(item); var tokenField = temp.GetType().GetField("_token", BindingFlags.Instance | BindingFlags.NonPublic); cpd = tokenField.GetValue(temp) as ControllerParameterDescriptor; if (cpd != null) { break; } } if (cpd != null && cpd.ParameterInfo.GetCustomAttribute <FromJsonAttribute>() != null) { return(new MultipartJsonModelBinderProvider()); } } return(null); }
protected Parameter GetParameter(ControllerParameterDescriptor apiParam, string ramlType, Type parameterType) { var parameter = new Parameter { Default = apiParam.ParameterInfo.HasDefaultValue ? null : apiParam.ParameterInfo.DefaultValue.ToString(), Required = !apiParam.ParameterInfo.IsOptional, Type = ramlType, DisplayName = apiParam.Name }; return(parameter); }
/// <summary> /// 创建接口参数 /// </summary> /// <param name="pams"></param> /// <param name="schemaRegistry"></param> /// <returns></returns> public IParameter CreateParamters(ControllerParameterDescriptor pams, ISchemaRegistry schemaRegistry) { var location = GetParameterLocation(pams); var schema = (pams.ParameterType == null) ? null : schemaRegistry.GetOrRegister(pams.ParameterType); if (location == "body") { return(new BodyParameter { Name = pams.Name, Description = pams.Name, Schema = schema }); } if (location != "header" && location != "query") { location = "path"; } var nonBodyParam = new NonBodyParameter { Name = pams.Name, In = location, Required = (location == "path"), }; if (schema == null) { nonBodyParam.Type = "string"; } else { nonBodyParam.PopulateFrom(schema); } if (nonBodyParam.Type == "array") { nonBodyParam.CollectionFormat = "multi"; } return(nonBodyParam); }
private void GetQueryParameter(ControllerParameterDescriptor apiParam, Dictionary <string, Parameter> queryParams) { Parameter parameter; if (!IsPrimitiveType(apiParam.ParameterType)) { var typeName = typeBuilder.Add(apiParam.ParameterType); parameter = GetParameter(apiParam, typeName, apiParam.ParameterType); } else { parameter = GetPrimitiveParameter(apiParam); } if (!queryParams.ContainsKey(apiParam.Name)) { queryParams.Add(apiParam.Name, parameter); } }
/// <summary> /// Get the first parameter with the MapFromBody attribute for the specified action descriptor. /// </summary> /// <param name="actionDescriptor">The action descriptor on which to search for the parameter.</param> /// <param name="parameterDescriptor">Parameter descriptor result.</param> /// <param name="mapFromBodyAttribute">MapFromBodyAttribute instance result.</param> public static void GetMapFromBodyParameter(this ActionDescriptor actionDescriptor, out ControllerParameterDescriptor parameterDescriptor, out MapFromBodyAttribute mapFromBodyAttribute) { ControllerParameterDescriptor resultParamDescriptor = null; MapFromBodyAttribute resultAttribute = null; var controllerParameters = actionDescriptor.Parameters.OfType <ControllerParameterDescriptor>(); foreach (var param in controllerParameters) { resultAttribute = param.ParameterInfo.GetCustomAttribute <MapFromBodyAttribute>(); if (resultAttribute != null) { resultParamDescriptor = param; break; } } parameterDescriptor = resultParamDescriptor; mapFromBodyAttribute = resultAttribute; }
public void OnResourceExecuting(ResourceExecutingContext context) { // 如果是api控制器,则处理 if (context.ActionDescriptor.FilterDescriptors.FirstOrDefault(x => x.Filter is ApiControllerAttribute) != null) { var p = context.ActionDescriptor.Parameters[0] as ControllerParameterDescriptor; ControllerParameterDescriptor cpd = new ControllerParameterDescriptor() { Name = p.Name, BindingInfo = p.BindingInfo, ParameterType = typeof(Args <>).MakeGenericType(p.ParameterType), ParameterInfo = p.ParameterInfo, }; p.ParameterType = typeof(Args <>).MakeGenericType(p.ParameterType); MyParameterInfo mp = new MyParameterInfo(p.ParameterInfo); mp.ChangeType = p.ParameterType; p.ParameterInfo = mp; } }
public void OnProvidersExecuted(ApiDescriptionProviderContext context) { foreach (ApiDescription desc in context.Results) { foreach (ApiParameterDescription param in desc.ParameterDescriptions) { // Mvc chokes on parameters with [Required] on them. Give it a hand. ControllerParameterDescriptor actionParam = desc.ActionDescriptor.Parameters .OfType <ControllerParameterDescriptor>() .FirstOrDefault(p => p.Name == param.Name); if (actionParam != null) { if (actionParam.ParameterInfo.GetCustomAttributes() .Any(a => a.GetType().Name == "RequiredAttribute")) { param.ModelMetadata = new SetRequiredModelMetadata(param.ModelMetadata); } } } } }
private object GetBodyData(ActionExecutingContext actionContext, IValidator bodyValidator) { ControllerParameterDescriptor descriptor = actionContext.ActionDescriptor .Parameters .Cast <ControllerParameterDescriptor>() .SingleOrDefault(prm => prm.ParameterInfo.GetCustomAttributes(typeof(FromBodyAttribute), inherit: true) .Any()); if (descriptor == null) { throw new ArgumentException("Method must have FromBodyAttribute"); } if (bodyValidator.CanValidateInstancesOfType(descriptor.ParameterType) == false) { throw new ArgumentException( $"Validator {bodyValidatorType.Name} can't validate object type {descriptor.ParameterType}"); } return(actionContext.ActionArguments[descriptor.Name]); }
public async Task BinderTypeOnParameterType_WithData_EmptyPrefix_GetsBound(BindingInfo bindingInfo) { // Arrange var parameterBinder = ModelBindingTestHelper.GetParameterBinder(); var parameters = typeof(TestController).GetMethod(nameof(TestController.Action)).GetParameters(); var parameter = new ControllerParameterDescriptor { Name = "Parameter1", BindingInfo = bindingInfo, ParameterInfo = parameters[0], ParameterType = typeof(Address), }; var testContext = ModelBindingTestHelper.GetTestContext(); var modelState = testContext.ModelState; // Act var modelBindingResult = await parameterBinder.BindModelAsync(parameter, testContext); // Assert // ModelBindingResult Assert.True(modelBindingResult.IsModelSet); // Model var address = Assert.IsType <Address>(modelBindingResult.Model); Assert.Equal("SomeStreet", address.Street); // ModelState Assert.True(modelState.IsValid); var kvp = Assert.Single(modelState); Assert.Equal("Street", kvp.Key); var entry = kvp.Value; Assert.NotNull(entry); Assert.Equal(ModelValidationState.Valid, entry.ValidationState); Assert.NotNull(entry.RawValue); // Value is set by test model binder, no need to validate it. }
public IActionResult CallByName([FromQuery] string methodName) { try { ControllerActionDescriptor actionDescriptor = null; if (!this.GetActionDescriptors()?.TryGetValue(methodName, out actionDescriptor) ?? false) { throw new Exception($"RPC method '{ methodName }' not found."); } // Prepare the named parameters that were passed via the query string in the order that they are expected by SendCommand. List <ControllerParameterDescriptor> paramInfo = actionDescriptor.Parameters.OfType <ControllerParameterDescriptor>().ToList(); var param = new object[paramInfo.Count]; for (int i = 0; i < paramInfo.Count; i++) { ControllerParameterDescriptor pInfo = paramInfo[i]; KeyValuePair <string, StringValues> stringValues = this.Request.Query.FirstOrDefault(p => p.Key.ToLower() == pInfo.Name.ToLower()); param[i] = (stringValues.Key == null) ? pInfo.ParameterInfo.HasDefaultValue ? pInfo.ParameterInfo.DefaultValue.ToString() : null : stringValues.Value[0]; } // Build RPC request object. RPCResponse response = this.SendRPCRequest(new RPCRequest(methodName, param)); // Throw error if any. if (response?.Error?.Message != null) { throw new Exception(response.Error.Message); } // Return a Json result from the API call. return(this.Json(response?.Result)); } catch (Exception e) { this.logger.LogError("Exception occurred: {0}", e.ToString()); return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString())); } }
private List <ApiDescriptionGroup> CreateDescriptors() { var groups = new List <ApiDescriptionGroup>(); var grainList = DiscoverGrainTypesToMap(grainInterfaceFeature); foreach (var grainType in grainList) { var typeDescription = grainType.GetCustomAttribute <DescriptionAttribute>(); var groupName = typeDescription?.Description ?? grainType.Name; var apiItems = new List <ApiDescription>(); var methods = grainType.GetMethods().Where(m => m.GetCustomAttributes(true) .Any(attr => attr.GetType() == _routeAttributeType || _methodAttributeTypes.Contains(attr.GetType()))).ToArray(); foreach (var methodInfo in methods) { var methodAttributes = methodInfo.GetCustomAttributes(true) .Where(attr => attr is Orleans.Http.Abstractions.MethodAttribute).SingleOrDefault() as Orleans.Http.Abstractions.MethodAttribute; if (null == methodAttributes) { continue; } var descriptor = new ControllerActionDescriptor() { ControllerName = groupName, ActionName = methodInfo.Name, DisplayName = methodInfo.Name, MethodInfo = methodInfo, ControllerTypeInfo = methodInfo.DeclaringType.GetTypeInfo(), RouteValues = new Dictionary <string, string>() { { "controller", groupName } }, AttributeRouteInfo = new AttributeRouteInfo() { Name = methodInfo.Name, Template = methodAttributes.Pattern }, ActionConstraints = new List <IActionConstraintMetadata>() { new HttpMethodActionConstraint(new[] { methodAttributes.Method }) }, Parameters = new List <ParameterDescriptor>(), BoundProperties = new List <ParameterDescriptor>(), FilterDescriptors = new List <FilterDescriptor>(), Properties = new Dictionary <object, object>() }; var description = new ApiDescription() { ActionDescriptor = descriptor, GroupName = groupName, HttpMethod = methodAttributes.Method, RelativePath = $"grains/{methodAttributes.Pattern}" }; var methodParams = methodInfo.GetParameters(); foreach (var parameter in methodParams) { var bindsource = BindingSource.Path; var attribute = parameter.GetCustomAttributes() .Where(attr => _parameterAttributeTypes.Contains(attr.GetType())).FirstOrDefault(); if (attribute != null) { if (attribute is Orleans.Http.Abstractions.FromBodyAttribute) { bindsource = BindingSource.Body; } else if (attribute is Orleans.Http.Abstractions.FromQueryAttribute) { bindsource = BindingSource.Query; } } var parameterDescriptor = new ControllerParameterDescriptor() { Name = parameter.Name, ParameterType = parameter.ParameterType, /* BindingInfo = new BindingInfo() * { * BinderModelName = parameter.Name, * BindingSource = bindsource, * BinderType = parameter.ParameterType * },*/ ParameterInfo = parameter }; descriptor.Parameters.Add(parameterDescriptor); description.ParameterDescriptions.Add(new ApiParameterDescription() { ModelMetadata = metadataProvider.GetMetadataForType(parameter.ParameterType), Name = parameter.Name, RouteInfo = new ApiParameterRouteInfo(), Type = parameter.ParameterType, Source = bindsource, IsRequired = true }); } //support path route ? description.SupportedRequestFormats.Add(new ApiRequestFormat() { MediaType = "application/json" }); description.SupportedResponseTypes.Add(new ApiResponseType() { ApiResponseFormats = new List <ApiResponseFormat>() { new ApiResponseFormat() { MediaType = "application/json" } }, ModelMetadata = metadataProvider .GetMetadataForType(/*methodInfo.ReturnType*/ typeof(System.Text.Json.JsonElement)), Type = methodInfo.ReturnType, StatusCode = (int)System.Net.HttpStatusCode.OK });; //InferRequestContentTypes(description); apiItems.Add(description); } var group = new ApiDescriptionGroup(groupName, new ReadOnlyCollection <ApiDescription>(apiItems)); groups.Add(group); } return(groups); }
private bool IsFromRoute(ControllerParameterDescriptor parameter) { return(parameter.ParameterInfo.GetCustomAttribute <FromRouteAttribute>() != null); }
/// <inheritdoc /> public async Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } // Special logic for body, treat the model name as string.Empty for the top level // object, but allow an override via BinderModelName. The purpose of this is to try // and be similar to the behavior for POCOs bound via traditional model binding. string modelBindingKey; if (bindingContext.IsTopLevelObject) { modelBindingKey = bindingContext.BinderModelName ?? string.Empty; } else { modelBindingKey = bindingContext.ModelName; } var httpContext = bindingContext.HttpContext; // Get the mapping source type and its metadata info var controllerParameters = bindingContext.ActionContext.ActionDescriptor.Parameters.OfType <ControllerParameterDescriptor>(); ControllerParameterDescriptor mapFromBodyParameter = null; MapFromBodyAttribute mapFromBodyAttribute = null; foreach (var param in controllerParameters) { mapFromBodyAttribute = param.ParameterInfo.GetCustomAttribute <MapFromBodyAttribute>(); if (mapFromBodyAttribute != null) { mapFromBodyParameter = param; break; } } // Set the formatter context for the mapped parameter if found InputFormatterContext formatterContext = null; if (mapFromBodyParameter == null || mapFromBodyAttribute == null) { formatterContext = new InputFormatterContext( httpContext, modelBindingKey, bindingContext.ModelState, bindingContext.ModelMetadata, _readerFactory); } else { var mappedModelMetadata = _modelMetadataProvider.GetMetadataForType(mapFromBodyAttribute.SourceType); formatterContext = new InputFormatterContext( httpContext, modelBindingKey, bindingContext.ModelState, mappedModelMetadata, _readerFactory); } var formatter = (IInputFormatter)null; for (var i = 0; i < _formatters.Count; i++) { if (_formatters[i].CanRead(formatterContext)) { formatter = _formatters[i]; break; } } if (formatter == null) { var message = string.Format("Unsupported content type '{0}'.", httpContext.Request.ContentType); var exception = new UnsupportedContentTypeException(message); bindingContext.ModelState.AddModelError(modelBindingKey, exception, bindingContext.ModelMetadata); return; } try { var previousCount = bindingContext.ModelState.ErrorCount; var result = await formatter.ReadAsync(formatterContext); var model = result.Model; if (result.HasError) { // Formatter encountered an error. Do not use the model it returned. return; } if (mapFromBodyParameter != null && mapFromBodyAttribute != null) { // Map the model to the desired model in the action model = _mapper.Map(model, mapFromBodyAttribute.SourceType, bindingContext.ModelType); } bindingContext.Result = ModelBindingResult.Success(model); return; } catch (Exception ex) { bindingContext.ModelState.AddModelError(modelBindingKey, ex, bindingContext.ModelMetadata); return; } }
/// <summary> /// Checks whether the a controller action matches the current route by comparing the parameters /// of the action with the data in the route. /// </summary> /// <param name="context">The current <see cref="RouteContext"/></param> /// <param name="actionDescriptor">The action descriptor</param> /// <param name="parameters">Parameters of the action. This excludes the <see cref="ODataPath"/> and <see cref="Query.ODataQueryOptions"/> parameters</param> /// <param name="availableKeys">The names of the keys found in the uri (entity set keys, related keys, operation parameters)</param> /// <param name="optionalWrapper">Used to check whether a parameter is optional</param> /// <param name="totalParameterCount">Total number of parameters in the action method</param> /// <param name="availableKeysCount">The number of key segments found in the uri. /// This might be less than the size of <paramref name="availableKeys"/> because some keys might have alias names</param> /// <returns></returns> private bool TryMatch( RouteContext context, ActionDescriptor actionDescriptor, IList <ParameterDescriptor> parameters, IList <string> availableKeys, ODataOptionalParameter optionalWrapper, int totalParameterCount, int availableKeysCount) { if (actionDescriptor is ControllerActionDescriptor controllerActionDescriptor) { // if this specific method was selected (e.g. via AttributeRouting) // then we return a match regardless of whether or not the parameters of // the method match the keys in the route if (context.RouteData.Values.TryGetValue(ODataRouteConstants.MethodInfo, out object method) && method is MethodInfo methodInfo) { if (controllerActionDescriptor.MethodInfo == methodInfo) { return(true); } } // if action has [EnableNestedPaths] attribute, then it doesn't // need to match parameters, since this action is expected to // match arbitrarily nested paths even if it doesn't have any parameters if (controllerActionDescriptor.MethodInfo .GetCustomAttributes <EnableNestedPathsAttribute>().Any()) { return(true); } } // navigationProperty is optional in some cases, therefore an action // should not be rejected simply because it does not declare a navigationProperty parameter if (availableKeys.Contains(ODataRouteConstants.NavigationProperty.ToLowerInvariant())) { availableKeysCount -= 1; } // reject action if it doesn't declare a parameter for each segment key // e.g. Get() will be rejected for route /Persons/1 if (totalParameterCount < availableKeysCount) { return(false); } bool matchedBody = false; IDictionary <string, object> conventionsStore = context.HttpContext.ODataFeature().RoutingConventionsStore; // use the parameter name to match. foreach (ParameterDescriptor p in parameters) { string parameterName = p.Name.ToLowerInvariant(); if (availableKeys.Contains(parameterName)) { continue; } if (conventionsStore != null) { // the convention store can contain the parameter as key // with a nested property (e.g. customer.Name) if (conventionsStore.Keys.Any(k => k.Contains(p.Name))) { continue; } } if (context.HttpContext.Request.Query.ContainsKey(p.Name)) { continue; } ControllerParameterDescriptor cP = p as ControllerParameterDescriptor; if (cP != null && optionalWrapper != null) { if (cP.ParameterInfo.IsOptional && optionalWrapper.OptionalParameters.Any(o => o.Name.ToLowerInvariant() == parameterName)) { continue; } } // if we can't find the parameter in the request, check whether // there's a special model binder registered to handle it if (ParameterHasRegisteredModelBinder(p)) { continue; } // if parameter is not bound to a key in the path, // assume that it's bound to the request body // only one parameter should be considered bound to the body if (!matchedBody && RequestHasBody(context)) { matchedBody = true; continue; } return(false); } return(true); }
/// <summary> /// Checks whether the a controller action matches the current route by comparing the parameters /// of the action with the data in the route. /// </summary> /// <param name="context">The current <see cref="RouteContext"/></param> /// <param name="parameters">Parameters of the action. This excludes the <see cref="ODataPath"/> and <see cref="Query.ODataQueryOptions"/> parameters</param> /// <param name="availableKeys">The names of the keys found in the uri (entity set keys, related keys, operation parameters)</param> /// <param name="optionalWrapper">Used to check whether a parameter is optional</param> /// <param name="totalParameterCount">Total number of parameters in the action method</param> /// <param name="availableKeysCount">The number of key segments found in the uri. /// This might be less than the size of <paramref name="availableKeys"/> because some keys might have alias names</param> /// <returns></returns> private bool TryMatch( RouteContext context, IList <ParameterDescriptor> parameters, IList <string> availableKeys, ODataOptionalParameter optionalWrapper, int totalParameterCount, int availableKeysCount) { // navigationProperty is optional in some cases, therefore an action // should not be rejected simply because it does not declare a navigationProperty parameter if (availableKeys.Contains(ODataRouteConstants.NavigationProperty.ToLowerInvariant())) { availableKeysCount -= 1; } // reject action if it doesn't declare a parameter for each segment key // e.g. Get() will be rejected for route /Persons/1 if (totalParameterCount < availableKeysCount) { return(false); } bool matchedBody = false; var conventionsStore = context.HttpContext.ODataFeature().RoutingConventionsStore; // use the parameter name to match. foreach (var p in parameters) { string parameterName = p.Name.ToLowerInvariant(); if (availableKeys.Contains(parameterName)) { continue; } if (conventionsStore != null) { // the convention store can contain the parameter as key // with a nested property (e.g. customer.Name) if (conventionsStore.Keys.Any(k => k.Contains(p.Name))) { continue; } } if (context.HttpContext.Request.Query.ContainsKey(p.Name)) { continue; } ControllerParameterDescriptor cP = p as ControllerParameterDescriptor; if (cP != null && optionalWrapper != null) { if (cP.ParameterInfo.IsOptional && optionalWrapper.OptionalParameters.Any(o => o.Name.ToLowerInvariant() == parameterName)) { continue; } } // if we can't find the parameter in the request, check whether // there's a special model binder registered to handle it if (ParameterHasRegisteredModelBinder(p)) { continue; } // if parameter is not bound to a key in the path, // assume that it's bound to the request body // only one parameter should be considered bound to the body if (!matchedBody && RequestHasBody(context)) { matchedBody = true; continue; } return(false); } return(true); }
/// <summary> /// Constructor /// </summary> /// <param name="operation">Operation</param> /// <param name="context">OperationFilterContext</param> public void Apply(Operation operation, OperationFilterContext context) { var pars = context.ApiDescription.ParameterDescriptions; foreach (var par in pars) { var swaggerParam = operation.Parameters.SingleOrDefault(p => p.Name == par.Name); ControllerParameterDescriptor parameterDescriptor = par.ParameterDescriptor as ControllerParameterDescriptor; System.Collections.Generic.IEnumerable <System.Reflection.CustomAttributeData> attributes = null; if (parameterDescriptor != null) { attributes = parameterDescriptor.ParameterInfo.CustomAttributes; } if (attributes != null && attributes.Count() > 0 && swaggerParam != null) { // Required - [Required] var requiredAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(RequiredAttribute)); if (requiredAttr != null) { swaggerParam.Required = true; } // Regex Pattern [RegularExpression] var regexAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(RegularExpressionAttribute)); if (regexAttr != null) { string regex = (string)regexAttr.ConstructorArguments[0].Value; if (swaggerParam is NonBodyParameter) { ((NonBodyParameter)swaggerParam).Pattern = regex; } } // String Length [StringLength] int?minLenght = null, maxLength = null; var stringLengthAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(StringLengthAttribute)); if (stringLengthAttr != null) { if (stringLengthAttr.NamedArguments.Count == 1) { minLenght = (int)stringLengthAttr.NamedArguments.Single(p => p.MemberName == "MinimumLength").TypedValue.Value; } maxLength = (int)stringLengthAttr.ConstructorArguments[0].Value; } var minLengthAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(MinLengthAttribute)); if (minLengthAttr != null) { minLenght = (int)minLengthAttr.ConstructorArguments[0].Value; } var maxLengthAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(MaxLengthAttribute)); if (maxLengthAttr != null) { maxLength = (int)maxLengthAttr.ConstructorArguments[0].Value; } if (swaggerParam is NonBodyParameter) { ((NonBodyParameter)swaggerParam).MinLength = minLenght; ((NonBodyParameter)swaggerParam).MaxLength = maxLength; } // Range [Range] var rangeAttr = attributes.FirstOrDefault(p => p.AttributeType == typeof(RangeAttribute)); if (rangeAttr != null) { int rangeMin = (int)rangeAttr.ConstructorArguments[0].Value; int rangeMax = (int)rangeAttr.ConstructorArguments[1].Value; if (swaggerParam is NonBodyParameter) { ((NonBodyParameter)swaggerParam).Minimum = rangeMin; ((NonBodyParameter)swaggerParam).Maximum = rangeMax; } } } } }