Skip to content

Commit c06a23a

Browse files
Abhinegi2sleidig
andauthored
fix: truncate filter params in URL to avoid HTTP 431 Error for overlong URLs (#2513)
fixes #2388 --------- Co-authored-by: Sebastian Leidig <[email protected]>
1 parent ffbc197 commit c06a23a

File tree

2 files changed

+67
-5
lines changed

2 files changed

+67
-5
lines changed

src/app/core/filter/filter/filter.component.spec.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import { TestbedHarnessEnvironment } from "@angular/cdk/testing/testbed";
77
import { HarnessLoader } from "@angular/cdk/testing";
88
import { MatSelectHarness } from "@angular/material/select/testing";
99
import { MockedTestingModule } from "../../../utils/mocked-testing.module";
10-
import { ActivatedRoute } from "@angular/router";
10+
import { ActivatedRoute, Router } from "@angular/router";
11+
import { BasicFilterConfig } from "../../entity-list/EntityListConfig";
12+
import { TestEntity } from "../../../utils/test-utils/TestEntity";
1113

1214
class ActivatedRouteMock {
1315
public snapshot = {
@@ -21,6 +23,7 @@ describe("FilterComponent", () => {
2123
let loader: HarnessLoader;
2224

2325
let activatedRouteMock = new ActivatedRouteMock();
26+
let router: Router;
2427

2528
beforeEach(async () => {
2629
activatedRouteMock.snapshot = {
@@ -36,7 +39,7 @@ describe("FilterComponent", () => {
3639
},
3740
],
3841
}).compileComponents();
39-
42+
router = TestBed.inject(Router);
4043
fixture = TestBed.createComponent(FilterComponent);
4144
loader = TestbedHarnessEnvironment.loader(fixture);
4245
component = fixture.componentInstance;
@@ -98,6 +101,40 @@ describe("FilterComponent", () => {
98101
expect(component.filterSelections[0].selectedOptionValues[1]).toBe("bar");
99102
});
100103

104+
it("should remove the longest filter option if URL length exceeds 2000 characters", async () => {
105+
const initialQueryParams = { dateOfBirth: "a,b" };
106+
activatedRouteMock.snapshot.queryParams = initialQueryParams;
107+
const routerNavigate = spyOn(router, "navigate");
108+
109+
component.entityType = TestEntity;
110+
component.entities = [];
111+
component.useUrlQueryParams = true;
112+
component.filterConfig = [{ id: "category" } as BasicFilterConfig];
113+
await component.ngOnChanges({ filterConfig: true } as any);
114+
115+
component.filterOptionSelected(component.filterSelections[0], [
116+
"categoryXX",
117+
"categoryYY",
118+
]);
119+
// filter options should be added to url
120+
expect(router.navigate).toHaveBeenCalledWith([], {
121+
relativeTo: activatedRouteMock as any,
122+
queryParams: { ...initialQueryParams, category: "categoryXX,categoryYY" },
123+
queryParamsHandling: "merge",
124+
});
125+
routerNavigate.calls.reset();
126+
127+
const longOptions = Array(250).fill("categoryZZ");
128+
component.filterOptionSelected(component.filterSelections[0], longOptions);
129+
// long options should be skipped, short options for other filter still present
130+
expect(router.navigate).toHaveBeenCalledWith([], {
131+
relativeTo: activatedRouteMock as any,
132+
queryParams: { ...initialQueryParams, category: undefined },
133+
queryParamsHandling: "merge",
134+
});
135+
routerNavigate.calls.reset();
136+
});
137+
101138
it("should load url params and set no filter value when empty", async () => {
102139
component.entityType = Note;
103140
component.useUrlQueryParams = true;

src/app/core/filter/filter/filter.component.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,36 @@ export class FilterComponent<T extends Entity = Entity> implements OnChanges {
113113
}
114114

115115
private updateUrl(key: string, value: string) {
116-
const params = {};
117-
params[key] = value;
116+
const MAX_URL_LENGTH = 2000;
117+
let queryParams = { ...this.route.snapshot.queryParams, [key]: value };
118+
119+
let potentialUrl = this.router
120+
.createUrlTree([], {
121+
relativeTo: this.route,
122+
queryParams,
123+
queryParamsHandling: "merge",
124+
})
125+
.toString();
126+
if (potentialUrl.length > MAX_URL_LENGTH) {
127+
let longestKey: string | null = null;
128+
let maxLength = 0;
129+
Object.keys(queryParams).forEach((key) => {
130+
if (queryParams[key].length > maxLength) {
131+
longestKey = key;
132+
maxLength = queryParams[key].length;
133+
}
134+
});
135+
136+
if (longestKey) {
137+
queryParams[longestKey] = undefined;
138+
} else {
139+
queryParams[key] = undefined;
140+
}
141+
}
142+
118143
this.router.navigate([], {
119144
relativeTo: this.route,
120-
queryParams: params,
145+
queryParams: queryParams,
121146
queryParamsHandling: "merge",
122147
});
123148
}

0 commit comments

Comments
 (0)