Skip to content

Commit

Permalink
Add ModelBinder support (#14)
Browse files Browse the repository at this point in the history
* feat: Add ModelBinder support

* feat:Simplify PatchObjectModelBinder
  • Loading branch information
ayrloong authored Oct 11, 2024
1 parent ddcfe53 commit f241ca4
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ public IEnumerable<string> Patch([FromBody] Patch<Person> person)
};
}
}
}
}
7 changes: 6 additions & 1 deletion samples/Simple.HttpPatch.Samples/Program.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@

using Simple.HttpPatch;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
builder.Services.AddControllers(options =>
{
options.ModelBinderProviders.Insert(0, new PatchModelBinderProvider());
});

var app = builder.Build();

Expand Down
16 changes: 16 additions & 0 deletions src/Simple.HttpPatch/DynamicMemberBinder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System.Dynamic;

namespace Simple.HttpPatch;

public class DynamicMemberBinder : SetMemberBinder
{
public DynamicMemberBinder(string name, bool ignoreCase) : base(name, ignoreCase)
{
}

public override DynamicMetaObject FallbackSetMember(DynamicMetaObject target, DynamicMetaObject value,
DynamicMetaObject errorSuggestion)
{
throw new System.NotImplementedException();
}
}
14 changes: 8 additions & 6 deletions src/Simple.HttpPatch/Patch.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Dynamic;
Expand All @@ -12,11 +13,12 @@ public sealed class Patch<TModel> : DynamicObject where TModel : class

public override bool TrySetMember(SetMemberBinder binder, object value)
{
PropertyInfo propertyInfo = typeof(TModel).GetProperty(binder.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
PropertyInfo propertyInfo = typeof(TModel).GetProperty(binder.Name,
BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (propertyInfo is not null)
{
var isIgnoredPropery = propertyInfo.GetCustomAttribute<PatchIgnoreAttribute>() is not null;
if (!isIgnoredPropery)
var isIgnoredProperty = propertyInfo.GetCustomAttribute<PatchIgnoreAttribute>() is not null;
if (!isIgnoredProperty)
{
var isIgnoredIfNull = propertyInfo.GetCustomAttribute<PatchIgnoreNullAttribute>() is not null;
if (isIgnoredIfNull)
Expand Down Expand Up @@ -82,8 +84,8 @@ private static object ChangeType(object value, Type type)

private static bool IsExcludedProperty(string propertyName)
{
IEnumerable<string> defaultExcludedProperies = new[] { "ID" };
return defaultExcludedProperies.Contains(propertyName.ToUpper());
IEnumerable<string> defaultExcludedProperties = new[] { "ID" };
return defaultExcludedProperties.Contains(propertyName.ToUpper());
}
}
}
}
2 changes: 1 addition & 1 deletion src/Simple.HttpPatch/PatchIgnoreAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ namespace Simple.HttpPatch
public class PatchIgnoreAttribute : Attribute
{
}
}
}
20 changes: 20 additions & 0 deletions src/Simple.HttpPatch/PatchModelBinderProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System;

namespace Simple.HttpPatch;

public class PatchModelBinderProvider:IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context.Metadata is { IsComplexType: true, ModelType.IsGenericType: true } &&
context.Metadata.ModelType.GetGenericTypeDefinition() == typeof(Patch<>))
{
var modelType = context.Metadata.ModelType.GenericTypeArguments[0];
var binderType = typeof(PatchObjectModelBinder<>).MakeGenericType(modelType);
return (IModelBinder)Activator.CreateInstance(binderType);
}

return null;
}
}
38 changes: 38 additions & 0 deletions src/Simple.HttpPatch/PatchObjectModelBinder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;

namespace Simple.HttpPatch;

public class PatchObjectModelBinder<T> : IModelBinder where T : class
{
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}

using (var reader = new StreamReader(bindingContext.HttpContext.Request.Body))
{
var json = await reader.ReadToEndAsync();

dynamic dynamicObject = Activator.CreateInstance(typeof(Patch<T>));

using (JsonDocument doc = JsonDocument.Parse(json))
{
foreach (var property in doc.RootElement.EnumerateObject())
{
dynamicObject.TrySetMember(new DynamicMemberBinder(property.Name, true), property.Value.ToString());
}
}

bindingContext.Result = ModelBindingResult.Success(dynamicObject);
}
}
}
7 changes: 5 additions & 2 deletions src/Simple.HttpPatch/Simple.HttpPatch.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net46;netstandard2.0;netcoreapp2.0;net6.0</TargetFrameworks>
<TargetFrameworks>net6.0</TargetFrameworks>
<Authors>Roman Marusyk</Authors>
<Company />
<PackageProjectUrl></PackageProjectUrl>
Expand All @@ -14,5 +14,8 @@
<PackageIconUrl>https://github.com/Marusyk/Simple.HttpPatch/blob/master/HttpPatch.png?raw=true</PackageIconUrl>
<LangVersion>10</LangVersion>
</PropertyGroup>


<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
</Project>
2 changes: 1 addition & 1 deletion tests/Simple.HttpPatch.Tests/Simple.HttpPatch.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net46;netcoreapp2.0;net6.0</TargetFrameworks>
<TargetFrameworks>net6.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
Expand Down

0 comments on commit f241ca4

Please sign in to comment.