/// <summary> /// Given a path, attempts to match the next part of it to the current segment. /// </summary> /// <param name="route">The route.</param> /// <param name="path">The path.</param> /// <returns> /// An object that indicates whether the path was successfully matched. /// </returns> public override SegmentPathMatch MatchPath(IRoute route, PathIterator path) { var values = new RouteValueDictionary(); var next = path.Next(); if (next.Length == 0) { if (defaultValue != UrlParameter.NotSpecified) { values[parameterName] = defaultValue; } else { return SegmentPathMatch.Failure(string.Format("The path does not contain a segment for parameter '{0}'", parameterName)); } } else { values[parameterName] = next; if (constraint != null && !constraint.IsValid(route, next, parameterName)) { return SegmentPathMatch.Failure(string.Format("Segment '{0}' did not match the constraint on parameter '{1}'", next, parameterName)); } } return SegmentPathMatch.Successful(values); }
/// <summary> /// Initializes a new instance of the <see cref="ModelBindingContext"/> class. /// </summary> /// <param name="targetParameterName">Name of the target parameter.</param> /// <param name="targetMethod">The target method.</param> /// <param name="targetParameterType">Type of the target parameter.</param> /// <param name="potentialValues">The potential values.</param> public ModelBindingContext(string targetParameterName, MethodInfo targetMethod, Type targetParameterType, RouteValueDictionary potentialValues) { this.targetParameterName = targetParameterName; this.targetMethod = targetMethod; this.targetParameterType = targetParameterType; this.potentialValues = potentialValues; }
/// <summary> /// Matches a collection of route values to a path. This method is primarily used when producing a /// link or navigating programmatically. /// </summary> /// <param name="values">The values.</param> /// <returns> /// A result containing the route, if matched, or a reason for failure if not matched. /// </returns> public PathMatch MatchRouteToPath(RouteValueDictionary values) { var routes = GetRoutes(); var fails = new List<PathMatch>(); if (routes.Count == 0) { return PathMatch.Failure(null, "No routes are registered in this route collection."); } foreach (var route in routes) { var match = route.MatchRouteToPath(values); if (match.Success) { return match; } fails.Add(match); } return PathMatch.Failure( null, string.Join(Environment.NewLine, fails.Select(fail => string.Format("- Route with specification '{0}' did not match: {1}.", fail.Route, fail.FailReason).CleanErrorMessage()).ToArray() )); }
/// <summary> /// Initializes a new instance of the <see cref="RouteMatch"/> class. /// </summary> /// <param name="success">if set to <c>true</c> [success].</param> /// <param name="route">The route.</param> /// <param name="parameterValues">The parameter values.</param> /// <param name="failReason">The fail reason.</param> private RouteMatch(bool success, IRoute route, IDictionary parameterValues, string failReason) { this.success = success; this.route = route; this.failReason = failReason; values = new RouteValueDictionary(parameterValues); }
private RouteValidationResult EnsureNoUnrecognizableSegments(Segment[] segments, RouteValueDictionary defaults, RouteValueDictionary constraints) { var unrecognized = segments.Where(x => SupportedSegmentTypes.Contains(x.GetType()) == false).ToList(); return unrecognized.Count > 0 ? RouteValidationResult.Failure(string.Format("Unrecognized segment types were found. If using a custom segment type, please replace the IRouteValidator to enforce your own route validation rules. Unrecognized types: {0}", string.Join(", ", unrecognized.Select(x => "'" + x.GetType().Name + "'").ToArray()))) : RouteValidationResult.Successful(); }
public void WhenConstructedWithObjectShouldUsePropertiesAsKeys() { var parameters = new RouteValueDictionary(new { foo = "bar", abc = "123"}); Assert.AreEqual(2, parameters.Count); Assert.AreEqual("bar", parameters["foo"]); Assert.AreEqual("123", parameters["abc"]); }
private static RouteValidationResult EnsureNoMoreThanOneCatchAllSegment(Segment[] segments, RouteValueDictionary defaults, RouteValueDictionary constraints) { var catchAll = segments.OfType<CatchAllParameterSegment>().ToList(); return catchAll.Count > 1 ? RouteValidationResult.Failure(string.Format("A route cannot have more than one catch-all parameter. Catch all parameters: {0}", string.Join(", ", catchAll.Select(x => "'" + x.ParameterName + "'").ToArray()))) : RouteValidationResult.Successful(); }
private static RouteValidationResult EnsureCatchAllOnlyAppearAtEnd(Segment[] segments, RouteValueDictionary defaults, RouteValueDictionary constraints) { var catchAll = segments.OfType<CatchAllParameterSegment>().ToList(); return catchAll.Count == 1 && segments.Last() != catchAll.Single() ? RouteValidationResult.Failure(string.Format("Catch-all parameters may only appear at the end of a route. Catch all parameter: '{0}'", catchAll.Single().ParameterName)) : RouteValidationResult.Successful(); }
public void RedirectResultShouldUseSuppliedNavigator() { var request = RequestBuilder.CreateRequest(); var newRequest = new RouteValueDictionary(new {controller = "Foo", action = "Bar", abc = "123"}); var result = new RedirectResult(newRequest); result.Execute(request.BuildControllerContext()); request.Navigator.Verify(x => x.ProcessRequest(It.IsAny<NavigationRequest>())); }
/// <summary> /// Tries to produce an appropriate segment for the given route specification part. Returning null /// indicates that the value is not valid for this kind of segment. /// </summary> /// <param name="value">The value.</param> /// <param name="defaults">The defaults.</param> /// <param name="constraints">The constraints.</param> /// <returns> /// The <see cref="Segment"/> produced by this <see cref="SegmentRecognizer"/>, or null if /// the value is not recognizable. /// </returns> public override Segment Recognise(string value, RouteValueDictionary defaults, RouteValueDictionary constraints) { var match = formatRegex.Match(value); if (!match.Success) return null; return Build(match, defaults, constraints); }
private static RouteValidationResult MustHaveActionSegment(Segment[] segments, RouteValueDictionary defaults, RouteValueDictionary constraints) { var hasController = segments.OfType<ParameterSegment>().Any(x => x.ParameterName == "action") || defaults.GetOrDefault<object>("action") != null; return hasController ? RouteValidationResult.Successful() : RouteValidationResult.Failure("The route does not contain an '{action}' segment, and no default action was provided."); }
/// <summary> /// Builds a segment from the regular expression match. This method is only called when the base /// class matches a the value to this regex. /// </summary> /// <param name="match">The regular expression match.</param> /// <param name="defaults">The default values used in the route.</param> /// <param name="constraints">The constraints used in the route.</param> /// <returns>The route segment.</returns> protected override Segment Build(Match match, RouteValueDictionary defaults, RouteValueDictionary constraints) { var placeholder = match.Groups[1].Value; return new CatchAllParameterSegment( placeholder, defaults.GetOrDefault<object>(placeholder, UrlParameter.NotSpecified), constraints.GetOrDefault<object>(placeholder, UrlParameter.NotSpecified) ); }
private static RouteValidationResult MustHaveViewModelSegment(Segment[] segments, RouteValueDictionary defaults, RouteValueDictionary constraints) { var hasViewModel = segments.OfType<ParameterSegment>().Any(x => x.ParameterName == "viewModel") || defaults.GetOrDefault<object>("viewModel") != null; return hasViewModel ? RouteValidationResult.Successful() : RouteValidationResult.Failure("The route does not contain a '{viewModel}' segment, and no default ViewModel type was provided."); }
/// <summary> /// Navigates to the specified controller and action, passing the parameters dictionary to the action, using a transiton. /// </summary> /// <param name="navigator">The navigator that will perform the navigation.</param> /// <param name="controller">The controller.</param> /// <param name="action">The action.</param> /// <param name="transition">The transition.</param> /// <param name="parameters">The parameters.</param> public static void NavigateWithTransition(this INavigator navigator, string controller, string action, string transition, object parameters) { Guard.ArgumentNotNull(navigator, "navigator"); var request = new RouteValueDictionary(parameters); request["controller"] = controller; request["action"] = action; request["Transition"] = transition; navigator.Navigate(request); }
/// <summary> /// Initializes a new instance of the <see cref="PathMatch"/> class. /// </summary> /// <param name="success">if set to <c>true</c> [success].</param> /// <param name="route">The route.</param> /// <param name="routeValues">The route values.</param> /// <param name="leftOver">The left over.</param> /// <param name="segmentValues">The segment values.</param> /// <param name="failReason">The fail reason.</param> private PathMatch(bool success, IRoute route, RouteValueDictionary routeValues, RouteValueDictionary leftOver, List<object> segmentValues, string failReason) { this.success = success; this.route = route; this.routeValues = routeValues; this.leftOver = leftOver; this.segmentValues = segmentValues; this.failReason = failReason; }
public void WhenEnumeratedShouldYieldKeyValuePairs() { var parameters = new RouteValueDictionary(new { foo = "bar", abc = "123" }); var items = parameters.Select(x => x).ToArray(); Assert.AreEqual(2, items.Count()); Assert.AreEqual("foo", items[0].Key); Assert.AreEqual("bar", items[0].Value); Assert.AreEqual("abc", items[1].Key); Assert.AreEqual("123", items[1].Value); }
public void WhenConstructedWithDictionaryShouldCopyItems() { var dictionary = new Dictionary<string, object>(); dictionary.Add("Key1", "Value1"); dictionary.Add("Key2", "Value2"); var parameters = new RouteValueDictionary(dictionary); Assert.AreEqual(2, parameters.Count); Assert.AreEqual("Value1", parameters["Key1"]); Assert.AreEqual("Value2", parameters["Key2"]); }
public void WhenItemsAreRemovedShouldRemoveItem() { var parameters = new RouteValueDictionary(new { foo = "bar", abc = "123" }); Assert.AreEqual(2, parameters.Count); Assert.AreEqual("bar", parameters["foo"]); Assert.AreEqual("123", parameters["abc"]); parameters.Remove("foo"); Assert.AreEqual(1, parameters.Count); Assert.AreEqual("123", parameters["abc"]); }
private RequestBuilder(string controllerName, string actionName, object routeValues) { _controllerName = controllerName; _actionName = actionName; _routeValues = new RouteValueDictionary(routeValues); NavigationService = new Mock<INavigationService>(); Navigator = new Mock<INavigator>(); Route = new Mock<IRoute>(); Controller = new Mock<IController>(); Path = "TestPath"; ProgressListeners = new List<INavigationProgressListener>(); Dispatcher = new SingleThreadDispatcher(); }
/// <summary> /// Initializes a new instance of the <see cref="ResolvedNavigationRequest"/> class. /// </summary> /// <param name="uri">The URI.</param> /// <param name="path">The path.</param> /// <param name="hasNonUriData">Indicates whether the URI is enough to get back to this request, or whether additional data lives in the route data.</param> /// <param name="navigator">The navigator.</param> /// <param name="route">The route.</param> /// <param name="routeData">The route data.</param> /// <param name="progressListeners">The progress listeners.</param> public ResolvedNavigationRequest(Uri uri, string path, bool hasNonUriData, INavigator navigator, IRoute route, RouteValueDictionary routeData, IEnumerable<INavigationProgressListener> progressListeners) { Guard.ArgumentNotNullOrEmpty(path, "path"); Guard.ArgumentNotNull(navigator, "navigator"); Guard.ArgumentNotNull(route, "route"); this.uri = uri; this.path = path; this.hasNonUriData = hasNonUriData; this.navigator = navigator; this.route = route; this.routeData = routeData ?? new RouteValueDictionary(); this.progressListeners = (progressListeners ?? new INavigationProgressListener[0]).ToList(); }
public ResolvedNavigationRequest BuildRequest() { Navigator.SetupGet(x => x.Dispatcher).Returns(Dispatcher); var values = new RouteValueDictionary(_routeValues); values["controller"] = _controllerName; values["action"] = _actionName; return new ResolvedNavigationRequest( new Uri("magellan://MyPath"), "MyPath", true, Navigator.Object, Route.Object, values, ProgressListeners ); }
private void ExpectNavigationRequest(string controllerName, string actionName, object parameters) { Navigator.Setup(x => x.ProcessRequest(It.IsAny<NavigationRequest>())).Callback( (NavigationRequest request) => { Assert.AreEqual(request.RouteData["action"], actionName); Assert.AreEqual(request.RouteData["controller"], controllerName); var expectedParameters = new RouteValueDictionary(parameters); foreach (var expectedParameter in expectedParameters) { var expected = expectedParameter.Value; var paramName = expectedParameter.Key; var actual = request.RouteData[expectedParameter.Key]; Assert.AreEqual(expected, actual, string.Format("Parameter {0}", paramName)); } }); }
/// <summary> /// Initializes a new instance of the <see cref="Route"/> class. /// </summary> /// <param name="routeSpecification">The path specification, which gives the pattern that paths will /// follow. For example, "/patients/{action}".</param> /// <param name="routeHandler">A delegate that will produce a route handler when needed for this /// route.</param> /// <param name="defaults">The default values of this route.</param> /// <param name="constraints">The constraints that will apply to this route.</param> /// <param name="parser">An object charged with parsing the route specification, producing a /// <see cref="ParsedRoute"/>.</param> /// <param name="validator">An object charged with validating the route configuration, ensuring the /// route doesn't violate any expectations around how a route can be declared.</param> public Route(string routeSpecification, Func<IRouteHandler> routeHandler, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteParser parser, IRouteValidator validator) { Guard.ArgumentNotNull(routeHandler, "routeHandler"); pathSpecification = routeSpecification ?? string.Empty; this.routeHandler = routeHandler; this.validator = validator ?? new RouteValidator(); this.defaults = defaults ?? new RouteValueDictionary(); this.constraints = constraints ?? new RouteValueDictionary(); this.parser = parser ?? new RouteParser( new ParameterSegmentRecognizer(), new LiteralSegmentRecognizer(), new CatchAllParameterSegmentRecognizer() ); }
/// <summary> /// Parses the given route specification, producing a <see cref="ParsedRoute"/>. /// </summary> /// <param name="route">The route.</param> /// <param name="routeSpecification">The route specification.</param> /// <param name="defaults">The defaults.</param> /// <param name="constraints">The constraints.</param> /// <returns></returns> public ParsedRoute Parse(IRoute route, string routeSpecification, RouteValueDictionary defaults, RouteValueDictionary constraints) { var parts = routeSpecification.SplitUrlPath(); var segments = new Segment[parts.Length]; for (var i = 0; i < parts.Length; i++) { var segment = recognisers.Select(x => x.Recognise(parts[i], defaults, constraints)).FirstOrDefault(x => x != null); if (segment == null) { throw new InvalidRouteException( route, string.Format("Invalid route: {0}. The route segment '{1}' was not recognized as a valid segment.", route, parts[i]) ); } segments[i] = segment; } return new ParsedRoute(defaults, constraints, segments); }
/// <summary> /// Matches the path. /// </summary> /// <param name="route">The route.</param> /// <param name="reader">The reader.</param> /// <returns></returns> public override SegmentPathMatch MatchPath(IRoute route, PathIterator reader) { var values = new RouteValueDictionary(); var path = reader.ReadAll(); values[parameterName] = path; if (path.Length == 0) { if (defaultValue != UrlParameter.NotSpecified) { values[parameterName] = defaultValue; } } else if (constraint != null && !constraint.IsValid(route, path, parameterName)) { return SegmentPathMatch.Failure(string.Format("Segment '{0}' did not match constraint for parameter '{1}'", path, parameterName)); } return SegmentPathMatch.Successful(values); }
/// <summary> /// Attempts to matche a path to this parsed route. /// </summary> /// <param name="route">The route.</param> /// <param name="request">The request.</param> /// <returns>An object indicating the success or failure of the attempt to match the path.</returns> public RouteMatch MatchPathToRoute(IRoute route, string request) { var values = new RouteValueDictionary(); var iterator = new PathIterator(request); foreach (var segment in Segments) { var match = segment.MatchPath(route, iterator); if (match.Success) { values.AddRange(match.Values); } else { return RouteMatch.Failure(route, match.FailReason); } } return iterator.IsAtEnd ? RouteMatch.Successful(route, values) : RouteMatch.Failure(route, "Route was initially matched, but the request contains additional unexpected segments"); }
/// <summary> /// Builds a segment from the regular expression match. This method is only called when the base /// class matches a the value to this regex. /// </summary> /// <param name="match">The regular expression match.</param> /// <param name="defaults">The default values used in the route.</param> /// <param name="constraints">The constraints used in the route.</param> /// <returns>The route segment.</returns> protected abstract Segment Build(Match match, RouteValueDictionary defaults, RouteValueDictionary constraints);
private void NavigateInternal(string controller, string action, object routeValues, NavigationDirections direction) { var request = new RouteValueDictionary(routeValues); request["MessageToken"] = MessageToken; _navigator.NavigateWithTransition(controller, action, direction.ToString(), request); }
/// <summary> /// Navigates to the specified URI, resolving the first matching route. /// </summary> /// <param name="requestUri">The request URI.</param> /// <param name="additionalData">Additional data (like post data) that is not in the URI but is used /// for navigation.</param> private void ExecuteRequestWithUri(Uri requestUri, RouteValueDictionary additionalData) { additionalData = additionalData ?? new RouteValueDictionary(); var path = requestUri.GetComponents(UriComponents.Host | UriComponents.Path, UriFormat.Unescaped); var queryString = requestUri.GetComponents(UriComponents.Query, UriFormat.Unescaped); var route = _routes.MatchPathToRoute(path); if (!route.Success) { throw new UnroutableRequestException(string.Format("The request URI '{0}' could not be routed. {1}{2}", requestUri, Environment.NewLine, route.FailReason)); } // Create a value dictionary with all of the known information - post data, query string data, // and data extracted from the route. We'll use this for the navigation request. var data = new RouteValueDictionary(additionalData); data.AddRange(route.Values, true); var queryValues = new QueryValueCollection(queryString); foreach (var pair in queryValues) { if (!data.ContainsKey(pair.Key)) { data[pair.Key] = pair.Value; } } var handler = route.Route.CreateRouteHandler(); handler.ProcessRequest( new ResolvedNavigationRequest( requestUri, path, additionalData.Count > 0, this, route.Route, data, _progressListeners.Union(Factory.ProgressListeners ?? new NavigationProgressListenerCollection()).ToList() )); }
/// <summary> /// Resolves and navigates to the first route that matches the given set of route values. /// </summary> /// <param name="request">The request.</param> private void ExecuteRequestWithoutUri(RouteValueDictionary request) { var path = _routes.MatchRouteToPath(request); if (!path.Success) { throw new UnroutableRequestException(string.Format("The request values '{0}' could not be routed. {1}{2}", request, Environment.NewLine, path.FailReason)); } var handler = path.Route.CreateRouteHandler(); var uriBuilder = new StringBuilder(); uriBuilder.Append(_scheme).Append("://").Append(path.Path); var queryValues = path.LeftOverValues.Where(x => x.Value is IFormattable || x.Value is string) .Select(x => x.Key + "=" + Uri.EscapeDataString(x.Value.ToString())) .ToArray(); if (queryValues.Length > 0) { uriBuilder.Append("?"); uriBuilder.Append(string.Join("&", queryValues)); } var uri = new Uri(uriBuilder.ToString()); handler.ProcessRequest( new ResolvedNavigationRequest( uri, path.Path, path.LeftOverValues.Count > queryValues.Length, this, path.Route, path.RouteValues, _progressListeners.Union(Factory.ProgressListeners ?? new NavigationProgressListenerCollection()).ToList() )); }