From cc3f07c6ad0ced89f33751bda6e89268a62275e6 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 28 Dec 2025 08:08:01 +0000 Subject: [PATCH 1/2] fix: Change subfolder parameter from [FromForm] to [FromQuery] in upload endpoints The upload endpoints were returning HTTP 400 (ValidationProblemDetails) because ASP.NET Core's model binding was failing when trying to bind the subfolder parameter from form data that wasn't being sent by the client. Root cause: - The [FromForm] attribute expects the parameter to be in the multipart form data - The client uploads files but doesn't include the subfolder parameter - Even though the parameter is nullable with a default value, the binding fails Solution: - Changed [FromForm] to [FromQuery] for the subfolder parameter in both: - UploadClientFiles endpoint (api/v1/editor/updates/client) - UploadEditorFiles endpoint (api/v1/editor/updates/editor) - This allows the parameter to be read from the query string - Works correctly when the parameter is omitted (defaults to null) This is the proper approach for optional string parameters in file upload endpoints, as mixing form fields with multipart file uploads can cause validation issues in ASP.NET Core. Fixes the HTTP 400 error that was blocking all upload attempts. --- .../Web/Controllers/Api/EditorUpdatesController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Intersect.Server/Web/Controllers/Api/EditorUpdatesController.cs b/Intersect.Server/Web/Controllers/Api/EditorUpdatesController.cs index 56721fdb39..a54508557d 100644 --- a/Intersect.Server/Web/Controllers/Api/EditorUpdatesController.cs +++ b/Intersect.Server/Web/Controllers/Api/EditorUpdatesController.cs @@ -59,7 +59,7 @@ IOptionsMonitor updateServerOptionsMonitor [ProducesResponseType(typeof(ErrorResponse), (int)HttpStatusCode.BadRequest, ContentTypes.Json)] [ProducesResponseType(typeof(ErrorResponse), (int)HttpStatusCode.Forbidden, ContentTypes.Json)] public async Task UploadClientFiles( - [FromForm] string? subfolder = null + [FromQuery] string? subfolder = null ) { return await UploadFilesInternal("client", Request.Form.Files, subfolder); @@ -78,7 +78,7 @@ public async Task UploadClientFiles( [ProducesResponseType(typeof(ErrorResponse), (int)HttpStatusCode.BadRequest, ContentTypes.Json)] [ProducesResponseType(typeof(ErrorResponse), (int)HttpStatusCode.Forbidden, ContentTypes.Json)] public async Task UploadEditorFiles( - [FromForm] string? subfolder = null + [FromQuery] string? subfolder = null ) { return await UploadFilesInternal("editor", Request.Form.Files, subfolder); From 89b9b242ac28720ded8531154a30e84f99797b64 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 28 Dec 2025 08:11:45 +0000 Subject: [PATCH 2/2] fix: Add missing attributes for multipart file upload handling Added critical attributes to both upload endpoints to properly handle large multipart file uploads from the editor: 1. [Consumes("multipart/form-data")] - Explicitly declares the endpoint accepts multipart form data - Helps ASP.NET Core routing and model binding understand the content type - Improves API documentation in Swagger 2. [DisableRequestSizeLimit] - Removes the default 30MB request size limit - Allows uploading large game asset packages without size restrictions - Essential for editor uploads which can contain hundreds of MB of assets 3. [RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)] - Removes limits on multipart body length and individual value length - Prevents HTTP 400 errors from default ASP.NET Core form size limits - Allows batched uploads of many files at once Also added: - using Microsoft.AspNetCore.Http.Features; for RequestFormLimits attribute Without these attributes, ASP.NET Core's default FormOptions would reject requests that exceed the built-in limits, resulting in HTTP 400 Bad Request errors before the controller method is even reached. These changes complement the previous fix that changed [FromForm] to [FromQuery] for the subfolder parameter. --- .../Web/Controllers/Api/EditorUpdatesController.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Intersect.Server/Web/Controllers/Api/EditorUpdatesController.cs b/Intersect.Server/Web/Controllers/Api/EditorUpdatesController.cs index a54508557d..8aec6079e8 100644 --- a/Intersect.Server/Web/Controllers/Api/EditorUpdatesController.cs +++ b/Intersect.Server/Web/Controllers/Api/EditorUpdatesController.cs @@ -7,6 +7,7 @@ using Intersect.Server.Web.Http; using Intersect.Server.Web.Types; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; @@ -54,6 +55,9 @@ IOptionsMonitor updateServerOptionsMonitor /// Optional subfolder within assets/client (e.g., "resources", "resources/images") /// Upload results for each file [HttpPost("client")] + [Consumes("multipart/form-data")] + [DisableRequestSizeLimit] + [RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)] [ProducesResponseType(typeof(UploadResponse), (int)HttpStatusCode.OK, ContentTypes.Json)] [ProducesResponseType(typeof(UploadResponse), (int)HttpStatusCode.MultiStatus, ContentTypes.Json)] [ProducesResponseType(typeof(ErrorResponse), (int)HttpStatusCode.BadRequest, ContentTypes.Json)] @@ -73,6 +77,9 @@ public async Task UploadClientFiles( /// Optional subfolder within assets/editor /// Upload results for each file [HttpPost("editor")] + [Consumes("multipart/form-data")] + [DisableRequestSizeLimit] + [RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue, ValueLengthLimit = int.MaxValue)] [ProducesResponseType(typeof(UploadResponse), (int)HttpStatusCode.OK, ContentTypes.Json)] [ProducesResponseType(typeof(UploadResponse), (int)HttpStatusCode.MultiStatus, ContentTypes.Json)] [ProducesResponseType(typeof(ErrorResponse), (int)HttpStatusCode.BadRequest, ContentTypes.Json)]