valdisiljuconoks / localization-provider-opti

Database driven localization provider for Optimizely (ex. Episerver) websites
Apache License 2.0
11 stars 16 forks source link

AdminUI: Error when updating / editing a resource #152

Closed jsalwen closed 9 months ago

jsalwen commented 2 years ago

Version: 7.1.0 Optimizely: 12.2.1

And the following error appears in the log file:

2022-02-23 09:39:21.832 -08:00 [ERR] An unhandled exception has occurred while executing the request.
System.InvalidOperationException: Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead.
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpRequestStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   at Utf8Json.JsonSerializer.FillFromStream(Stream input, Byte[]& buffer)
   at Utf8Json.JsonSerializer.Deserialize[T](Stream stream, IJsonFormatterResolver resolver)
   at Utf8Json.JsonSerializer.NonGeneric.Deserialize(Type type, Stream stream, IJsonFormatterResolver resolver)
   at Utf8Json.AspNetCoreMvcFormatter.JsonInputFormatter.ReadAsync(InputFormatterContext context)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder.BindModelAsync(ModelBindingContext bindingContext)
   at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value, Object container)
   at Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.Policy.AuthorizationMiddlewareResultHandler.HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at SixLabors.ImageSharp.Web.Middleware.ImageSharpMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
valdisiljuconoks commented 2 years ago

Hi,

Just tested with exact version numbers and also higher - unfortunately, I can't replicate the issue. From the callstack does not look like related to provider.. Anything odd in your project setup?

thicker2k13 commented 2 years ago

I added:

services.Configure<KestrelServerOptions>(options =>
{
  options.AllowSynchronousIO = true;
});

...to get around this, am now getting:

2022-03-01 11:51:04.271 -05:00 [ERR] An unhandled exception has occurred while executing the request.
System.ArgumentNullException: Value cannot be null. (Parameter 'name')
   at System.Globalization.CultureInfo..ctor(String name, Boolean useUserOverride)
   at DbLocalizationProvider.AdminUI.AspNetCore.ServiceController.Save(CreateOrUpdateTranslationRequestModel model)
   at lambda_method657(Closure , Object , Object[] )
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Authorization.Policy.AuthorizationMiddlewareResultHandler.HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
valdisiljuconoks commented 2 years ago

Hi,

Can you please capture XHR request going out from browser to the API endpoint? Interested in payload.

thicker2k13 commented 2 years ago

{"titleKey":"Indigo.Cms.Infrastructure.Resources.IndigoResources.AnotherLabel","key":"Indigo.Cms.Infrastructure.Resources.IndigoResources.AnotherLabel","translation":"asdfasdfsadf","language":"sv","isModified":false}

thicker2k13 commented 2 years ago

One other semi-related comment: we had issues with performance after installing 7.1 as I believe is referenced in #153, rolling back to 7.0 solved it. Same issue with the save in 7.0 though.

valdisiljuconoks commented 2 years ago

Yeah, performance issue is fixed

valdisiljuconoks commented 2 years ago

Also, out of curiosity, is it possible for you to dump Startup.cs file (ofc omitting all sensitive info)?

thicker2k13 commented 2 years ago

Here it is, only change is that client name is redacted

using System;

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;

using Utf8Json.AspNetCoreMvcFormatter;
using Utf8Json.Resolvers;

using EPiServer.Cms.TinyMce;
using EPiServer.Cms.UI.AspNetIdentity;
using EPiServer.Data;
using EPiServer.Framework.Web.Resources;
using EPiServer.Web;
using EPiServer.Web.Routing;

using Baaijte.Optimizely.ImageSharp.Web;

using Common.Foundation.Optimizely.Extensions;
using Common.Foundation.Optimizely.Infrastructure.Display;
using Common.Foundation.WebExtensions.Services;
using DbLocalizationProvider.AdminUI.AspNetCore;
using DbLocalizationProvider.AdminUI.AspNetCore.Routing;
using DbLocalizationProvider.AdminUI.EPiServer;
using DbLocalizationProvider.AspNetCore;
using DbLocalizationProvider.EPiServer;
using DbLocalizationProvider.Storage.SqlServer;
using (redacted).Cms.Infrastructure;
using (redacted).Cms.Infrastructure.Display;
using (redacted).Cms.Features.Locations;
using (redacted).Cms.Features.Locations.PubSub;
using (redacted).Cms.Infrastructure.Services;
using Microsoft.AspNetCore.Server.Kestrel.Core;

namespace (redacted)
{
    public class Startup
    {
        #region Private Variables

        private readonly IConfiguration _configuration;
        private readonly IWebHostEnvironment _webHostingEnvironment;

        #endregion Private Variables

        #region Constructors

        public Startup(IConfiguration configuration,
            IWebHostEnvironment webHostingEnvironment)
        {
            _configuration = configuration;
            _webHostingEnvironment = webHostingEnvironment;
        }

        #endregion Constructors

        #region Public Methods

        #region Configure
        /// <summary>
        /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        /// </summary>
        /// <param name="app"></param>
        /// <param name="env"></param>
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseBaaijteOptimizelyImageSharp();
            app.UseStaticFiles();
            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseDbLocalizationProvider();

            app.UseDbLocalizationProvider();
            app.UseDbLocalizationProviderAdminUI();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapContent();
                endpoints.MapControllers();
                endpoints.MapRazorPages();
                // SignalR endpoints
                endpoints.MapHub<BookingHub>("/hub/booking");
                endpoints.MapDbLocalizationAdminUI();
            });
        }
        #endregion

        #region ConfigureServices
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(o => o.Conventions.Add(new FeatureConvention()))
                .AddRazorOptions(ro => ro.ViewLocationExpanders.Add(new (redacted)FeatureViewLocationExpander()));

            if (_webHostingEnvironment.IsDevelopment())
            {
                services.Configure<ClientResourceOptions>(uiOptions => uiOptions.Debug = true);
                services.Configure<DataAccessOptions>(dataOptions => dataOptions.UpdateDatabaseSchema = true);
                // for developers, recompile views when cshtml file is updated (without rebuild)
                // only works for cshtml files in base project, not in views in referenced libraries
                services.AddControllersWithViews().AddRazorRuntimeCompilation();
            }

            services.Configure<KestrelServerOptions>(options =>
            {
                options.AllowSynchronousIO = true;
            });

            services
                .AddDbLocalizationProvider(ctx =>
                {
                    ctx.UseSqlServer(_configuration.GetConnectionString("EPiServerDB"));
                })
                .AddOptimizely();  // add Optimizely integration

            services
                .AddDbLocalizationProviderAdminUI(ctx =>
                {
                    // now you can set different options via configuration context (`ctx`)
                })
                .AddOptimizelyAdminUI();  // add Optimizely integration (adds menu items and stuff)

            services.AddCms()
                .AddCmsAspNetIdentity<ApplicationUser>();
            // add support for cshtml in Common.Foundation.Optimizely
            services.AddRazorPages();
            services.AddTinyMce();
            services.ConfigureTinyMce();
            // add support for Common.Foundation.Optimizely
            services.ConfigureFoundation();
            services.ConfigureApplicationCookie(options =>
            {
                options.LoginPath = "/util/Login";
                options.ExpireTimeSpan = new TimeSpan(0, 20, 0);
                options.SlidingExpiration = true;
            });
            services.TryAddEnumerable(ServiceDescriptor.Singleton(typeof(IFirstRequestInitializer), typeof(ContentInstaller)));
            services.AddDetection();
            services.RemoveSuggestedPageTypes();

            services.AddSingleton<ILocationService, LocationService>();
            services.AddHostedService<QueuedHostedService>();
            services.AddSingleton<IBackgroundTaskQueue>(ctx => {
                if (!int.TryParse(_configuration["BackgroundQueueCapacity"], out var queueCapacity))
                    queueCapacity = 100;
                return new BackgroundTaskQueue(queueCapacity);
            });
            // run the LocationsStartup to fill cache
            services.TryAddEnumerable(ServiceDescriptor.Singleton(typeof(IFirstRequestInitializer), typeof(LocationsStartup)));
            services.AddBaaijteOptimizelyImageSharp();

            services.AddControllersWithViews()
                // Add Utf8Json formatters
                .AddMvcOptions(option =>
                {
                    option.OutputFormatters.Clear();
                    option.OutputFormatters.Add(new JsonOutputFormatter(StandardResolver.CamelCase));
                    option.InputFormatters.Clear();
                    option.InputFormatters.Add(new JsonInputFormatter());
                });

            // add support for rendering view as string
            services.AddScoped<IViewRenderService, ViewRenderService>();

            // add SignalR for pub/sub with browser clients
            services.AddSignalR();

            var hostEnvironment = GetHostEnvironment();
            if (hostEnvironment != HostEnvironment.Development)
            {
                // add DXP support for Integration, Preproduction and Production
                services.AddCmsCloudPlatformSupport(_configuration);
            }
        }

        #endregion

        #region GetHostEnvironment
        private static HostEnvironment GetHostEnvironment()
        {
            var environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");

            return environmentName switch
            {
                "Development" => HostEnvironment.Development,
                "Preproduction" => HostEnvironment.Preproduction,
                "Production" => HostEnvironment.Production,
                _ => HostEnvironment.Integration,
            };
        }
        #endregion

        #endregion Public Methods

        #region Private Enums

        #region Envrionment
        private enum HostEnvironment
        {
            Development,
            Integration,
            Preproduction,
            Production
        }
        #endregion

        #endregion Private Enums
    }
}
valdisiljuconoks commented 2 years ago

Hey,

Sorry for delay (was knocked a bit off the planet). Did you get around this issue meanwhile?

Btw, you have double app.UseDbLocalizationProvider(); line in your startup (although it's not probably a root cause for the issue).

thicker2k13 commented 2 years ago

No we had to back it out for the time being. Would love any other thoughts though.

thicker2k13 commented 2 years ago

Does your payload match ours? I'm wondering why the CultureInfo constructor is seeing "name" as null.

valdisiljuconoks commented 2 years ago

No, I'm not able to repo this. If you have time - is this an issue also in simple alloy site?

thicker2k13 commented 2 years ago

I've narrowed this down to the payload not being parsed in the Save method. Everything else works fine. Could be a Newtonsoft issue or something. Will continue to look at it and let you know if/when I sort it.

valdisiljuconoks commented 9 months ago

closed due to not able to repro. please reopen if this is still an issue...