diff --git a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs index f4041e97d6..fd6ec8947a 100644 --- a/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs +++ b/src/JsonApiDotNetCore/Controllers/BaseJsonApiController.cs @@ -146,13 +146,15 @@ public virtual async Task GetRelationshipAsync(TId id, string rel public virtual async Task PostAsync([FromBody] T entity) { - if (_create == null) throw Exceptions.UnSupportedRequestMethod; + if (_create == null) + throw Exceptions.UnSupportedRequestMethod; if (entity == null) return UnprocessableEntity(); if (!_jsonApiContext.Options.AllowClientGeneratedIds && !string.IsNullOrEmpty(entity.StringId)) return Forbidden(); + if (_jsonApiContext.Options.ValidateModelState && !ModelState.IsValid) return BadRequest(ModelState.ConvertToErrorCollection()); diff --git a/src/JsonApiDotNetCore/Extensions/ModelStateExtensions.cs b/src/JsonApiDotNetCore/Extensions/ModelStateExtensions.cs index 8eb0fc95f7..d67f7e66c4 100644 --- a/src/JsonApiDotNetCore/Extensions/ModelStateExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/ModelStateExtensions.cs @@ -8,17 +8,22 @@ public static class ModelStateExtensions { public static ErrorCollection ConvertToErrorCollection(this ModelStateDictionary modelState) { - ErrorCollection errors = new ErrorCollection(); + ErrorCollection collection = new ErrorCollection(); foreach (var entry in modelState) { - if (!entry.Value.Errors.Any()) + if (entry.Value.Errors.Any() == false) continue; + foreach (var modelError in entry.Value.Errors) { - errors.Errors.Add(new Error(400, entry.Key, modelError.ErrorMessage, modelError.Exception != null ? ErrorMeta.FromException(modelError.Exception) : null)); + if (modelError.Exception is JsonApiException jex) + collection.Errors.AddRange(jex.GetError().Errors); + else + collection.Errors.Add(new Error(400, entry.Key, modelError.ErrorMessage, modelError.Exception != null ? ErrorMeta.FromException(modelError.Exception) : null)); } } - return errors; + + return collection; } } } diff --git a/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs b/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs index 204f3e0491..e10a3f31c2 100644 --- a/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs +++ b/src/JsonApiDotNetCore/Formatters/JsonApiReader.cs @@ -52,11 +52,9 @@ public Task ReadAsync(InputFormatterContext context) context.ModelState.AddModelError(context.ModelName, ex, context.Metadata); return InputFormatterResult.FailureAsync(); } - catch (JsonApiException jex) + catch (JsonApiException) { - _logger?.LogError(new EventId(), jex, "An error occurred while de-serializing the payload"); - context.ModelState.AddModelError(context.ModelName, jex, context.Metadata); - return InputFormatterResult.FailureAsync(); + throw; } } diff --git a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj index cc337c28dc..a212eecd70 100755 --- a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj +++ b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj @@ -1,6 +1,6 @@ - + - 2.3.3 + 2.3.4 $(NetStandardVersion) JsonApiDotNetCore JsonApiDotNetCore diff --git a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs index a1e3c76214..285923995d 100644 --- a/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs +++ b/src/JsonApiDotNetCore/Serialization/JsonApiDeSerializer.cs @@ -57,6 +57,10 @@ public object Deserialize(string requestBody) var entity = DocumentToObject(document.Data, document.Included); return entity; } + catch (JsonApiException) + { + throw; + } catch (Exception e) { throw new JsonApiException(400, "Failed to deserialize request body", e); @@ -109,10 +113,15 @@ public List DeserializeList(string requestBody) public object DocumentToObject(DocumentData data, List included = null) { - if (data == null) throw new JsonApiException(422, "Failed to deserialize document as json:api."); + if (data == null) + throw new JsonApiException(422, "Failed to deserialize document as json:api."); var contextEntity = _jsonApiContext.ContextGraph.GetContextEntity(data.Type?.ToString()); - _jsonApiContext.RequestEntity = contextEntity; + _jsonApiContext.RequestEntity = contextEntity ?? throw new JsonApiException(400, + message: $"This API does not contain a json:api resource named '{data.Type}'.", + detail: "This resource is not registered on the ContextGraph. " + + "If you are using Entity Framework, make sure the DbSet matches the expected resource name. " + + "If you have manually registered the resource, check that the call to AddResource correctly sets the public name."); ; var entity = Activator.CreateInstance(contextEntity.EntityType); diff --git a/test/UnitTests/Models/IdentifiableTests.cs b/test/UnitTests/Models/IdentifiableTests.cs new file mode 100644 index 0000000000..778b1b485f --- /dev/null +++ b/test/UnitTests/Models/IdentifiableTests.cs @@ -0,0 +1,26 @@ +using JsonApiDotNetCore.Models; +using Xunit; + +namespace UnitTests.Models +{ + public class IdentifiableTests + { + [Fact] + public void Can_Set_StringId_To_Value_Type() + { + var resource = new IntId(); + resource.StringId = "1"; + Assert.Equal(1, resource.Id); + } + + [Fact] + public void Setting_StringId_To_Null_Sets_Id_As_Default() + { + var resource = new IntId(); + resource.StringId = null; + Assert.Equal(0, resource.Id); + } + + private class IntId : Identifiable { } + } +}