I have the following DxDatagrid
block in my dotnet core webapp index.cshtml
page:
@(Html.DevExtreme().DataGrid<UserModel>()
.ID("grid-container")
.ShowBorders(true)
.DataSource(d => d.Mvc().Controller("UserSearch").LoadAction("Get").Key("UserId"))
.Selection(s => s
.Mode(SelectionMode.Multiple)
.SelectAllMode(SelectAllMode.Page)
)
With this code in place and using dotnet core 2.2
the datasource makes a call to:
http://localhost:5000/api/UserSearch/Get?skip=0&take=10&requireTotalCount=true&_=1600859370033
Having updated to dotnet core 3.1 and updated the DevExpress references in the csproj
and _Layout.cshtml
files, the routing now attempts to call:
http://localhost:5000/?skip=0&take=10&requireTotalCount=true&_=1600859693687
The startup.cs
is this:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using AccessUsers.Middleware;
using AccessUsers.Models;
using Microsoft.AspNetCore.HttpOverrides;
namespace WebAppTest
{
public class Startup
{
private readonly IConfiguration _config;
private readonly AppSettings _appSettings;
public Startup(IConfiguration config)
{
_config = config;
_appSettings = _config.Get<AppSettings>();
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false;
// options.MinimumSameSitePolicy = SameSiteMode.None;
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
options.OnAppendCookie = cookieContext => cookieContext.CookieOptions.SameSite = SameSiteMode.Unspecified;
options.OnDeleteCookie = cookieContext => cookieContext.CookieOptions.SameSite = SameSiteMode.Unspecified;
});
services.Configure<AppSettings>(_config);
services.AddSingleton<APIService>();
services.AddSingleton<UserService>();
services.AddSingleton<ShipToService>();
services.AddApplicationInsightsTelemetry();
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddSession();
services.AddMemoryCache();
services.AddRazorPages().AddNewtonsoftJson(options => {
options.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver();
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
}).AddXmlSerializerFormatters();
services.UseOpenIDConnectMiddleware(new OpenIDConnectMiddlewareOptions
{
BaseUrl = _appSettings.API.BaseUrl,
AppName = _appSettings.AppName,
ClientId = _appSettings.API.ClientId,
ClientSecret = _appSettings.API.ClientSecret,
Secure = !_appSettings.Local
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedProto
});
if (_appSettings.Local)
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
app.UseGlobalLoginMiddleware();
app.UseHttpsRedirection();
}
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.UseEndpoints(endpoints => {
endpoints.MapRazorPages();
});
CultureInfo[] allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
string location = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);
var supportedCultures = allCultures.Where(c => Directory.Exists(Path.Combine(location, c.Name)) && c.LCID != 127).ToList();
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("en-US"),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
});
}
}
}
The csproj
contains this:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="3.1.6" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.7" />
<PackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="6.7.1" />
<PackageReference Include="DevExtreme.AspNet.Data" Version="2.7.1" />
<PackageReference Include="DevExtreme.AspNet.Core" Version="20.1.7" />
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.7.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.7.1" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.14.0" />
<PackageReference Include="Amazon.Lambda.AspNetCoreServer" Version="5.0.0" />
</ItemGroup>
</Project>
The controller.cs
contains this:
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using AccessUsers.Models;
using DevExtreme.AspNet.Data;
using DevExtreme.AspNet.Mvc;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
namespace WebAppTest.Pages
{
[Route("api/[controller]/[action]")]
public class UserSearchController : Controller
{
private readonly UserService _userService;
public UserSearchController(UserService userService)
{
_userService = userService;
}
[HttpGet]
public object Get(DataSourceLoadOptions loadOptions)
{
var result = DataSourceLoader.Load(GetProfiles(user:new UserModel(),useDummyData: true), loadOptions);
return result;
}
The _Layout.cshtml
contains this:
<script src="https://cdn3.devexpress.com/jslib/20.1.7/js/dx.all.js" integrity="sha384-LAn+t9UxSqkm8biNuoUbJcohKoYmbiFRfVLERIJ4I3RyEpAIBizEcIztuXPG9Cqg sha512-OAjfsw+eXv345AD9H6kDJLChXetpJD6ChGgDvjVIEumiHYulOLXIO/Do5gxljW2GUgpObic42JyS8a0wZqb1Fw==" crossorigin="anonymous"></script>
<script src="https://cdn3.devexpress.com/jslib/20.1.7/js/dx.aspnet.mvc.js" integrity="sha384-5rtF4jUX5Hez5YwkW7PHC/0XplJQS26qVUCfec8fBX0IkoR1y35EXHkZDbgeMh3x sha512-0eJebJTnN45FCtUOrVqxk5p73OMWsx94vLQpnlRtDp/CKbssiUR0j0os+0y01fvzDtdtnEKSeau32g30fgtrYQ==" crossorigin="anonymous"></script>
As specified here: https://js.devexpress.com/Documentation/Guide/Common/Distribution_Channels/CDN/
I'm sure it's the change to dotnet core 3.1
which caused the routing to break because the functionality of the application hasn't changed, but I can't see what specifically breaks it.
Startup.ConfigureServices
doesn't add support for controllers, only for Razor Pages with :From the Remarks in the method's documentation
The controller is never registered right now, so the code that tries to generate the action URL
fails to find anything and returns an empty string.
To fix this, add controller support :
Controllers should be added in the endpoint routing code in
Configure
as well, with MapControllers :