private string GetLink(MatcherEndpoint endpoint, LinkGeneratorContext context)
        {
            var templateBinder = new TemplateBinder(
                UrlEncoder.Default,
                _uriBuildingContextPool,
                new RouteTemplate(endpoint.RoutePattern),
                new RouteValueDictionary(endpoint.RoutePattern.Defaults));

            var templateValuesResult = templateBinder.GetValues(
                ambientValues: context.AmbientValues,
                explicitValues: context.ExplicitValues,
                endpoint.RequiredValues.Keys);

            if (templateValuesResult == null)
            {
                // We're missing one of the required values for this route.
                return(null);
            }

            if (!MatchesConstraints(context.HttpContext, endpoint, templateValuesResult.CombinedValues))
            {
                return(null);
            }

            var url = templateBinder.BindValues(templateValuesResult.AcceptedValues);

            return(Normalize(context, url));
        }
        public override bool TryGetLink(LinkGeneratorContext context, out string link)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            link = null;

            if (context.Endpoints == null)
            {
                return(false);
            }

            var matcherEndpoints = context.Endpoints.OfType <MatcherEndpoint>();

            if (!matcherEndpoints.Any())
            {
                //todo:log here
                return(false);
            }

            foreach (var endpoint in matcherEndpoints)
            {
                link = GetLink(endpoint, context);
                if (link != null)
                {
                    return(true);
                }
            }

            return(false);
        }
        public override string GetLink(LinkGeneratorContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (TryGetLink(context, out var link))
            {
                return(link);
            }

            throw new InvalidOperationException("Could not find a matching endpoint to generate a link.");
        }
        private string Normalize(LinkGeneratorContext context, string url)
        {
            var lowercaseUrls         = context.LowercaseUrls.HasValue ? context.LowercaseUrls.Value : _options.LowercaseUrls;
            var lowercaseQueryStrings = context.LowercaseQueryStrings.HasValue ?
                                        context.LowercaseQueryStrings.Value : _options.LowercaseQueryStrings;
            var appendTrailingSlash = context.AppendTrailingSlash.HasValue ?
                                      context.AppendTrailingSlash.Value : _options.AppendTrailingSlash;

            if (!string.IsNullOrEmpty(url) && (lowercaseUrls || appendTrailingSlash))
            {
                var indexOfSeparator      = url.IndexOfAny(UrlQueryDelimiters);
                var urlWithoutQueryString = url;
                var queryString           = string.Empty;

                if (indexOfSeparator != -1)
                {
                    urlWithoutQueryString = url.Substring(0, indexOfSeparator);
                    queryString           = url.Substring(indexOfSeparator);
                }

                if (lowercaseUrls)
                {
                    urlWithoutQueryString = urlWithoutQueryString.ToLowerInvariant();
                }

                if (lowercaseUrls && lowercaseQueryStrings)
                {
                    queryString = queryString.ToLowerInvariant();
                }

                if (appendTrailingSlash && !urlWithoutQueryString.EndsWith("/", StringComparison.Ordinal))
                {
                    urlWithoutQueryString += "/";
                }

                // queryString will contain the delimiter ? or # as the first character, so it's safe to append.
                url = urlWithoutQueryString + queryString;
            }

            return(url);
        }