public void ParseManyVariables() { var pattern = HttpRoutePattern.Parse("/{a}/{b}/{c}"); var adapter = JsonTranscodingRouteAdapter.Parse(pattern); Assert.Equal("/{a}/{b}/{c}", adapter.ResolvedRouteTemplate); }
public void ParseCatchAllVerb() { var pattern = HttpRoutePattern.Parse("/a/{b=*}/**:verb"); var adapter = JsonTranscodingRouteAdapter.Parse(pattern); Assert.Equal("/a/{b}/{**__Discard_2}", adapter.ResolvedRouteTemplate); }
public void ParseSlash() { var pattern = HttpRoutePattern.Parse("/"); Assert.Empty(pattern.Segments); Assert.Empty(pattern.Variables); }
public void ParseMultipleVariables() { var pattern = HttpRoutePattern.Parse("/shelves/{shelf}/books/{book}"); Assert.Null(pattern.Verb); Assert.Collection( pattern.Segments, s => Assert.Equal("shelves", s), s => Assert.Equal("*", s), s => Assert.Equal("books", s), s => Assert.Equal("*", s)); Assert.Collection( pattern.Variables, v => { Assert.Equal(1, v.StartSegment); Assert.Equal(2, v.EndSegment); Assert.Equal("shelf", string.Join(".", v.FieldPath)); Assert.False(v.HasCatchAllPath); }, v => { Assert.Equal(3, v.StartSegment); Assert.Equal(4, v.EndSegment); Assert.Equal("book", string.Join(".", v.FieldPath)); Assert.False(v.HasCatchAllPath); }); }
public void ParseVerb() { var pattern = HttpRoutePattern.Parse("/a:foo"); var adapter = JsonTranscodingRouteAdapter.Parse(pattern); Assert.Equal("/a", adapter.ResolvedRouteTemplate); Assert.Empty(adapter.RewriteVariableActions); }
public void ParseAnySegment() { var pattern = HttpRoutePattern.Parse("/*") !; var adapter = JsonTranscodingRouteAdapter.Parse(pattern); Assert.Equal("/{__Discard_0}", adapter.ResolvedRouteTemplate); Assert.Empty(adapter.RewriteVariableActions); }
public void ParseComplexCatchAll() { var pattern = HttpRoutePattern.Parse("/a/{b=**}"); var adapter = JsonTranscodingRouteAdapter.Parse(pattern); Assert.Equal("/a/{**b}", adapter.ResolvedRouteTemplate); Assert.Empty(adapter.RewriteVariableActions); }
public void ParseMultipleVariables() { var pattern = HttpRoutePattern.Parse("/shelves/{shelf}/books/{book}"); var adapter = JsonTranscodingRouteAdapter.Parse(pattern); Assert.Equal("/shelves/{shelf}/books/{book}", adapter.ResolvedRouteTemplate); Assert.Empty(adapter.RewriteVariableActions); }
public void ParseAnyAndCatchAllSegment2() { var pattern = HttpRoutePattern.Parse("/*/a/**"); var adapter = JsonTranscodingRouteAdapter.Parse(pattern); Assert.Equal("/{__Discard_0}/a/{**__Discard_2}", adapter.ResolvedRouteTemplate); Assert.Empty(adapter.RewriteVariableActions); }
public void ParseComplexNestedFieldPath() { var pattern = HttpRoutePattern.Parse("/a/{a.b.c=*}"); var adapter = JsonTranscodingRouteAdapter.Parse(pattern); Assert.Equal("/a/{a.b.c}", adapter.ResolvedRouteTemplate); Assert.Empty(adapter.RewriteVariableActions); }
public void ParseAnySegment() { var pattern = HttpRoutePattern.Parse("/*"); Assert.Collection( pattern.Segments, s => Assert.Equal("*", s)); Assert.Empty(pattern.Variables); }
public void ParseVerb() { var pattern = HttpRoutePattern.Parse("/a:foo"); Assert.Equal("foo", pattern.Verb); Assert.Collection( pattern.Segments, s => Assert.Equal("a", s)); Assert.Empty(pattern.Variables); }
public void ParseCatchAllSegment() { var pattern = HttpRoutePattern.Parse("/shelves/**"); Assert.Collection( pattern.Segments, s => Assert.Equal("shelves", s), s => Assert.Equal("**", s)); Assert.Empty(pattern.Variables); }
public void ParseAnyAndCatchAllSegment2() { var pattern = HttpRoutePattern.Parse("/*/a/**"); Assert.Collection( pattern.Segments, s => Assert.Equal("*", s), s => Assert.Equal("a", s), s => Assert.Equal("**", s)); Assert.Empty(pattern.Variables); }
private static HttpRouteVariable?GetVariable(HttpRoutePattern pattern, int i) { foreach (var variable in pattern.Variables) { if (i >= variable.StartSegment && i < variable.EndSegment) { return(variable); } } return(null); }
public void ParseComplexPathCatchAll() { var pattern = HttpRoutePattern.Parse("/a/{b=c/**}"); var adapter = JsonTranscodingRouteAdapter.Parse(pattern); Assert.Equal("/a/c/{**__Complex_b_2}", adapter.ResolvedRouteTemplate); var httpContext = new DefaultHttpContext(); httpContext.Request.RouteValues = new RouteValueDictionary { { "__Complex_b_2", "value" } }; adapter.RewriteVariableActions[0](httpContext); Assert.Equal("c/value", httpContext.Request.RouteValues["b"]); }
public void ParseComplexPrefixSuffixCatchAll() { var pattern = HttpRoutePattern.Parse("/{x.y.z=a/**/b}/c/d"); var adapter = JsonTranscodingRouteAdapter.Parse(pattern); Assert.Equal("/a/{**__Complex_x.y.z_1:regex(b/c/d$)}", adapter.ResolvedRouteTemplate); var httpContext = new DefaultHttpContext(); httpContext.Request.RouteValues = new RouteValueDictionary { { "__Complex_x.y.z_1", "my/value/b/c/d" } }; adapter.RewriteVariableActions[0](httpContext); Assert.Equal("a/my/value/b", httpContext.Request.RouteValues["x.y.z"]); }
public void ParseComplexCatchAll() { var pattern = HttpRoutePattern.Parse("/a/{b=**}"); Assert.Collection( pattern.Segments, s => Assert.Equal("a", s), s => Assert.Equal("**", s)); Assert.Collection( pattern.Variables, v => { Assert.Equal(1, v.StartSegment); Assert.Equal(2, v.EndSegment); Assert.Equal("b", string.Join(".", v.FieldPath)); Assert.True(v.HasCatchAllPath); }); }
public void ParseNestedFieldPath() { var pattern = HttpRoutePattern.Parse("/a/{a.b.c}"); Assert.Collection( pattern.Segments, s => Assert.Equal("a", s), s => Assert.Equal("*", s)); Assert.Collection( pattern.Variables, v => { Assert.Equal(1, v.StartSegment); Assert.Equal(2, v.EndSegment); Assert.Equal("a.b.c", string.Join(".", v.FieldPath)); Assert.False(v.HasCatchAllPath); }); }
private static JsonTranscodingServerCallContext CreateServerCallContext(DefaultHttpContext httpContext) { return(new JsonTranscodingServerCallContext( httpContext, MethodOptions.Create(Enumerable.Empty <GrpcServiceOptions>()), new Method <object, object>( MethodType.Unary, "Server", "Method", new Marshaller <object>(o => null !, c => null !), new Marshaller <object>(o => null !, c => null !)), new CallHandlerDescriptorInfo( null, null, false, null, new Dictionary <string, List <FieldDescriptor> >(), JsonTranscodingRouteAdapter.Parse(HttpRoutePattern.Parse("/") !)), NullLogger.Instance)); }
public void ParseCatchAllVerb() { var pattern = HttpRoutePattern.Parse("/a/{b=*}/**:verb"); Assert.Equal("verb", pattern.Verb); Assert.Collection( pattern.Segments, s => Assert.Equal("a", s), s => Assert.Equal("*", s), s => Assert.Equal("**", s)); Assert.Collection( pattern.Variables, v => { Assert.Equal(1, v.StartSegment); Assert.Equal(2, v.EndSegment); Assert.Equal("b", string.Join(".", v.FieldPath)); Assert.False(v.HasCatchAllPath); }); }
public void ParseComplexPrefixSuffixSegment() { var pattern = HttpRoutePattern.Parse("/a/{b=c/*/d}"); Assert.Collection( pattern.Segments, s => Assert.Equal("a", s), s => Assert.Equal("c", s), s => Assert.Equal("*", s), s => Assert.Equal("d", s)); Assert.Collection( pattern.Variables, v => { Assert.Equal(1, v.StartSegment); Assert.Equal(4, v.EndSegment); Assert.Equal("b", string.Join(".", v.FieldPath)); Assert.False(v.HasCatchAllPath); }); }
public void ParseComplexVariable() { var route = HttpRoutePattern.Parse("/v1/{book.name=shelves/*/books/*}"); var adapter = JsonTranscodingRouteAdapter.Parse(route); Assert.Equal("/v1/shelves/{__Complex_book.name_2}/books/{__Complex_book.name_4}", adapter.ResolvedRouteTemplate); Assert.Single(adapter.RewriteVariableActions); var httpContext = new DefaultHttpContext(); httpContext.Request.RouteValues = new RouteValueDictionary { { "__Complex_book.name_2", "first" }, { "__Complex_book.name_4", "second" } }; adapter.RewriteVariableActions[0](httpContext); Assert.Equal("shelves/first/books/second", httpContext.Request.RouteValues["book.name"]); }
public void ParseComplexPrefixSuffixCatchAll() { var pattern = HttpRoutePattern.Parse("/{x.y.z=a/**/b}/c/d"); Assert.Collection( pattern.Segments, s => Assert.Equal("a", s), s => Assert.Equal("**", s), s => Assert.Equal("b", s), s => Assert.Equal("c", s), s => Assert.Equal("d", s)); Assert.Collection( pattern.Variables, v => { Assert.Equal(0, v.StartSegment); Assert.Equal(3, v.EndSegment); Assert.Equal("x.y.z", string.Join(".", v.FieldPath)); Assert.True(v.HasCatchAllPath); }); }
public void ParseComplexVariable() { var pattern = HttpRoutePattern.Parse("/v1/{book.name=shelves/*/books/*}"); Assert.Null(pattern.Verb); Assert.Collection( pattern.Segments, s => Assert.Equal("v1", s), s => Assert.Equal("shelves", s), s => Assert.Equal("*", s), s => Assert.Equal("books", s), s => Assert.Equal("*", s)); Assert.Collection( pattern.Variables, v => { Assert.Equal(1, v.StartSegment); Assert.Equal(5, v.EndSegment); Assert.Equal("book.name", string.Join(".", v.FieldPath)); Assert.False(v.HasCatchAllPath); }); }
public static JsonTranscodingRouteAdapter Parse(HttpRoutePattern pattern) { var rewriteActions = new List <Action <HttpContext> >(); var tempSegments = pattern.Segments.ToList(); var i = 0; while (i < tempSegments.Count) { var segmentVariable = GetVariable(pattern, i); if (segmentVariable != null) { var fullPath = string.Join(".", segmentVariable.FieldPath); var segmentCount = segmentVariable.EndSegment - segmentVariable.StartSegment; if (segmentCount == 1) { // Single segment parameter. Include in route with its default name. tempSegments[i] = segmentVariable.HasCatchAllPath ? $"{{**{fullPath}}}" : $"{{{fullPath}}}"; i++; } else { var routeParameterParts = new List <string>(); var routeValueFormatTemplateParts = new List <string>(); var variableParts = new List <string>(); var haveCatchAll = false; var catchAllSuffix = string.Empty; while (i < segmentVariable.EndSegment && !haveCatchAll) { var segment = tempSegments[i]; var segmentType = GetSegmentType(segment); switch (segmentType) { case SegmentType.Literal: routeValueFormatTemplateParts.Add(segment); break; case SegmentType.Any: { var parameterName = $"__Complex_{fullPath}_{i}"; tempSegments[i] = $"{{{parameterName}}}"; routeValueFormatTemplateParts.Add($"{{{variableParts.Count}}}"); variableParts.Add(parameterName); break; } case SegmentType.CatchAll: { var parameterName = $"__Complex_{fullPath}_{i}"; var suffix = string.Join("/", tempSegments.Skip(i + 1)); catchAllSuffix = string.Join("/", tempSegments.Skip(i + segmentCount - 1)); // It's possible to have multiple routes with catch-all parameters that have different suffixes. // For example: // - /{name=v1/**/b}/one // - /{name=v1/**/b}/two // The suffix is added as a route constraint to avoid matching multiple routes to a request. var constraint = suffix.Length > 0 ? $":regex({suffix}$)" : string.Empty; tempSegments[i] = $"{{**{parameterName}{constraint}}}"; routeValueFormatTemplateParts.Add($"{{{variableParts.Count}}}"); variableParts.Add(parameterName); haveCatchAll = true; // Remove remaining segments. They have been added in the route constraint. while (i < tempSegments.Count - 1) { tempSegments.RemoveAt(tempSegments.Count - 1); } break; } } i++; } var routeValueFormatTemplate = string.Join("/", routeValueFormatTemplateParts); // Add an action to reconstruct the multiple segment parameter from ASP.NET Core // request route values. This should be called when the request is received. rewriteActions.Add(context => { var values = new object?[variableParts.Count]; for (var i = 0; i < values.Length; i++) { values[i] = context.Request.RouteValues[variableParts[i]]; } var finalValue = string.Format(CultureInfo.InvariantCulture, routeValueFormatTemplate, values); // Catch-all route parameter is always the last parameter. The original HTTP pattern could specify a // literal suffix after the catch-all, e.g. /{param=**}/suffix. Because ASP.NET Core routing provides // the entire remainder of the URL in the route value, we must trim the suffix from that route value. if (!string.IsNullOrEmpty(catchAllSuffix)) { finalValue = finalValue.Substring(0, finalValue.Length - catchAllSuffix.Length - 1); } context.Request.RouteValues[fullPath] = finalValue; }); } } else { // HTTP route can match any value in a segment without a parameter. // For example, v1/*/books. Add a parameter to match this behavior logic. // Parameter value is never used. var segmentType = GetSegmentType(tempSegments[i]); switch (segmentType) { case SegmentType.Literal: // Literal is unchanged. break; case SegmentType.Any: // Ignore any segment value. tempSegments[i] = $"{{__Discard_{i}}}"; break; case SegmentType.CatchAll: // Ignore remaining segment values. tempSegments[i] = $"{{**__Discard_{i}}}"; break; } i++; } } return(new JsonTranscodingRouteAdapter(pattern, "/" + string.Join("/", tempSegments), rewriteActions)); }
public void Error(string pattern, string errorMessage) { var ex = Assert.Throws <InvalidOperationException>(() => HttpRoutePattern.Parse(pattern)); Assert.Equal(errorMessage, ex.InnerException !.Message); }
private JsonTranscodingRouteAdapter(HttpRoutePattern httpRoutePattern, string resolvedRoutePattern, List <Action <HttpContext> > rewriteVariableActions) { HttpRoutePattern = httpRoutePattern; ResolvedRouteTemplate = resolvedRoutePattern; RewriteVariableActions = rewriteVariableActions; }
private static ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, HttpRule httpRule, MethodDescriptor methodDescriptor, string pattern, string verb) { var apiDescription = new ApiDescription(); apiDescription.HttpMethod = verb; apiDescription.ActionDescriptor = new ActionDescriptor { RouteValues = new Dictionary <string, string?> { // Swagger uses this to group endpoints together. // Group methods together using the service name. ["controller"] = methodDescriptor.Service.FullName }, EndpointMetadata = routeEndpoint.Metadata.ToList() }; apiDescription.RelativePath = pattern.TrimStart('/'); apiDescription.SupportedRequestFormats.Add(new ApiRequestFormat { MediaType = "application/json" }); apiDescription.SupportedResponseTypes.Add(new ApiResponseType { ApiResponseFormats = { new ApiResponseFormat { MediaType = "application/json" } }, ModelMetadata = new GrpcModelMetadata(ModelMetadataIdentity.ForType(methodDescriptor.OutputType.ClrType)), StatusCode = 200 }); var explorerSettings = routeEndpoint.Metadata.GetMetadata <ApiExplorerSettingsAttribute>(); if (explorerSettings != null) { apiDescription.GroupName = explorerSettings.GroupName; } var methodMetadata = routeEndpoint.Metadata.GetMetadata <GrpcMethodMetadata>() !; var httpRoutePattern = HttpRoutePattern.Parse(pattern); var routeParameters = ServiceDescriptorHelpers.ResolveRouteParameterDescriptors(httpRoutePattern.Variables.Select(v => v.FieldPath).ToList(), methodDescriptor.InputType); foreach (var routeParameter in routeParameters) { var field = routeParameter.Value.Last(); var parameterName = ServiceDescriptorHelpers.FormatUnderscoreName(field.Name, pascalCase: true, preservePeriod: false); var propertyInfo = field.ContainingType.ClrType.GetProperty(parameterName); // If from a property, create model as property to get its XML comments. var identity = propertyInfo != null ? ModelMetadataIdentity.ForProperty(propertyInfo, MessageDescriptorHelpers.ResolveFieldType(field), field.ContainingType.ClrType) : ModelMetadataIdentity.ForType(MessageDescriptorHelpers.ResolveFieldType(field)); apiDescription.ParameterDescriptions.Add(new ApiParameterDescription { Name = routeParameter.Key, ModelMetadata = new GrpcModelMetadata(identity), Source = BindingSource.Path, DefaultValue = string.Empty }); } var bodyDescriptor = ServiceDescriptorHelpers.ResolveBodyDescriptor(httpRule.Body, methodMetadata.ServiceType, methodDescriptor); if (bodyDescriptor != null) { // If from a property, create model as property to get its XML comments. var identity = bodyDescriptor.PropertyInfo != null ? ModelMetadataIdentity.ForProperty(bodyDescriptor.PropertyInfo, bodyDescriptor.Descriptor.ClrType, bodyDescriptor.PropertyInfo.DeclaringType !) : ModelMetadataIdentity.ForType(bodyDescriptor.Descriptor.ClrType); // Or if from a parameter, create model as parameter to get its XML comments. var parameterDescriptor = bodyDescriptor.ParameterInfo != null ? new ControllerParameterDescriptor { ParameterInfo = bodyDescriptor.ParameterInfo } : null; apiDescription.ParameterDescriptions.Add(new ApiParameterDescription { Name = "Input", ModelMetadata = new GrpcModelMetadata(identity), Source = BindingSource.Body, ParameterDescriptor = parameterDescriptor ! });