I have an API project that handles all the logic/data handling for a separate Razor Pages site. I've configured CORS on the API project, and I can now successfully call an endpoint from a page's code file:
public async Task OnGetAsync()
{
var client = _clientFactory.CreateClient("ApiClient");
var response = await client.GetAsync("http://localhost:5122/vendors");
var vendors = await response.Content.ReadAsStringAsync();
ViewData["Vendors"] = vendors;
}
When I try to consume the same endpoint from a DataTables component, I get blocked by CORS. I'm assuming I need to configure DataTables to adjust how it makes it's ajax call, but I can't find how to make it work.
The error message: Access to XMLHttpRequest at 'http://localhost:5122/vendors?_=1738157999876' from origin 'http://localhost:5256' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
But when I look at the headers, the response header shows the location as http://localhost:5122/Identity/Account/Login?ReturnUrl=%2Fvendors%3F_%3D1738158369219 so I'm assuming it's an authentication issue. As the authentication is configured using cookies, I've tried finding a way to attach the cookie to the js request in the Datatables code, but no matter what I tried I couldn't get it to work (I eventually found an article that said to change the cookie to HttpOnly = false, then read the cookie value and create a new cookie with the required name and value, but that didn't work either).
This is how the authentication on the API is configured:
internal static class IdentityServices
{
public static IServiceCollection AddIdentityServices(this IServiceCollection services, WebApplicationBuilder builder)
{
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(builder.Environment.ContentRootPath + "/Identity"))
.SetApplicationName("MarketManager");
services
.AddDefaultIdentity<Person>()
.AddRoles<AppRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = ".MarketManager.SharedCookie";
options.Cookie.Path = "/";
});
services.AddAuthentication();
services.AddAuthorization();
services.AddHttpContextAccessor();
return services;
}
}
Here is the API configuration:
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowApiOrigin",
builder =>
{
builder.WithOrigins("http://localhost:5256")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
var app = builder.Build();
app.UseCors("AllowApiOrigin");
app.MapGroup("/vendors")
.MapVendors();
app.Run();
And this is how I'm calling the endpoint in JS:
$(document).ready(function () {
datatable = $('#dataTable').DataTable({
ajax: {
url: 'http://localhost:5122/vendors',
dataType: 'json',
dataSrc: ''
},
columns: [
{ data: 'id' }
]
})
});
The endpoint call:
internal static class VendorEndpoints
{
public static RouteGroupBuilder MapVendors(this RouteGroupBuilder app)
{
app.MapGet("/", GetAllVendorsAsync);
app.WithTags("Vendors");
return app;
}
public static async Task<IResult> GetAllVendorsAsync(
[FromServices] ISender sender) =>
TypedResults.Ok(
await sender.Send(
new GetAllVendors.Query()));
}
I suggest you could modify the jquery datatable request as below :
$(document).ready(function () {
datatable = $('#dataTable').DataTable({
ajax: {
url: 'http://localhost:5122/vendors',
dataType: 'json',
dataSrc: '',
xhrFields: {
withCredentials: true
}
},
columns: [
{ data: 'id' }
]
});
});
From the article, you could find this is an object of fieldName-fieldValue pairs to set on the native XHR object. For example, you can use it to set withCredentials to true for cross-domain requests if needed.
Besides, I suggest you could modify the use CORS middleware order , to make sure the call to UseCors must be placed after UseRouting, but before UseAuthorization.
More details, you could refer to this article.