简体   繁体   中英

How to bind a property to a complex object using WebApi and a custom value binder?

I have different web API controllers that are secured using claims. So, in my controllers, I'm using asp.net Identity and the User.Identity is of type ClaimsIdentity.

One of those claims is the current userId logged to the application.

When I'm posting a complex object like an Order for instance and that Order has a property UserId like the following example, I'd like that property to be filled by the value that is in the claim.

class Order
{
  public int Quantity { get; set; }
  public int ProductId { get; set; }
  public int UserId { get; set; }
}

class OrdersController : BaseApiController 
{
  [HttpPost]
  public IHttpActionResult Post(Order order)
  {
    // do something
    return Ok();
  }
}

So, what I want is to have the Quantity and ProducId properties to be filled with values coming from the body of the request, and the UserId from the Claims.

I've tried creating a custom Value Provider like the following:

public class IdentityClaimsValueProvider : IValueProvider
{
    private readonly Dictionary<string, string> _claims;
    private readonly string[] _claimsKeys;

    public IdentityClaimsValueProvider(Dictionary<string, string> claims)
    {
        _claims = claims;
        _claimsKeys = claims.Keys.ToArray();
    }

    public bool ContainsPrefix(string prefix)
    {
        if (prefix == null)
            throw new ArgumentNullException(nameof(prefix));

        return _claimsKeys.Any(x => prefix.Equals(x, StringComparison.OrdinalIgnoreCase));
    }

    public ValueProviderResult GetValue(string key)
    {
        if (key == null)
            throw new ArgumentNullException(nameof(key));

        var claim = _claimsKeys.FirstOrDefault(x => key.Equals(x, StringComparison.OrdinalIgnoreCase));
        if (claim != null && _claims[claim] != null)
        {
            return new ValueProviderResult(_claims[claim], _claims[claim], CultureInfo.CurrentCulture);
        }

        return null;
    }
}

public class IdentityClaimsValueProviderFactory : ValueProviderFactory
{
    public override IValueProvider GetValueProvider(HttpActionContext actionContext)
    {
        var controller = actionContext.ControllerContext.Controller as WebApiBaseController;
        if (controller == null)
            return null;

        var claims = controller.GetUserIdentity().Claims;

        var claimsValues = new Dictionary<string, string>
        {
            { "userId", claims.First(x => x.Type == Common.Constants.ClaimTypes.USER_ID).Value }
        };

        return new IdentityClaimsValueProvider(claimsValues);
    }
}

I also added this to my config:

config.Services.Add(typeof(ValueProviderFactory), new IdentityClaimsValueProviderFactory());

But that does not work. When I make a post, I have the Quantity and ProductId properties filled, but not the UserId

You must change the signature of the action by :

public IHttpActionResult Post([ValueProvider(typeof(IdentityClaimsValueProviderFactory))]string userId, Order order)

If you want to have the value of useId in order property, you must to change the class IdentityClaimsValueProviderFactory

Regards.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM