private OutboundRouteEntry CreateOutboundRouteEntry(
            RouteEndpoint endpoint,
            IReadOnlyDictionary <string, object> requiredValues,
            string routeName)
        {
            var entry = new OutboundRouteEntry()
            {
                Handler            = NullRouter.Instance,
                Order              = endpoint.Order,
                Precedence         = RoutePrecedence.ComputeOutbound(endpoint.RoutePattern),
                RequiredLinkValues = new RouteValueDictionary(requiredValues),
                RouteTemplate      = new RouteTemplate(endpoint.RoutePattern),
                Data      = endpoint,
                RouteName = routeName,
            };

            entry.Defaults = new RouteValueDictionary(endpoint.RoutePattern.Defaults);
            return(entry);
        }
Example #2
0
        private OutboundRouteEntry CreateOutboundRouteEntry(MatcherEndpoint endpoint)
        {
            var routeNameMetadata = endpoint.Metadata.GetMetadata <IRouteNameMetadata>();
            var entry             = new OutboundRouteEntry()
            {
                Handler            = NullRouter.Instance,
                Order              = endpoint.Order,
                Precedence         = RoutePrecedence.ComputeOutbound(endpoint.ParsedTemplate),
                RequiredLinkValues = endpoint.RequiredValues,
                RouteTemplate      = endpoint.ParsedTemplate,
                Data      = endpoint,
                RouteName = routeNameMetadata?.Name,
            };

            // TODO: review. These route constriants should be constructed when the endpoint
            // is built. This way they can be checked for validity on app startup too
            var constraintBuilder = new RouteConstraintBuilder(
                _inlineConstraintResolver,
                endpoint.ParsedTemplate.TemplateText);

            foreach (var parameter in endpoint.ParsedTemplate.Parameters)
            {
                if (parameter.InlineConstraints != null)
                {
                    if (parameter.IsOptional)
                    {
                        constraintBuilder.SetOptional(parameter.Name);
                    }

                    foreach (var constraint in parameter.InlineConstraints)
                    {
                        constraintBuilder.AddResolvedConstraint(parameter.Name, constraint.Constraint);
                    }
                }
            }
            entry.Constraints = constraintBuilder.Build();
            entry.Defaults    = endpoint.Defaults;
            return(entry);
        }
Example #3
0
    public OutboundRouteEntry MapOutbound(
        IRouter handler,
        RouteTemplate routeTemplate,
        RouteValueDictionary requiredLinkValues,
        string routeName,
        int order)
    {
        if (handler == null)
        {
            throw new ArgumentNullException(nameof(handler));
        }

        if (routeTemplate == null)
        {
            throw new ArgumentNullException(nameof(routeTemplate));
        }

        if (requiredLinkValues == null)
        {
            throw new ArgumentNullException(nameof(requiredLinkValues));
        }

        var entry = new OutboundRouteEntry()
        {
            Handler            = handler,
            Order              = order,
            Precedence         = RoutePrecedence.ComputeOutbound(routeTemplate),
            RequiredLinkValues = requiredLinkValues,
            RouteName          = routeName,
            RouteTemplate      = routeTemplate,
        };

        var constraintBuilder = new RouteConstraintBuilder(_constraintResolver, routeTemplate.TemplateText);

        foreach (var parameter in routeTemplate.Parameters)
        {
            if (parameter.InlineConstraints != null)
            {
                if (parameter.IsOptional)
                {
                    constraintBuilder.SetOptional(parameter.Name);
                }

                foreach (var constraint in parameter.InlineConstraints)
                {
                    constraintBuilder.AddResolvedConstraint(parameter.Name, constraint.Constraint);
                }
            }
        }

        entry.Constraints = constraintBuilder.Build();

        entry.Defaults = new RouteValueDictionary();
        foreach (var parameter in entry.RouteTemplate.Parameters)
        {
            if (parameter.DefaultValue != null)
            {
                entry.Defaults.Add(parameter.Name, parameter.DefaultValue);
            }
        }

        OutboundEntries.Add(entry);
        return(entry);
    }
Example #4
0
    private VirtualPathData GenerateVirtualPath(
        VirtualPathContext context,
        OutboundRouteEntry entry,
        TemplateBinder binder)
    {
        // In attribute the context includes the values that are used to select this entry - typically
        // these will be the standard 'action', 'controller' and maybe 'area' tokens. However, we don't
        // want to pass these to the link generation code, or else they will end up as query parameters.
        //
        // So, we need to exclude from here any values that are 'required link values', but aren't
        // parameters in the template.
        //
        // Ex:
        //      template: api/Products/{action}
        //      required values: { id = "5", action = "Buy", Controller = "CoolProducts" }
        //
        //      result: { id = "5", action = "Buy" }
        var inputValues = new RouteValueDictionary();

        foreach (var kvp in context.Values)
        {
            if (entry.RequiredLinkValues.ContainsKey(kvp.Key))
            {
                var parameter = entry.RouteTemplate.GetParameter(kvp.Key);

                if (parameter == null)
                {
                    continue;
                }
            }

            inputValues.Add(kvp.Key, kvp.Value);
        }

        var bindingResult = binder.GetValues(context.AmbientValues, inputValues);

        if (bindingResult == null)
        {
            // A required parameter in the template didn't get a value.
            return(null);
        }

        var matched = RouteConstraintMatcher.Match(
            entry.Constraints,
            bindingResult.CombinedValues,
            context.HttpContext,
            this,
            RouteDirection.UrlGeneration,
            _constraintLogger);

        if (!matched)
        {
            // A constraint rejected this link.
            return(null);
        }

        var pathData = entry.Handler.GetVirtualPath(context);

        if (pathData != null)
        {
            // If path is non-null then the target router short-circuited, we don't expect this
            // in typical MVC scenarios.
            return(pathData);
        }

        var path = binder.BindValues(bindingResult.AcceptedValues);

        if (path == null)
        {
            return(null);
        }

        return(new VirtualPathData(this, path));
    }