diff --git a/src/Altinn.App.Api/Controllers/InstancesController.cs b/src/Altinn.App.Api/Controllers/InstancesController.cs index 6adb17fd2..168c74a5b 100644 --- a/src/Altinn.App.Api/Controllers/InstancesController.cs +++ b/src/Altinn.App.Api/Controllers/InstancesController.cs @@ -1228,14 +1228,8 @@ string action { RequestPart? instancePart = parts.Find(part => part.Name == "instance"); - // assume that first part with no name is an instanceTemplate - if ( - instancePart == null - && parts.Count == 1 - && parts[0].ContentType.Contains("application/json") - && parts[0].Name == null - && parts[0].Bytes.Length > 0 - ) + // If the request has a single part with no name, assume it is the instance template + if (instancePart == null && parts.Count == 1 && parts[0].Name == null) { instancePart = parts[0]; } @@ -1244,7 +1238,17 @@ string action { parts.Remove(instancePart); - return System.Text.Json.JsonSerializer.Deserialize(instancePart.Bytes, _jsonSerializerOptionsWeb); + // Some clients might set contentType to application/json even if the body is empty + if ( + instancePart is { Bytes.Length: > 0 } + && instancePart.ContentType.Contains("application/json", StringComparison.Ordinal) + ) + { + return System.Text.Json.JsonSerializer.Deserialize( + instancePart.Bytes, + _jsonSerializerOptionsWeb + ); + } } return null; diff --git a/test/Altinn.App.Api.Tests/Controllers/InstancesController_PostNewInstance.cs b/test/Altinn.App.Api.Tests/Controllers/InstancesController_PostNewInstance.cs index 3ce51ca68..9d8f91fb2 100644 --- a/test/Altinn.App.Api.Tests/Controllers/InstancesController_PostNewInstance.cs +++ b/test/Altinn.App.Api.Tests/Controllers/InstancesController_PostNewInstance.cs @@ -1,5 +1,6 @@ using System.Net; using System.Net.Http.Headers; +using System.Net.Http.Json; using System.Text; using System.Text.Json; using System.Text.Json.Nodes; @@ -273,6 +274,56 @@ public async Task InstationAllowedByOrg_Returns_Forbidden_For_user() createResponse.StatusCode.Should().Be(HttpStatusCode.Forbidden, createResponseContent); } + [Fact] + public async Task PostNewInstanceWithInstanceTemplate() + { + string org = "tdd"; + string app = "contributer-restriction"; + int instanceOwnerPartyId = 501337; + int userId = 1337; + HttpClient client = GetRootedClient(org, app, userId, null); + + using var content = JsonContent.Create( + new Instance() { InstanceOwner = new InstanceOwner() { PartyId = instanceOwnerPartyId.ToString() }, } + ); + + var response = await client.PostAsync($"{org}/{app}/instances", content); + response.Should().HaveStatusCode(HttpStatusCode.Created); + var responseContent = await response.Content.ReadAsStringAsync(); + var instance = JsonSerializer.Deserialize(responseContent, JsonSerializerOptions); + instance.Should().NotBeNull(); + instance!.Id.Should().NotBeNullOrEmpty(); + + TestData.DeleteInstanceAndData(org, app, instance.Id); + } + + [Fact] + public async Task PostNewInstanceWithMissingTemplate() + { + string org = "tdd"; + string app = "contributer-restriction"; + int instanceOwnerPartyId = 501337; + int userId = 1337; + HttpClient client = GetRootedClient(org, app, userId, null); + + using var content = new ByteArrayContent([]) + { + Headers = { ContentType = new MediaTypeHeaderValue("application/json") } + }; + + var response = await client.PostAsync( + $"{org}/{app}/instances?instanceOwnerPartyId={instanceOwnerPartyId}", + content + ); + response.Should().HaveStatusCode(HttpStatusCode.Created); + var responseContent = await response.Content.ReadAsStringAsync(); + var instance = JsonSerializer.Deserialize(responseContent, JsonSerializerOptions); + instance.Should().NotBeNull(); + instance!.Id.Should().NotBeNullOrEmpty(); + + TestData.DeleteInstanceAndData(org, app, instance.Id); + } + [Fact] public async Task InstationAllowedByOrg_Returns_Forbidden_For_User_SimplifiedEndpoint() {