1

Is it possible to pass into the ModelBinder which implementation you want to use inline?

Given the following definitions:

public interface ISomeInterface
{
    string MyString{get;set;}
}

public class SomeInterfaceImplementation_One : ISomeInterface
{
    private string _MyString;

    public string MyString
    {
       get {return "This is implementation One " + _MyString ; }
       set { _MyString = value;  }
    }
}

public class SomeInterfaceImplementation_Two : ISomeInterface
{
    private string _MyString;

    public string MyString
    {
       get {return "This is implementation Two" + _MyString ; }
       set { _MyString = value;  }
    }
}

Given this route in asp.net mvc core:

public ActionResult InterfaceWithInlineImplementation([ModelBinder(typeof(SomeBinder))]ISomeInterface SomeInterface)
{
       //Return actionresult
}

I do not want a different ModelBinder class for each implementation rather I would like each route to specify which implementation inline.

So something like:

[UseImplementation(SomeInterfaceImplementation_One)]
public ActionResult InterfaceWithInlineImplementation([ModelBinder(typeof(SomeBinder))]ISomeInterface SomeInterface)
{

}

Or:

 public ActionResult InterfaceWithInlineImplementation([ModelBinder(typeof(SomeBinder), ConcreteType = SomeInterfaceImplementation_Two )]ISomeInterface SomeInterface)
    {

    }

This way the SomeBinder class can access which implementation is being requested in the BindModelAsync method of SomeBinder : IModelBinder class.

public class SomeBinder : Microsoft.AspNetCore.Mvc.ModelBinding.IModelBinder
    {

        public Task BindModelAsync(Microsoft.AspNetCore.Mvc.ModelBinding.ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
                throw new ArgumentNullException(nameof(bindingContext));

            string valueFromBody = string.Empty;

            using (var sr = new StreamReader(bindingContext.HttpContext.Request.Body))
            {
                valueFromBody = sr.ReadToEnd();
            }

            if (string.IsNullOrEmpty(valueFromBody))
            {
                return Task.CompletedTask;
            }

            var settings = new JsonSerializerSettings()
            {
                ContractResolver = new InterfaceContractResolver(), // Need requested implementation from InterfaceWithInlineImplementation() method

            }; 

            var obj = JsonConvert.DeserializeObject(valueFromBody, [**Need Requested Implementation from Method**], settings);
            bindingContext.Model = obj;


            bindingContext.Result = ModelBindingResult.Success(obj);


            return Task.CompletedTask;
        }
Watson
  • 1,385
  • 1
  • 15
  • 36
  • This really doesn't make sense. Restful API endpoints should be unique based on url. I shouldn't be able to pass a `Customer` to a `User` API just because it implements the same interface. To me this just looks like an overly complicated problem that only exists because it's poorly designed (as using an interface instead of a class provides no **real** benefit). – Erik Philips Mar 28 '19 at 00:56
  • If the contract is broad enough, then yes? All interface/contract is going to do is guarantee certain properties and or methods are present. When you deserialize based on the Interface a customer/user won't exist but only the contract. The server can then determine if it wants to use a customer or user implementation. – Watson Mar 28 '19 at 01:03
  • You can't deserialize to an interface. So you're telling a model binder to deserialize to a type, but only pass specific interface properties. Way way way overly complicated. Just bind to a base (non-abstract) base class that has all the properties of the interface. Now none of your issues exist. – Erik Philips Mar 28 '19 at 01:04
  • Yes you can! In json you can deserialize to the interface. Which means stripping out anything that was added by the implementor. Whats left? Only the contract. If you define the contract and let the Client do its implementation and let the server do its implementation you can create rich implementation for the server and then the client can be super rich or super cheap. – Watson Mar 28 '19 at 01:05
  • There is no such thing as an *Instance of an Interface* in .Net. So no *you cannot* have an instance of an interface. You can have run-time objects that derive from an interface and only passed as interfaces, but they are still concrete objects. – Erik Philips Mar 28 '19 at 01:08
  • Yes, but what you want to do is define a contract, that whenever you serialize to any implementation you guarantee only the contract is honored. Your implementation for client poor, client rich, server poor, server rich can change but the contract will remain. Make sense? – Watson Mar 28 '19 at 01:10
  • So since we cannot have an *instance of an interface* where is the advantage of using an interface as the method parameter instead of a concrete class. For example, if you said, `I have code that uses reflection to expose only the properties of interface for consumers` that makes sense (except that one could do the same for a class). As it stands there is only complication of intent without any visible benefit. – Erik Philips Mar 28 '19 at 01:12
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/190802/discussion-between-erik-philips-and-watson). – Erik Philips Mar 28 '19 at 01:14
  • The biggest item is to remind the programmer that only the interface will be serialized. The function will strip out everything but the interface definition, so no matter how the server deserializes into any implementation it can expect only the contract portion to be coming from the payload. So lets say the client knows that the server implementation has security methods and tries to match the signature hoping everything get deseriazled for example. Its being explicit as to what you're expecting. And you're explicitly expecting only the contract definition and nothing more. – Watson Mar 28 '19 at 01:15

1 Answers1

2

Use generics.

public class SomeBinder<TConcreteType> : IModelBinder
{
}

Then your signature becomes

public ActionResult InterfaceWithInlineImplementation(
  [ModelBinder(typeof(SomeBinder<SomeInterfaceImpelemtation_One>))]ISomeInterface SomeInterface)

Then deserialization is:

JsonConvert.DeserializeObject<TConcreteType>(json)

However based on your last comment it sounds like you just need to Prevent overposting instead of this convoluted model binding.

So lets say the client knows that the server implementation has security methods and tries to match the signature hoping everything get deseriazled for example. Its being explicit as to what you're expecting. And you're explicitly expecting only the contract definition and nothing more.

Excerpt:

Mass assignment typically occurs during model binding as part of MVC. A simple example would be where you have a form on your website in which you are editing some data. You also have some properties on your model which are not editable as part of the form, but instead are used to control the display of the form, or may not be used at all.

public class UserModel
{
  public string Name { get; set; }
  public bool IsAdmin { get; set; }
}

So the idea here is that you only render a single input tag to the markup, but you post this to a method that uses the same model as you used for rendering:

[HttpPost]
public IActionResult Vulnerable(UserModel model)
{
    return View("Index", model);
}

However, with a simple bit of HTML manipulation, or by using Postman/Fiddler , a malicious user can set the IsAdmin field to true. The model binder will dutifully bind the value, and you have just fallen victim to mass assignment/over posting:

So how can you prevent this attack? Luckily there's a whole host of different ways, and they are generally the same as the approaches you could use in the previous version of ASP.NET. I'll run through a number of your options here.

Continue to article...

Erik Philips
  • 53,428
  • 11
  • 128
  • 150