Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0fba047
move anti-forgery to shared for jQuery demos
GoodDayForSurf Jan 26, 2026
bcff9b9
Merge branch '25_2' into 25_2_demos_anti_froge
GoodDayForSurf Jan 27, 2026
e779b8f
Merge branch '25_2' into 25_2_demos_anti_froge
GoodDayForSurf Jan 27, 2026
2f39a44
move anti-forgery to shared for Vue demos
GoodDayForSurf Jan 27, 2026
2d85d35
Merge branch '25_2' into 25_2_demos_anti_froge
GoodDayForSurf Jan 27, 2026
987ac67
Merge branch '25_2_demos_anti_froge' of https://github.com/GoodDayFor…
GoodDayForSurf Jan 27, 2026
cfaf4a3
move anti-forgery to shared for Angular demo
GoodDayForSurf Jan 27, 2026
6927ead
move anti-forgery to shared for React demo
GoodDayForSurf Jan 27, 2026
e23bf90
use anti-forgery by map systemjs
GoodDayForSurf Jan 28, 2026
ea6a6e9
use anti-forgery by map systemjs
GoodDayForSurf Jan 28, 2026
6cfe215
use anti-forgery by map systemjs
GoodDayForSurf Jan 28, 2026
af8f66c
fix lint
GoodDayForSurf Jan 28, 2026
127542b
fix lint
GoodDayForSurf Jan 28, 2026
ea86016
fix ReactJs demo
GoodDayForSurf Jan 28, 2026
2d12f4a
insert anti-forgery checking in devextreme ajax
GoodDayForSurf Jan 29, 2026
a7af0a9
regenerate and fix lint
GoodDayForSurf Jan 29, 2026
7956e3d
Merge branch '25_2' into 25_2_demos_anti_froge
GoodDayForSurf Jan 29, 2026
ecdcd21
fix anti-forgery for jquery
GoodDayForSurf Jan 29, 2026
31cbba1
Merge remote-tracking branch 'my/25_2_demos_anti_froge' into 25_2_dem…
GoodDayForSurf Jan 29, 2026
ae49d03
fix lint in ReactJs demo
GoodDayForSurf Jan 30, 2026
b6508f2
fix lint warnings
GoodDayForSurf Jan 30, 2026
a079403
fix lint warnings
GoodDayForSurf Jan 30, 2026
89fa1a9
fix lint warnings
GoodDayForSurf Jan 30, 2026
c7cae1b
fix lint warnings
GoodDayForSurf Jan 30, 2026
ab2c22c
Merge branch '25_2' into 25_2_demos_anti_froge
GoodDayForSurf Jan 30, 2026
582a354
fix lint warnings
GoodDayForSurf Jan 30, 2026
ee4ce83
Merge remote-tracking branch 'my/25_2_demos_anti_froge' into 25_2_dem…
GoodDayForSurf Jan 30, 2026
0fed61d
fix lint warnings
GoodDayForSurf Jan 30, 2026
873d068
fix lint warnings
GoodDayForSurf Jan 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { bootstrapApplication } from '@angular/platform-browser';
import { Component, enableProdMode, provideZoneChangeDetection } from '@angular/core';
import { HttpClient, provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';
import { HttpClient, provideHttpClient, withFetch } from '@angular/common/http';
import { lastValueFrom } from 'rxjs';
import * as AspNetData from 'devextreme-aspnet-data-nojquery';
import { DxDataGridComponent, DxDataGridModule, DxDataGridTypes } from 'devextreme-angular/ui/data-grid';
import { antiForgeryInterceptor, AntiForgeryTokenService } from './app.service';
import 'anti-forgery';

if (!/localhost/.test(document.location.host)) {
enableProdMode();
Expand All @@ -30,15 +30,13 @@ if (window && window.config?.packageConfigPaths) {
export class AppComponent {
ordersStore: AspNetData.CustomStore;

constructor(private http: HttpClient, private tokenService: AntiForgeryTokenService) {
constructor(private http: HttpClient) {
this.ordersStore = AspNetData.createStore({
key: 'OrderID',
loadUrl: `${URL}/Orders`,
async onBeforeSend(_method, ajaxOptions) {
const tokenData = await lastValueFrom(tokenService.getToken());
ajaxOptions.xhrFields = {
withCredentials: true,
headers: { [tokenData.headerName]: tokenData.token },
};
},
});
Expand Down Expand Up @@ -108,7 +106,6 @@ bootstrapApplication(AppComponent, {
provideZoneChangeDetection({ eventCoalescing: true, runCoalescing: true }),
provideHttpClient(
withFetch(),
withInterceptors([antiForgeryInterceptor]),
),
],
});

This file was deleted.

74 changes: 14 additions & 60 deletions apps/demos/Demos/DataGrid/BatchUpdateRequest/React/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,52 +6,12 @@ import 'whatwg-fetch';

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

async function fetchAntiForgeryToken(): Promise<{ headerName: string; token: string }> {
try {
const response = await fetch(`${BASE_PATH}/api/Common/GetAntiForgeryToken`, {
method: 'GET',
credentials: 'include',
cache: 'no-cache',
});

if (!response.ok) {
const errorMessage = await response.text();
throw new Error(`Failed to retrieve anti-forgery token: ${errorMessage || response.statusText}`);
}

return await response.json();
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
throw new Error(errorMessage);
}
}

async function getAntiForgeryTokenValue(): Promise<{ headerName: string; token: string }> {
const tokenMeta = document.querySelector<HTMLMetaElement>('meta[name="csrf-token"]');
if (tokenMeta) {
const headerName = tokenMeta.dataset.headerName || 'RequestVerificationToken';
const token = tokenMeta.getAttribute('content') || '';
return Promise.resolve({ headerName, token });
}

const tokenData = await fetchAntiForgeryToken();
const meta = document.createElement('meta');
meta.name = 'csrf-token';
meta.content = tokenData.token;
meta.dataset.headerName = tokenData.headerName;
document.head.appendChild(meta);
return tokenData;
}

const ordersStore = createStore({
key: 'OrderID',
loadUrl: `${URL}/Orders`,
async onBeforeSend(_method, ajaxOptions) {
const tokenData = await getAntiForgeryTokenValue();
ajaxOptions.xhrFields = {
withCredentials: true,
headers: { [tokenData.headerName]: tokenData.token },
};
},
});
Expand Down Expand Up @@ -81,31 +41,25 @@ function normalizeChanges(changes: DataGridTypes.DataChange[]): DataGridTypes.Da
}) as DataGridTypes.DataChange[];
}

async function sendBatchRequest(url: string, changes: DataGridTypes.DataChange[], headers: Record<string, string>) {
try {
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify(changes),
headers: {
'Content-Type': 'application/json;charset=UTF-8',
...headers,
},
credentials: 'include',
});
async function sendBatchRequest(url: string, changes: DataGridTypes.DataChange[]) {
const result = await fetch(url, {
method: 'POST',
body: JSON.stringify(changes),
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
credentials: 'include',
});

if (!response.ok) {
const errorMessage = await response.text();
throw new Error(`Batch save failed: ${errorMessage || response.statusText}`);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
throw new Error(errorMessage);
if (!result.ok) {
const json = await result.json();

throw json.Message;
}
}

async function processBatchRequest(url: string, changes: DataGridTypes.DataChange[], component: ReturnType<DataGridRef['instance']>) {
const tokenData = await getAntiForgeryTokenValue();
await sendBatchRequest(url, changes, { [tokenData.headerName]: tokenData.token });
await sendBatchRequest(url, changes);
await component.refresh(true);
component.cancelEditData();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import ReactDOM from 'react-dom';

// eslint-disable-next-line import/no-unresolved
import 'anti-forgery';
import App from './App.tsx';

ReactDOM.render(
Expand Down
69 changes: 13 additions & 56 deletions apps/demos/Demos/DataGrid/BatchUpdateRequest/ReactJs/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,48 +5,12 @@ import 'whatwg-fetch';

const BASE_PATH = 'https://js.devexpress.com/Demos/NetCore';
const URL = `${BASE_PATH}/api/DataGridBatchUpdateWebApi`;
async function fetchAntiForgeryToken() {
try {
const response = await fetch(`${BASE_PATH}/api/Common/GetAntiForgeryToken`, {
method: 'GET',
credentials: 'include',
cache: 'no-cache',
});
if (!response.ok) {
const errorMessage = await response.text();
throw new Error(
`Failed to retrieve anti-forgery token: ${errorMessage || response.statusText}`,
);
}
return await response.json();
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
throw new Error(errorMessage);
}
}
async function getAntiForgeryTokenValue() {
const tokenMeta = document.querySelector('meta[name="csrf-token"]');
if (tokenMeta) {
const headerName = tokenMeta.dataset.headerName || 'RequestVerificationToken';
const token = tokenMeta.getAttribute('content') || '';
return Promise.resolve({ headerName, token });
}
const tokenData = await fetchAntiForgeryToken();
const meta = document.createElement('meta');
meta.name = 'csrf-token';
meta.content = tokenData.token;
meta.dataset.headerName = tokenData.headerName;
document.head.appendChild(meta);
return tokenData;
}
const ordersStore = createStore({
key: 'OrderID',
loadUrl: `${URL}/Orders`,
async onBeforeSend(_method, ajaxOptions) {
const tokenData = await getAntiForgeryTokenValue();
ajaxOptions.xhrFields = {
withCredentials: true,
headers: { [tokenData.headerName]: tokenData.token },
};
},
});
Expand Down Expand Up @@ -74,29 +38,22 @@ function normalizeChanges(changes) {
}
});
}
async function sendBatchRequest(url, changes, headers) {
try {
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify(changes),
headers: {
'Content-Type': 'application/json;charset=UTF-8',
...headers,
},
credentials: 'include',
});
if (!response.ok) {
const errorMessage = await response.text();
throw new Error(`Batch save failed: ${errorMessage || response.statusText}`);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
throw new Error(errorMessage);
async function sendBatchRequest(url, changes) {
const result = await fetch(url, {
method: 'POST',
body: JSON.stringify(changes),
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
credentials: 'include',
});
if (!result.ok) {
const json = await result.json();
throw json.Message;
}
}
async function processBatchRequest(url, changes, component) {
const tokenData = await getAntiForgeryTokenValue();
await sendBatchRequest(url, changes, { [tokenData.headerName]: tokenData.token });
await sendBatchRequest(url, changes);
await component.refresh(true);
component.cancelEditData();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
// eslint-disable-next-line import/no-unresolved
import 'anti-forgery';
import App from './App.js';

ReactDOM.render(<App />, document.getElementById('app'));
Loading
Loading