admin管理员组

文章数量:1352141

Short version. I need to implement a custom model binder in an ASP.NET Core 9 Web API.

Why? I am working on teasing the Web API out of a .NET Framework 4 (App_2014) web app and into its own .NET 9 (Service_2025) code base.

We want to have them as two separate applications going forward.

The data stores that Service_2025 will be using have changed slightly over the last 10 years. I want to avoid porting legacy data models from App_2014 to Service_2025 just to make model binding in the service work.

I know how to transform the data sent by App_2014 into the latest data models. Therefore, I figured I would use a custom model binder.

The issue I am running into is that the only the RouteValueProvider is present in the ValueProvider collection of the ModelBindingContext when my custom BindModelAsync is called.

I have reviewed the Microsoft documentation at .0, especially the custom model binder sample.

I have tried my controller action method with and without the FromBody attribute before the parameter that should be bound. I have tried this using the ModelBinder attribute on my model class, and also using a IModelBinderProvider class that was registered during the AddControllers method call on startup.

What am I missing? How do I get the BodyValueProvider included in the ValueProviders collection of the ModelBindingContext?

If I can't get this to work, I will just bring in the data as a JsonObject and parse it out.

Thanks.

POST  {{WebApplication1_HostAddress}}/api/overrides/save
Content-Type: application/json
Accept-Language: en-US,en;q=0.5

{
  "ean":"2940192561911",
  "expandWorkId":false,
  "overrideSetDetail":{
    "setName":"Test 1",
    "startDate":"2025-04-01T14:12:50.182Z",
    "endDate":"2026-04-01T14:12:50.182Z",
    "importDate":"1970-01-01T00:00:00.000Z",
    "Comment":"Linked to EAN 9781250869548 with WorkId 1146328438.",
    "isDeleted":false,
    "isNew":true,
    "allowAutoFill":false,
    "requiredRecsToExpire":0,
    "recs":[
      {"ean":"2940177201672","position":2,"overrideType":8,"originalPosition":2,"isFixed":true,"reason":"Batch add on override set creation."},
      {"ean":"2940178784112","position":4,"overrideType":8,"originalPosition":4,"isFixed":true,"reason":"Batch add on override set creation."},
      {"ean":"2940177114248","position":5,"overrideType":8,"originalPosition":5,"isFixed":true,"reason":"Batch add on override set creation."},
      {"ean":"9780935112962","position":7,"overrideType":8,"originalPosition":7,"isFixed":true,"reason":"Batch add on override set creation."},
      {"ean":"9781631492563","position":9,"overrideType":8,"originalPosition":9,"isFixed":true,"reason":"Batch add on override set creation."}
    ]
  }
}


[Route("api/[controller]")]
[ApiController]
public class OverridesController : ControllerBase
{
    [HttpPost("save")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
    public IActionResult Save(OverrideSetModel model)
    {
        try
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            // await _overrideDataService.SaveOverrideSet(model.Ean, model.ExpandWorkId, model.OverrideSet!, ModelState);
            return Ok();
        }
        catch (Exception ex)
        {
            return new StatusCodeResult(StatusCodes.Status500InternalServerError);
        }
    }
}

[ModelBinder(typeof(OverrideSetModelBinder))]
public class OverrideSetModel
{
    /// <summary> Gets or sets the ean. </summary>
    /// <value> The ean. </value>

    [Required]
    public long Ean { get; set; }

    /// <summary> Gets or sets a value indicating whether the expand work identifier. </summary>
    /// <value> True if expand work identifier, false if not. </value>

    [Required]
    public bool ExpandWorkId { get; set; }

    /// <summary> Gets or sets the override set detail. </summary>
    /// <value> The override set detail. </value>

    [Required]
    public JsonObject OverrideSetDetail { get; set; }
}

public class OverrideSetModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext is null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        var ean = bindingContext.ValueProvider.GetValue("ean").FirstValue;
        var expandWorkId = bindingContext.ValueProvider.GetValue("expandWorkId").FirstValue;
        var overrideSetDetail = bindingContext.ValueProvider.GetValue("overrideSetDetail").FirstValue;

        var modelName = bindingContext.ModelName;

        // try to fetch the value of the argument by name
        var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);

        if (valueProviderResult == ValueProviderResult.None)
        {
            return Task.CompletedTask;
        }

        bindingContext.Result = ModelBindingResult.Success(null);
        return Task.CompletedTask;
    }
}

public class OverrideSetModelBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (context.Metadata.ModelType == typeof(OverrideSetModel))
        {
            return new BinderTypeModelBinder(typeof(OverrideSetModelBinder));
        }

        return null;
    }
}

本文标签: net 90ASPNET Core 9 Custom Model Binding doesn39t work with bodyStack Overflow