Giter Club home page Giter Club logo

hybrid-model-binding's People

Contributors

andreakn avatar billbogaiv avatar jrusbatch avatar misiu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

hybrid-model-binding's Issues

Failing to bind a single value to collection from query parameter

Maybe I'm trying to do this incorrectly, but when I try to bind query parameters to a collection it will bind great as long as there are multiple, but fails when there is a single value.

An example of the code I'm attempting to use:

[ApiController]
[Route("/test")]
public class TestController : ControllerBase
{
    [HttpGet]
    public IActionResult Test(Request request)
    {
        return Ok(request);
    }
}

public class Request
{
    [HybridBindProperty(Source.QueryString)]
    public List<int> Values { get; set; }
}

Results:

https://localhost:5001/test?values=1&values=2 => {"values":[1,2]}
https://localhost:5001/test?values=1 => {"values":null}

Model is not validated

My controller has the annotation [ApiController] which by default triggers automatic model validation and sends 400 response. However this functionality stops working when using hybrid-model-binding.
I created ActionFilter to do that manualy but context.ModelState.IsValid always return true even if I send empty body. I am using .net core 2.2.
Any idea why?

Is `name:` supposed to allow binding to different value names?

I'm trying to bind a model that looks like this:

   public class AccessTokenRequest
   {
      [HybridBindProperty(new[] { Source.QueryString, Source.Form }, name: "grant_type")]
      public string GrantType { get; set; }

      [HybridBindProperty(new[] { Source.QueryString, Source.Form }, name: "redirect_uri")]
      public Uri Redirecturi { get; set; }

   }

It doesn't seem that GrantType is using the grant_type parameter name. Am I misunderstanding the purpose of name?

Clearing the ModelState causes loss of initial binding errors and sets default model properties

Hi,

What's exactly the reason why the ModelState is being cleared first in src/HybridModelBinding/HybridModelBinder.cs on line 110?
Clearing the ModelState there will remove possible initial binding errors (e.g. providing a string for a property that should be a bool). This results in no binding error at all (ModelState is valid) and instead, all properties (including those with valid values) are set their defaults. So, the model binding failed and everything is set to default, but we're not informed of this (ModelState is valid). In the worst case scenario, we'd fully process the request as if it were a valid request (e.g. if all properties are optional).

Anyway, by simply removing the line that clears the ModelState first, I get the expected initial model binding errors and HybridModelBinding still works as expected when the model is valid. So, is there any particular reason why the ModelState is cleared first?

This issue appears to be the same or similar to the initial post of #55 . However, I can't relate my issue to the discussion that follows in that thread.

Incompatible with a project that also uses WebApiCompatShim

I've got a legacy project that originally used Web API and was ported to Dotnet core making use of microsoft's WebApiCompatShim.

I've got an API method that looks like this public List<MyModel> GetMyModel(int? size = null).

HybridModelBinding will throw an error on this line because it can't find a model of type Nullable<int>. The last resort to set the value of hydratedModel using Activator.CreateInstance also returns null.

Is there a way I can tell hybrid model binder to ignore WebApi controllers? I actually only need the library for MVC controllers.

Could there be a release with HybridMvcBuilderExtensions?

I spent a little while trying to figure out why I couldn't use the awesome looking .AddHybridModelBinder() only to find out it hasn't actually been released yet. Would it be possible to create a release with that functionality in please?

Cannot load type when .NET Core targeting .NET Framework

Could not load type 'Microsoft.AspNetCore.Mvc.Internal.IHttpRequestStreamReaderFactory' from assembly 'Microsoft.AspNetCore.Mvc.Core, Version=2.2.5.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.

Been using your project for a couple of personal ASP.NET Core projects, thought I would test it out at work trying to migrate a .NET 4.7.2 project to ASP.NET Core targeting .NET Framework, but I receive the above error.

I tried it out on an empty project and the same thing happened.

Will try and debug it during the week and see if I can figure out the exact cause.

Expection Property set method not found when binding json body from GET

Workaround on model use [HybridBindClass(defaultBindingOrder: new[] { Source.QueryString })] to disable JSON body binding on GET request. Querystring binding works as expected.

Expected: GET requests shouldn't have bodies submitted and if they do ignore the request or if you want to process them then not crash

Version .18

STACK: at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)\r\n at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index)\r\n at HybridModelBinding.HybridModelBinder.BindModelAsync(ModelBindingContext bindingContext)\r\n at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value)\r\n at Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider

Combining with Custom Model Binder

Hi all,

Great nuget package; thanks.

Is it possible to combining HybridModelBinder with another Custom IModelBinder.

My Request class has a complex type (Company) which I'm attempting to resolve using CompanyModelBinder in the example below. If I use [FromQuery] it all works, but changing to [FromHybrid] prevents the model binder from being hit.

Any help/info would be great.

CompanyModelBinder.cs

    public class CompanyModelBinderProvider: IModelBinderProvider
    {
        public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            return context.Metadata.ModelType == typeof(Company) 
                ? new CompanyModelBinder() 
                : null;
        }
    }

    public class CompanyModelBinder : IModelBinder
    {
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
                throw new ArgumentNullException(nameof(bindingContext));

            var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            if (valueProviderResult == ValueProviderResult.None)
                return Task.CompletedTask;

            bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);

            var value = valueProviderResult.FirstValue;

            if (string.IsNullOrEmpty(value)) 
                return Task.CompletedTask;
            
            var model = new Company(value);

            bindingContext.Result = ModelBindingResult.Success(model);

            return Task.CompletedTask;
        }
    }

Startup.cs....

services
                .AddMvc(config =>
                {
                    // Custom Model Binders.
                    config.ModelBinderProviders.Insert(0, new CompanyModelBinderProvider());
                 })
                .AddHybridModelBinder();

Request.cs

public class Request : IRequest<Result<Response>>
{
    public Company Company { get; set; }
}

Controller.cs

Using [FromQuery] works (The Company model binder is invoked)

public IActionResult Index([FromQuery]Request request)
{
   ...
}

...but using [FromHybrid]` doesn't.... :(

public IActionResult Index([FromHybrid]Request request)
{
   ...
}
GET /api/v1/company?company=Foo HTTP/1.1
Host: localhost:44398

Any ideas what I might be doing wrong?

Thanks.

HybridModelBinder.BindModelAsync doesn't respect BindPropertyAttribute

Let's say we have a model like this:

public class Foo
{
    [BindProperty(Name = "barrrr")]
    public string Bar { get; set; }
}

and a action like this:

[Route("[action]")]
public IActionResult TestFoo([FromHybrid]Foo foo)
{
    return new ContentResult() { Content = foo.Bar, ContentType = "text/plain" };
}

Now I'd like to perform a httprequest, specify the foo argument by QueryString barrrr=barvalue.
foo.Bar property will never be set to barvalue.
But if I change [FromHybrid] to [FromQuery], it works fine.

Provide Better Attribute Syntax

Currently

using Microsoft.AspNetCore.Mvc;

public class Person
{
    [FromQuery]
    public int Age { get; set; }

    [FromRoute]
    public string Name { get; set; }
}

Proposed

using Microsoft.AspNetCore.Mvc;

public class Person
{
    [From("query", "route", "headers")]
    public int Age { get; set; }

    [From("body", "query")]
    public string Name { get; set; }
}

This approach reduces the stacks of attributes that may build up on a model. Additionally, if people have their convention for models. They could just create their own attribute to avoid magic strings.

public class BodyQueryAttribute : FromAttribute {
  public BodyQueryAttribute()
     : base("body", "query") { }
}

Question: hybrid binding not working for me without attributes

Hi I may be using the package incorrectly but I am trying to create API endpoints with a single object and bind from the body and the route to my single object. Reading the documentation I assume for my situation if I installed the package and added it in Startup when configuring services my scenario would work out the box without any additional decoration. I have a endpoint like this

``

    [HttpPost("todolist/{listId}/todo")]
    public async Task<ActionResult> CreateTodo(CreateTodoItemCommand command)
    {
        //some code
    }

``

My command object looks like this

``

public class CreateTodoItemCommand : IRequest
{
public int ListId { get; set; }

    public string Title { get; set; } = string.Empty;
    
    public string Note { get; set; } = string.Empty;
}

``

Title and note are bound from the body of the request and I want ListId to be bound from the route. I had assumed as I had a single source for each property I would not need additional binding attributes on either the controller method or the contract. However this does not work and ListId is never bound. Title and Note are bound without issue. I have tried playing around with global fallback order but this does not seem to do anything. The only way I can get this to work is to add the HybridBindProperty with a source of route on the ListId property.

Ideally I dont want to add a reference to hybridbinding into my contract repository and did not think I would need to in this simple use case. The documents seem to suggest that if you have a single object on the controller method and that the standard order for binding is acceptable this should just work. Am I doing something wrong or is my scenario not supported. Any help is much appreciated. Regards

CQRS mappings with [FromHybrid]

Hello,

I'm trying to do some mappings in the direction of CQRS with [FromHybrid] in the controller actions.

One controller contract example looks like this:

[HttpPost("{organisationId}/teams")]
public async Task<ActionResult<Team>> CreateTeamAsync([FromHybrid] CreateTeamCommand createTeamCmd)

Here's what I've tried so far:

public class CreateTeamCommand
{
    public readonly Guid OrganisationId;
    public readonly string Name;

    public CreateTeamCommand(Guid organisationId, string name)
    {
        OrganisationId = organisationId;
        Name = name;
    }
}

and

public class CreateTeamCommand
{
        public Guid OrganisationId { get; internal set; }
        public string Name { get; internal set; }
}

Unfortunately neither works. The only one that works is the standard

public class CreateTeamCommand
{
    public Guid OrganisationId { get; set; }
    public string Name { get; set; }
}

Any ideas on this? Personally I would love to have support for this. I would also consider contributing if this is feasible to implement and if you hint me the direction.

IFormFile binding

Is it possible to bind IFormFile to a model when posting a form with multiple fields and one or more files?

Currently I am able to bind only form key/values with HybridModelBinding.
Binding works for Id property but not for file

    public class SetImageCommand
    {
        [HybridBindProperty(Source.Form, "UniqueName")]
        public string Id { get; set; }

        [HybridBindProperty(Source.Form, "MyImage")]
        public IFormFile Image { get; set; }
    }

Workaround:
public async Task<ActionResult> SetImage([FromForm(Name = "UniqueName")]string id, [FromForm(Name = "MyImage")]IFormFile image)

Add a sample project

The sample project should show how to use all the value providers, and let users exercise the binders.

Binding error passes silently

I've stumbled upon an odd behaviour with hybrid model binding. Consider this example:

Model:

public class AnotherDto
{
    public string SomeString { get; set; }
    public int  SomeInt { get; set: }
}

public class MyDto
{
    public string SomeString { get; set; }
    public DateTime SomeDateTime { get; set; }
    public AnotherDto SomeNestedDto { get; set; }
}

Controller endpoint:

public IActionResult SomeEndpoint([FromHybrid] MyDto dto)
{
    // code here...
}

Request:

{
    "someString" : "str",
    "someDateTime": "*imagine this is a valid datetime*"
    "someNestedDto": { "someProperty": 123, "anotherProperty": "..." }
}

In the request property "someNestedDto" has INTENTIONALLY a wrong type.

Behaviour: dto that comes to my controller method has empty fields with default values.

Expected behaviour: parse error. If this happens with [FromBody] attribute applied to MyDto, then a parse exception will be raised.

As for me such errors shouldn't pass silently.

Add Source.Claim to allow binding from ClaimsPrincipal

I'm searching for a custom model binder to use in .net 5 REST API project.
Your HybridModelBinder is almost perfect, but it lacks the ability to bind specific claim to model property.
The use case is simple. You need to get id of the user that is doing the request.

I've used this code in the past:

namespace Utils.Binders
{
    [AttributeUsage(AttributeTargets.Parameter)]
    public class FromClaimAttribute : Attribute, IBindingSourceMetadata, IModelNameProvider
    {
        public BindingSource BindingSource => ClaimBindingSource.Claim;

        public FromClaimAttribute(string type)
        {
            Name = type;
        }

        public string Name { get; }
    }

    public class ClaimValueProviderFactory : IValueProviderFactory
    {
        public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
        {
            context.ValueProviders.Add(new ClaimValueProvider(ClaimBindingSource.Claim, context.ActionContext.HttpContext.User));
            return Task.CompletedTask;
        }
    }

    public class ClaimValueProvider : BindingSourceValueProvider
    {
        private readonly ClaimsPrincipal _claimsPrincipal;

        public ClaimValueProvider(BindingSource bindingSource, ClaimsPrincipal claimsPrincipal) : base(bindingSource)
        {
            _claimsPrincipal = claimsPrincipal;
        }

        public override bool ContainsPrefix(string prefix) => _claimsPrincipal.HasClaim(claim => claim.Type == prefix);

        public override ValueProviderResult GetValue(string key)
        {
            var claimValue = _claimsPrincipal.FindFirstValue(key);
            return claimValue != null ? new ValueProviderResult(claimValue) : ValueProviderResult.None;
        }
    }

    public static class ClaimBindingSource
    {
        public static readonly BindingSource Claim = new(
            "Claim",
            "BindingSource_Claim",
            isGreedy: false,
            isFromRequest: true);
    }
}

maybe a similar value provider factory could be added to your project?

Enum type isn't binding from QueryString

I've got an enum type which I've declared as a property on a model and am issuing an HTTP GET request. I found that the value being passed in wasn't binding to the model which caused the enum to be set to its default value. I decided to make the property nullable to test whether the property was being set to the default value because no value was set or because it was somehow receiving the default value and discovered that the binding started working. I then reverted the change and unregistered the HybridModelBinderApplicationModelConvention to use the default model binding and saw that value was set properly. I then tested with the hybrid model binder again, but changed the request to an HTTP POST rather than a GET and it worked properly.

So, it seems there is an issue with the hybrid model binder with Enums when the values are sent via the QueryString.

Binder does not work when used with Newtonsoft.Json

I've noticed that after adding HybridModelBinding to my project JsonProperty attributes stopped working.

Below is a very simple controller that allows testing this behaviour:

namespace HybridModelBinding.Samples.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class DataController : ControllerBase
    {
        [HttpPost()]
        public ActionResult<string> GetResCreateponse([FromHybrid]SimpleDto dto)
        {
            return Ok($"Hello {dto.Name} {dto.IsAdmin}");
        }
    }

    public class SimpleDto
    {
        public string Name { get; set; }

        [JsonProperty("is_admin"), JsonPropertyName("is_admin")]
        [HybridBindProperty(Source.Body)]
        public bool IsAdmin  { get; set; }
    }
}

Every time I do a request I get this behavior:
image

When I remove AddHybridModelBinder from Startup everything works as expected.

Does HybridModelBinding work when used with Newtonsoft.Json?
@billbogaiv your feedback is more than welcome.

Rename IsUnsafe to Behavior

Since IsUnsafe sounds scary and not exactly what you want to portray to users of this library, I propose we change the property to Behavior.

using HybridModelBinding;
using Microsoft.AspNetCore.Mvc;

public class Person
{
    // Will continue to Bind through to Route
    // only keeping a non-null value in the Property
    [From(QueryString, Route, Behavior = Behavior.Last)]
    public int Age { get; set; }

    // Will bind on the first non-null value
    // and then stop.
    [From(Route, Body, Behavior = Behavior.First)]
    public string Name { get; set; }
}

The default behavior should be First, which will work even for one provider.

Default value on optional parameters

Hi,

We are trying to set default values on optional parameters.

    public class Query : IPagedQuery<Model>
    {
        [From(Source.Route)]
        public int ClientId { get; set; }
        [From(Source.QueryString)]
        public int PageSize { get; set; } = Pagination.MaxPageSize;
        [From(Source.QueryString)]
        public int PageNumber { get; set; } = 1;
    }

It seems HybridBinding tries to initialize the value even if query parameters are missing. In our case if PageSize or PageNumber is missing we would expect the value to be set to MaxPageSize or PageNumber. Any recommendation on this case?

Seb

Combine with ApiController attribute

Hi, I'm using .NET core 2.2 and hybrid binding works fine without ApiControllerAttribute.
I'm trying to bind a model which consists of values from route and body, and when attribute is applied, only body binding works. Is it possible to combine them together?

Crash on Startup with ASP.NET Core 2.1.x packages

When the base library packages use version 2.1.x, the following error is generated during sample startup. It occurs just by the library referencing the Microsoft.AspNetCore.Mvc.Core package. For now, version 2.0.4 seems to be the highest version to not cause the error.

System.MissingMethodException: Method not found: 'Void Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker..ctor(System.Diagnostics.DiagnosticSource, Microsoft.Extensions.Logging.ILogger, Microsoft.AspNetCore.Mvc.ActionContext, Microsoft.AspNetCore.Mvc.Filters.IFilterMetadata[], System.Collections.Generic.IList`1<Microsoft.AspNetCore.Mvc.ModelBinding.IValueProviderFactory>)'.
   at Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvokerProvider.CreateActionInvoker(ActionContext actionContext, PageActionInvokerCacheEntry cacheEntry, IFilterMetadata[] filters)
   at Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvokerProvider.OnProvidersExecuting(ActionInvokerProviderContext context)
   at Microsoft.AspNetCore.Mvc.Internal.ActionInvokerFactory.CreateInvoker(ActionContext actionContext)
   at Microsoft.AspNetCore.Mvc.Internal.MvcAttributeRouteHandler.<>c__DisplayClass12_0.<RouteAsync>b__0(HttpContext c)
   at Microsoft.AspNetCore.Builder.RouterMiddleware.<Invoke>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.<Invoke>d__7.MoveNext()

Consider creating abstractions package

If you like to have your models in a separate library that doesn't have references to framework specific packages like ASP.NET Core, MVC etc. then this is undermined if you want to apply HybridBindProperty attributes to their properties, as the dependencies of your package include ASP.NET Core packages. This could be avoided with a separate package for just abstractions/attributes. Arguably the naming of these attributes and their arguments somewhat undermines the abstraction of the models anyway, but at least without the additional packages.

Just something to consider! :)

Extend HybridModelBinderApplicationModelConvention to allow CancellationToken

From #20

When using .AddHybridModelBinder(), this adds a Convention to look for controller-actions which have one complex parameter (i.e. not a string or int) and no currently-assigned BindingSource and assign it with a BindingSource == "Hybrid".

It would be handy for the HybridModelBinderApplicationModel convention to also allow a second parameter to be a CancellationToken to allow the FromHybrid to be removed in this scenario:

public async Task<IActionResult> MyAction([FromHybrid] MyModel myModel, CancellationToken cancellationToken) { }

Rename Project To Smoothie?

Call this project Smoothie because of a few factors:

  • It allows you to mix value providers and model binders into one delicious model.
  • You feel sooo smooth when you use it.

Horizontal

smoothie_horizontal

Centered

smoothie_centered

Square / Icon

smoothie_square

Release notes?

Are there release notes for the changes from 0.17.0 to 0.18.0?

The model binding stopped working when I upgraded and I can't figure out what changed.

Refactor ModelBinder and ValueProvider into Source

The idea that something is a ModelBinder or a ValueProvider may be too much information for a developer. Moving all the constants into one class called Source may reduce the noise on attributes.

using HybridModelBinding;
using Microsoft.AspNetCore.Mvc;

public class Person
{
    [From(Source.QueryString)]
    public int Age { get; set; }

    [From(Source.Route)]
    public string Name { get; set; }
}

Which can also be reduced to the following in C# 6.

using HybridModelBinding;
using Microsoft.AspNetCore.Mvc;
// static class using
using HybridModelBinding.Source;

public class Person
{
    [From(QueryString)]
    public int Age { get; set; }

    [From(Route)]
    public string Name { get; set; }
}

Swashbuckle/Swagger integration

Hi,

I am having great success in using hybrid model binding to bind from route and body to a single model.
However the downside is that swashbuckle no longer functions correctly when generating swagger documentation.

public class AddOrUpdatePhoneNumbersRequest
    {
        [HybridBindProperty(Source.Route)]
        public string PersonId { get; set; }

        [HybridBindProperty(Source.Body)]
        public string Mobile { get; set; }

        [HybridBindProperty(Source.Body)]
        public string Home { get; set; }

        [HybridBindProperty(Source.Body)]
        public string Work { get; set; }

        [HybridBindProperty(Source.Body)]
        public PhoneNumberType PreferredPhoneNumberType { get; set; }
    }

Instead of the document example being displayed we just see the empty {} - also noting that the body thinks its coming from the query

image

Has anybody previously looked at adding a swashbuckle filter to correctly display the properties bound on the body?

FromHybrid parameter attribute for Core 2.1+

Hi, is the FromHybrid not working for .NET Core 2.1+?

image

Using [From(Source.____)] for the property seems ok but when using [FromHybrid] for the parameter is not, or am I using FromHybrid incorrectly?

image

An exception when dictionary binding occurs

The package throws an exception when model has a property of type Dictionary<string, string>.

I have registered HybridModelBinding in startup:

services.AddMvcCore()
    .AddHybridModelBinder()
    .AddApiExplorer();

I have a controller that has one parameter with model

public async Task<IndexModelResponse> CreateIndex(IndexModelRequest command) =>
     await Mediator.Send(command);

I have a model with one property of type Dictionary<string, string>:

public class IndexModel
{
    public IDictionary<string, string> Name { get; set; }
}

If I make a request to this endpoint I have an exception:

System.ArgumentException: The value "[en, Automatic]" is not of type "System.String" and cannot be used in this generic collection. (Parameter 'value')
at System.Collections.Generic.List`1.System.Collections.IList.Add(Object item)
at HybridModelBinding.HybridModelBinder.BindModelAsync(ModelBindingContext bindingContext)
at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value)
at Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<g__Bind|0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Request body:

{
    "Name": {
        "en": "Automatic"
    }
}

And additional question: Can I manually set by attribute when I want to HybridModelBinding, but not automatically when controller has a one parameter?

ASP.NET Core 3.1; SDK 3.1.405
Package version: 0.18.1 -alpha.4

I had tried to downgrade the package version to 0.18.0 and my output change to:

Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.ArgumentException: The value "[en, Automatic]" is not of type "System.String" and cannot be used in this generic collection. (Parameter 'value')
at System.Collections.Generic.List`1.System.Collections.IList.Add(Object item)
at HybridModelBinding.HybridModelBinder.BindModelAsync(ModelBindingContext bindingContext)
at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value)
at Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<g__Bind|0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Collections are not being bound

Issue Version: 0.4.0

I'm attempting to bind to a collection as outlined here: https://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/.

I have a model object defined as follows:

    public class GetRemittancesRequest
    {
        public int AccountId { get; set; }
        public DateTime? TransactionDate { get; set; }
        public decimal? TransactionAmount { get; set; }
        public ICollection<string> Expands { get; set; } 
    }

If I define my controller action as public IActionResult GetRemittances(GetRemittancesRequest request) then the Expands does not get bound, but if I define as follows then both the Expands property as well as the expands parameter are populated:

public IActionResult GetRemittances(GetRemittancesRequest request, ICollection<string> expands)

I don't quite understand why the second parameter is triggering the Expands property to get populated, but I'd of course like to not have the explicit expands to do so.

I'm still on version 0.4.0 because my project is targeting an 4.5.2 and upgrading to 4.6.2 is causing me some backward compatibility issues with other dependencies at the moment.

Map entire body to model property (rest of the properties bind from route)

I have a REST API that needs to support a dynamic model,
I have this endpoint:

[HttpPost("{entity}")]
[ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[AllowAnonymous]
public async Task<object> CreateSingleRecord2([ModelBinder(typeof(CreateSingleRecordBinder))] CreateSingleRecord model)
{
    //process model;
}

and this model:

public class CreateSingleRecord : ICommand<object>
{
    [FromRoute(Name ="entity")]
    public string Entity { get; init; }

    [FromBody]
    public IDictionary<string, object> Record { get; init; }
}

I'm doing this request:

curl --location --request POST 'https://localhost:7299/api/data/cars' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{
    "model": 1,
    "name": "Micra",
    "id":"a47d52de-fcd1-48e7-8656-7edb84dc78bd",
    "is_created": true,
    "date":"2022-09-23",
    "datetime":"2022-09-23 13:10"
}'

Right now I'm using this binder:

public class CreateSingleRecordBinder : IModelBinder
{
    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        if (!bindingContext.ActionContext.RouteData.Values.ContainsKey("entity"))
        {
            bindingContext.Result = ModelBindingResult.Failed();
            return;
        }

        var entityName = bindingContext.ActionContext.RouteData.Values["entity"]?.ToString();
        if (string.IsNullOrWhiteSpace(entityName))
        {
            bindingContext.Result = ModelBindingResult.Failed();
            return;
        }

        using var reader = new StreamReader(bindingContext.HttpContext.Request.Body);
        var body = await reader.ReadToEndAsync();
        var data = System.Text.Json.JsonSerializer.Deserialize<IDictionary<string, object>>(body);
        if (data == null || !data.Any())
        {
            //return failed result
            bindingContext.Result = ModelBindingResult.Failed();
            return;
        }

        var model = new CreateSingleRecord
        {
            Entity = entityName,
            Record = data
        };

        bindingContext.Result = ModelBindingResult.Success(model);
    }
}

I'd like to avoid writing a custom binder because the same request binds to:

[HttpPost("{entity}")]
[ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[AllowAnonymous]
public async Task<object> CreateSingleRecord([FromRoute] string entity, [FromBody] IDictionary<string, object> model)
{
    //process model
}

Sadly when I try to use FromHybrid I'm getting this error:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "00-5975e1c9239eebbea90add81d3698958-d4ad813d42423c4a-00",
    "errors": {
        "Record": [
            "The Record field is required."
        ]
    }
}

How can I use Your binder to bind entire body of request to Property of a model.
Can I use FromBody or do I have to create another binder, for example FromEntireBody?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.