Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Examples/JsonApiDotNetCoreExample/Models/Article.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using JsonApiDotNetCore.Models;
using JsonApiDotNetCore.Models.CustomValidators;

namespace JsonApiDotNetCoreExample.Models
{
public sealed class Article : Identifiable
{
[Attr]
[Required(AllowEmptyStrings = true)]
public string Name { get; set; }

[HasOne]
Expand Down
2 changes: 2 additions & 0 deletions src/Examples/JsonApiDotNetCoreExample/Models/Author.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using JsonApiDotNetCore.Models;
using System.Collections.Generic;
using JsonApiDotNetCore.Models.CustomValidators;

namespace JsonApiDotNetCoreExample.Models
{
public sealed class Author : Identifiable
{
[Attr]
[Required(AllowEmptyStrings = true)]
public string Name { get; set; }

[HasMany]
Expand Down
3 changes: 2 additions & 1 deletion src/Examples/ReportsExample/ReportsExample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="$(NpgsqlPostgreSQLVersion)" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.5" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.4" />
</ItemGroup>
</Project>
1 change: 1 addition & 0 deletions src/JsonApiDotNetCore/Formatters/JsonApiReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public async Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
object model;
try
{
_deserializer.Context = context;
model = _deserializer.Deserialize(body);
}
catch (InvalidRequestBodyException exception)
Expand Down
2 changes: 1 addition & 1 deletion src/JsonApiDotNetCore/JsonApiDotNetCore.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<VersionPrefix>4.0.0</VersionPrefix>
<TargetFramework>$(NetCoreAppVersion)</TargetFramework>
Expand Down
29 changes: 29 additions & 0 deletions src/JsonApiDotNetCore/Models/CustomValidators/RequiredIfEnabled.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System.ComponentModel.DataAnnotations;

namespace JsonApiDotNetCore.Models.CustomValidators
{
public class Required : RequiredAttribute
{
private bool Disabled { get; set; }

public override bool IsValid(object value)
{
return Disabled || base.IsValid(value);
}

protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
CheckDisableKey(validationContext);
return Disabled ? ValidationResult.Success : base.IsValid(value, validationContext);
}

private void CheckDisableKey(ValidationContext validationContext)
{
var httpContextAccessor = (IHttpContextAccessor)validationContext.GetRequiredService(typeof(IHttpContextAccessor));
Disabled = httpContextAccessor.HttpContext.Items.ContainsKey($"DisableValidation_{validationContext.ObjectType.Name}_{validationContext.MemberName}")
|| httpContextAccessor.HttpContext.Items.ContainsKey($"DisableValidation_{validationContext.ObjectType.Name}_Relation");
}
}
}
52 changes: 42 additions & 10 deletions src/JsonApiDotNetCore/Serialization/Common/BaseDocumentParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,19 @@
using JsonApiDotNetCore.Serialization.Server;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Microsoft.AspNetCore.Mvc.Formatters;
using Required = JsonApiDotNetCore.Models.CustomValidators.Required;

namespace JsonApiDotNetCore.Serialization
{
/// <summary>
/// Abstract base class for deserialization. Deserializes JSON content into <see cref="Document"/>s
/// And constructs instances of the resource(s) in the document body.
/// </summary>
public abstract class BaseDocumentParser
public abstract class BaseDocumentParser : IJsonApiDeserializer
{
public InputFormatterContext Context { get; set; }

protected readonly IResourceContextProvider _contextProvider;
protected readonly IResourceFactory _resourceFactory;
protected Document _document;
Expand All @@ -46,7 +50,7 @@ protected BaseDocumentParser(IResourceContextProvider contextProvider, IResource
protected abstract void AfterProcessField(IIdentifiable entity, IResourceField field, RelationshipEntry data = null);

/// <inheritdoc/>
protected object Deserialize(string body)
public object Deserialize(string body)
{
var bodyJToken = LoadJToken(body);
_document = bodyJToken.ToObject<Document>();
Expand All @@ -71,21 +75,38 @@ protected object Deserialize(string body)
/// <returns></returns>
protected IIdentifiable SetAttributes(IIdentifiable entity, Dictionary<string, object> attributeValues, List<AttrAttribute> attributes)
{
if (attributeValues == null || attributeValues.Count == 0)
return entity;

foreach (var attr in attributes)
{
if (attributeValues.TryGetValue(attr.PublicAttributeName, out object newValue))
if (attributeValues == null || attributeValues.Count == 0)
{
var convertedValue = ConvertAttrValue(newValue, attr.PropertyInfo.PropertyType);
attr.SetValue(entity, convertedValue);
AfterProcessField(entity, attr);
if (Context == null || Context.HttpContext.Request.Method != "PATCH") continue;
if (attr.PropertyInfo.GetCustomAttribute<Required>() != null)
{
DisableValidator(attr.PropertyInfo.ReflectedType?.Name, attr.PropertyInfo.Name);
}
}
else
{
if (attributeValues.TryGetValue(attr.PublicAttributeName, out object newValue))
{
var convertedValue = ConvertAttrValue(newValue, attr.PropertyInfo.PropertyType);
attr.SetValue(entity, convertedValue);
AfterProcessField(entity, attr);
}
else
{
if (Context == null || Context.HttpContext.Request.Method != "PATCH") continue;
if (attr.PropertyInfo.GetCustomAttribute<Required>() != null)
{
DisableValidator(attr.PropertyInfo.ReflectedType?.Name, attr.PropertyInfo.Name);
}
}
}
}

return entity;
}

/// <summary>
/// Sets the relationships on a parsed entity
/// </summary>
Expand All @@ -101,14 +122,15 @@ protected IIdentifiable SetRelationships(IIdentifiable entity, Dictionary<string
var entityProperties = entity.GetType().GetProperties();
foreach (var attr in relationshipAttributes)
{
DisableValidator(attr.PropertyInfo.Name, "Relation");

if (!relationshipsValues.TryGetValue(attr.PublicRelationshipName, out RelationshipEntry relationshipData) || !relationshipData.IsPopulated)
continue;

if (attr is HasOneAttribute hasOneAttribute)
SetHasOneRelationship(entity, entityProperties, hasOneAttribute, relationshipData);
else
SetHasManyRelationship(entity, (HasManyAttribute)attr, relationshipData);

}
return entity;
}
Expand Down Expand Up @@ -260,5 +282,15 @@ private object DeserializeComplexType(JContainer obj, Type targetType)
{
return obj.ToObject(targetType);
}

private void DisableValidator(string model, string name)
{
if (Context == null) return;
var itemKey = $"DisableValidation_{model}_{name}";
if (!Context.HttpContext.Items.ContainsKey(itemKey))
{
Context.HttpContext.Items.Add(itemKey, true);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using JsonApiDotNetCore.Models;
using Microsoft.AspNetCore.Mvc.Formatters;

namespace JsonApiDotNetCore.Serialization.Server
{
Expand All @@ -7,6 +8,7 @@ namespace JsonApiDotNetCore.Serialization.Server
/// </summary>
public interface IJsonApiDeserializer
{
public InputFormatterContext Context { get; set; }
/// <summary>
/// Deserializes JSON in to a <see cref="Document"/> and constructs entities
/// from <see cref="ExposableData{T}.Data"/>.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using JsonApiDotNetCore.Exceptions;
using JsonApiDotNetCore.Internal;
using JsonApiDotNetCore.Internal.Contracts;
Expand All @@ -9,7 +8,7 @@ namespace JsonApiDotNetCore.Serialization.Server
/// <summary>
/// Server deserializer implementation of the <see cref="BaseDocumentParser"/>
/// </summary>
public class RequestDeserializer : BaseDocumentParser, IJsonApiDeserializer
public class RequestDeserializer : BaseDocumentParser
{
private readonly ITargetedFields _targetedFields;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,10 @@ public async Task Can_Create_Many_To_Many()
data = new
{
type = "articles",
attributes = new Dictionary<string, object>
{
{"name", "An article with relationships"}
},
relationships = new Dictionary<string, dynamic>
{
{ "author", new {
Expand Down
Loading