Repository navigation
fix(@angular/ssr): resolve parameters in redirect targets - #34321
bschaeublin wants to merge 2 commits into
Conversation
Route parameters in the path of a `redirectTo` target, such as `:id` in `profile/:id`, were sent unchanged in the `Location` header by the server, while the router replaces them with the matching parameters of the current URL. The server now does the same.
…argets Query parameters of an absolute `redirectTo` target whose value is a placeholder, such as `q=:term`, were sent unchanged in the `Location` header by the server, while the router replaces them with the values of the matching query parameters of the current URL. The server now does the same. Query parameters whose source parameter is missing in the current URL are left out, which matches the `undefined` value that the router assigns to them.
There was a problem hiding this comment.
Code Review
This pull request enhances Angular SSR's redirect handling by resolving route and query parameter placeholders in redirect targets, aligning its behavior with the Angular router. It introduces buildQueryWithParams to resolve query placeholders, supports positional placeholders (*) in buildPathWithParams, and adds corresponding unit tests. The review feedback highlights a potential runtime error in buildPathWithParams when positional placeholder indices are out of bounds, suggesting a fallback to empty strings. Additionally, it recommends using .startsWith(':') instead of direct index access [0] in both replaceRedirectParams and buildQueryWithParams to safely handle empty strings.
| const resolvedParts = toPathParts.map((part, index) => | ||
| toPathParts[index] === '*' ? fromPathParts[index] : part, | ||
| part === '*' | ||
| ? fromPathParts[index] | ||
| : part.replace(POSITIONAL_PLACEHOLDER_REGEXP, (_, position) => fromPathParts[position]), | ||
| ); |
There was a problem hiding this comment.
If fromPath has fewer segments than toPath, or if the positional placeholder index is out of bounds, fromPathParts[index] or fromPathParts[position] will be undefined. This will cause joinUrlParts to throw a TypeError when accessing part.length on the undefined value, or result in the string 'undefined' being inserted into the path. Adding a fallback to an empty string prevents these runtime errors.
| const resolvedParts = toPathParts.map((part, index) => | |
| toPathParts[index] === '*' ? fromPathParts[index] : part, | |
| part === '*' | |
| ? fromPathParts[index] | |
| : part.replace(POSITIONAL_PLACEHOLDER_REGEXP, (_, position) => fromPathParts[position]), | |
| ); | |
| const resolvedParts = toPathParts.map((part, index) => | |
| part === '*' | |
| ? (fromPathParts[index] ?? '') | |
| : part.replace(POSITIONAL_PLACEHOLDER_REGEXP, (_, position) => fromPathParts[position] ?? ''), | |
| ); |
There was a problem hiding this comment.
Not reachable: each */*<n> placeholder is derived from the position of a parameter in the matched route, and the route tree only matches when the request has a segment at each of those positions.
PR Checklist
Please check to confirm your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
Follow-up to #34301 (comment).
Parameters in a
redirectTotarget are sent unchanged in the server'sLocationheader, while the router replaces them:Locationuser/:id→profile/:id/user/7/profile/:id/profile/7search/:term→/find?q=:x/search/cats?x=dogs/find?q=:x/find?q=dogsWhat is the new behavior?
The server resolves both, one commit each:
:namein the redirect path becomes*<n>, the position of that parameter in the request path, whichbuildPathWithParamsresolves at request time. Prerendered routes get the value fromgetPrerenderParams.q=:xtakes the values ofxfrom the request query.Does this PR introduce a breaking change?
Other information
Without
xin the request, the server redirects to/find. The router would go to/find?q=undefined.