Exemple #1
0
    public void ParseManyVariables()
    {
        var pattern = HttpRoutePattern.Parse("/{a}/{b}/{c}");
        var adapter = JsonTranscodingRouteAdapter.Parse(pattern);

        Assert.Equal("/{a}/{b}/{c}", adapter.ResolvedRouteTemplate);
    }
Exemple #2
0
    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);
        });
    }
Exemple #5
0
    public void ParseVerb()
    {
        var pattern = HttpRoutePattern.Parse("/a:foo");
        var adapter = JsonTranscodingRouteAdapter.Parse(pattern);

        Assert.Equal("/a", adapter.ResolvedRouteTemplate);
        Assert.Empty(adapter.RewriteVariableActions);
    }
Exemple #6
0
    public void ParseAnySegment()
    {
        var pattern = HttpRoutePattern.Parse("/*") !;
        var adapter = JsonTranscodingRouteAdapter.Parse(pattern);

        Assert.Equal("/{__Discard_0}", adapter.ResolvedRouteTemplate);
        Assert.Empty(adapter.RewriteVariableActions);
    }
Exemple #7
0
    public void ParseComplexCatchAll()
    {
        var pattern = HttpRoutePattern.Parse("/a/{b=**}");
        var adapter = JsonTranscodingRouteAdapter.Parse(pattern);

        Assert.Equal("/a/{**b}", adapter.ResolvedRouteTemplate);
        Assert.Empty(adapter.RewriteVariableActions);
    }
Exemple #8
0
    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);
    }
Exemple #9
0
    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);
    }
Exemple #10
0
    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);
    }
Exemple #16
0
    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"]);
    }
Exemple #17
0
    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);
        });
    }
Exemple #23
0
    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 !
            });