Skip to content

Commit f2d38f5

Browse files
Demos - Move Anti Forgery implementation to shared files (DataGrid / BatchUpdateRequest) (#32589)
1 parent ed4180a commit f2d38f5

File tree

17 files changed

+283
-422
lines changed

17 files changed

+283
-422
lines changed

apps/demos/Demos/DataGrid/BatchUpdateRequest/Angular/app/app.component.ts

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
import { bootstrapApplication } from '@angular/platform-browser';
22
import { Component, enableProdMode, provideZoneChangeDetection } from '@angular/core';
3-
import { HttpClient, provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';
3+
import { HttpClient, provideHttpClient, withFetch } from '@angular/common/http';
44
import { lastValueFrom } from 'rxjs';
55
import * as AspNetData from 'devextreme-aspnet-data-nojquery';
66
import { DxDataGridComponent, DxDataGridModule, DxDataGridTypes } from 'devextreme-angular/ui/data-grid';
7-
import { antiForgeryInterceptor, AntiForgeryTokenService } from './app.service';
7+
import 'anti-forgery';
88

99
if (!/localhost/.test(document.location.host)) {
1010
enableProdMode();
1111
}
1212

13-
const BASE_PATH = 'https://js.devexpress.com/Demos/NetCore';
14-
const URL = `${BASE_PATH}/api/DataGridBatchUpdateWebApi`;
13+
const URL = 'https://js.devexpress.com/Demos/NetCore/api/DataGridBatchUpdateWebApi';
1514

1615
let modulePrefix = '';
1716
// @ts-ignore
@@ -30,16 +29,12 @@ if (window && window.config?.packageConfigPaths) {
3029
export class AppComponent {
3130
ordersStore: AspNetData.CustomStore;
3231

33-
constructor(private http: HttpClient, private tokenService: AntiForgeryTokenService) {
32+
constructor(private http: HttpClient) {
3433
this.ordersStore = AspNetData.createStore({
3534
key: 'OrderID',
3635
loadUrl: `${URL}/Orders`,
37-
async onBeforeSend(_method, ajaxOptions) {
38-
const tokenData = await lastValueFrom(tokenService.getToken());
39-
ajaxOptions.xhrFields = {
40-
withCredentials: true,
41-
headers: { [tokenData.headerName]: tokenData.token },
42-
};
36+
onBeforeSend(method, ajaxOptions) {
37+
ajaxOptions.xhrFields = { withCredentials: true };
4338
},
4439
});
4540
}
@@ -58,23 +53,16 @@ export class AppComponent {
5853
changes: DxDataGridTypes.DataChange[],
5954
component: DxDataGridComponent['instance'],
6055
): Promise<void> {
61-
try {
62-
await lastValueFrom(
63-
this.http.post(url, JSON.stringify(changes), {
64-
withCredentials: true,
65-
headers: {
66-
'Content-Type': 'application/json',
67-
},
68-
}),
69-
);
70-
await component.refresh(true);
71-
component.cancelEditData();
72-
} catch (error: any) {
73-
const errorMessage = (typeof error?.error === 'string' && error.error)
74-
? error.error
75-
: (error?.statusText || 'Unknown error');
76-
throw new Error(`Batch save failed: ${errorMessage}`);
77-
}
56+
await lastValueFrom(
57+
this.http.post(url, JSON.stringify(changes), {
58+
withCredentials: true,
59+
headers: {
60+
'Content-Type': 'application/json',
61+
},
62+
}),
63+
);
64+
await component.refresh(true);
65+
component.cancelEditData();
7866
}
7967

8068
normalizeChanges(changes: DxDataGridTypes.DataChange[]): DxDataGridTypes.DataChange[] {
@@ -106,9 +94,6 @@ export class AppComponent {
10694
bootstrapApplication(AppComponent, {
10795
providers: [
10896
provideZoneChangeDetection({ eventCoalescing: true, runCoalescing: true }),
109-
provideHttpClient(
110-
withFetch(),
111-
withInterceptors([antiForgeryInterceptor]),
112-
),
97+
provideHttpClient(withFetch()),
11398
],
11499
});

apps/demos/Demos/DataGrid/BatchUpdateRequest/Angular/app/app.service.ts

Lines changed: 0 additions & 104 deletions
This file was deleted.

apps/demos/Demos/DataGrid/BatchUpdateRequest/React/App.tsx

Lines changed: 17 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -4,55 +4,13 @@ import type { DataGridRef, DataGridTypes } from 'devextreme-react/data-grid';
44
import { createStore } from 'devextreme-aspnet-data-nojquery';
55
import 'whatwg-fetch';
66

7-
const BASE_PATH = 'https://js.devexpress.com/Demos/NetCore';
8-
const URL = `${BASE_PATH}/api/DataGridBatchUpdateWebApi`;
9-
10-
async function fetchAntiForgeryToken(): Promise<{ headerName: string; token: string }> {
11-
try {
12-
const response = await fetch(`${BASE_PATH}/api/Common/GetAntiForgeryToken`, {
13-
method: 'GET',
14-
credentials: 'include',
15-
cache: 'no-cache',
16-
});
17-
18-
if (!response.ok) {
19-
const errorMessage = await response.text();
20-
throw new Error(`Failed to retrieve anti-forgery token: ${errorMessage || response.statusText}`);
21-
}
22-
23-
return await response.json();
24-
} catch (error) {
25-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
26-
throw new Error(errorMessage);
27-
}
28-
}
29-
30-
async function getAntiForgeryTokenValue(): Promise<{ headerName: string; token: string }> {
31-
const tokenMeta = document.querySelector<HTMLMetaElement>('meta[name="csrf-token"]');
32-
if (tokenMeta) {
33-
const headerName = tokenMeta.dataset.headerName || 'RequestVerificationToken';
34-
const token = tokenMeta.getAttribute('content') || '';
35-
return Promise.resolve({ headerName, token });
36-
}
37-
38-
const tokenData = await fetchAntiForgeryToken();
39-
const meta = document.createElement('meta');
40-
meta.name = 'csrf-token';
41-
meta.content = tokenData.token;
42-
meta.dataset.headerName = tokenData.headerName;
43-
document.head.appendChild(meta);
44-
return tokenData;
45-
}
7+
const URL = 'https://js.devexpress.com/Demos/NetCore/api/DataGridBatchUpdateWebApi';
468

479
const ordersStore = createStore({
4810
key: 'OrderID',
4911
loadUrl: `${URL}/Orders`,
50-
async onBeforeSend(_method, ajaxOptions) {
51-
const tokenData = await getAntiForgeryTokenValue();
52-
ajaxOptions.xhrFields = {
53-
withCredentials: true,
54-
headers: { [tokenData.headerName]: tokenData.token },
55-
};
12+
onBeforeSend: (method, ajaxOptions) => {
13+
ajaxOptions.xhrFields = { withCredentials: true };
5614
},
5715
});
5816

@@ -81,31 +39,25 @@ function normalizeChanges(changes: DataGridTypes.DataChange[]): DataGridTypes.Da
8139
}) as DataGridTypes.DataChange[];
8240
}
8341

84-
async function sendBatchRequest(url: string, changes: DataGridTypes.DataChange[], headers: Record<string, string>) {
85-
try {
86-
const response = await fetch(url, {
87-
method: 'POST',
88-
body: JSON.stringify(changes),
89-
headers: {
90-
'Content-Type': 'application/json;charset=UTF-8',
91-
...headers,
92-
},
93-
credentials: 'include',
94-
});
42+
async function sendBatchRequest(url: string, changes: DataGridTypes.DataChange[]) {
43+
const result = await fetch(url, {
44+
method: 'POST',
45+
body: JSON.stringify(changes),
46+
headers: {
47+
'Content-Type': 'application/json;charset=UTF-8',
48+
},
49+
credentials: 'include',
50+
});
9551

96-
if (!response.ok) {
97-
const errorMessage = await response.text();
98-
throw new Error(`Batch save failed: ${errorMessage || response.statusText}`);
99-
}
100-
} catch (error) {
101-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
102-
throw new Error(errorMessage);
52+
if (!result.ok) {
53+
const json = await result.json();
54+
55+
throw json.Message;
10356
}
10457
}
10558

10659
async function processBatchRequest(url: string, changes: DataGridTypes.DataChange[], component: ReturnType<DataGridRef['instance']>) {
107-
const tokenData = await getAntiForgeryTokenValue();
108-
await sendBatchRequest(url, changes, { [tokenData.headerName]: tokenData.token });
60+
await sendBatchRequest(url, changes);
10961
await component.refresh(true);
11062
component.cancelEditData();
11163
}

apps/demos/Demos/DataGrid/BatchUpdateRequest/React/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React from 'react';
22
import ReactDOM from 'react-dom';
33

44
import App from './App.tsx';
5+
// eslint-disable-next-line import/no-unresolved
6+
import 'anti-forgery';
57

68
ReactDOM.render(
79
<App />,

0 commit comments

Comments
 (0)