Модель WebAPI [ModelBinder] с классом интерфейса при указании реализации

#asp.net-mvc #interface #asp.net-web-api2 #interface-implementation

#asp.net-mvc #интерфейс #asp.net-web-api2 #интерфейс-реализация

Вопрос:

Возможно ли передать в ModelBinder, какую реализацию вы хотите использовать встроенную?

Учитывая следующие определения:

 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;  }
    }
}
  

Учитывая этот маршрут в asp.net ядро mvc:

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

Мне не нужен другой класс ModelBinder для каждой реализации, скорее я хотел бы, чтобы каждый маршрут указывал, какая реализация встроена.

Итак, что-то вроде:

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

}
  

Или:

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

    }
  

Таким образом, класс SomeBinder может получить доступ к тому, какая реализация запрашивается в методе BindModelAsync класса SomeBinder: IModelBinder.

 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;
        }
  

Комментарии:

1. Это действительно не имеет смысла. Конечные точки Restful API должны быть уникальными на основе URL. Я не должен быть в состоянии передать Customer в User API только потому, что он реализует тот же интерфейс. Для меня это просто выглядит как чрезмерно сложная проблема, которая существует только потому, что она плохо спроектирована (поскольку использование интерфейса вместо класса не дает реальной выгоды).

2. Если контракт достаточно широкий, тогда да? Все, что интерфейс / контракт собирается сделать, это гарантировать наличие определенных свойств и / или методов. При десериализации на основе интерфейса клиент / пользователь не будет существовать, а будет существовать только контракт. Затем сервер может определить, хочет ли он использовать клиентскую или пользовательскую реализацию.

3. Вы не можете выполнить десериализацию в интерфейс. Итак, вы указываете связующему модели десериализоваться в тип, но передаете только определенные свойства интерфейса. Способ, способ, способ, способ чрезмерно сложный. Просто привяжитесь к базовому (неабстрактному) базовому классу, который обладает всеми свойствами интерфейса. Теперь ни одна из ваших проблем не существует.

4. Да, вы можете! В json вы можете выполнить десериализацию в интерфейс. Что означает удаление всего, что было добавлено разработчиком. Что осталось? Только контракт. Если вы определяете контракт и позволяете клиенту выполнять его реализацию, а серверу — ее реализацию, вы можете создать расширенную реализацию для сервера, и тогда клиент может быть сверхбогатым или сверхдешевым.

5. В .Net нет такого понятия, как экземпляр интерфейса . Итак, нет, у вас не может быть экземпляра интерфейса. У вас могут быть объекты времени выполнения, которые являются производными от интерфейса и передаются только как интерфейсы, но они все еще являются конкретными объектами.

Ответ №1:

Используйте обобщенные.

 public class SomeBinder<TConcreteType> : IModelBinder
{
}
  

Затем ваша подпись становится

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

Затем десериализация выполняется:

 JsonConvert.DeserializeObject<TConcreteType>(json)
  

Однако, основываясь на вашем последнем комментарии, похоже, что вам просто нужно предотвратить повторную отправку вместо этой запутанной привязки модели.

Итак, допустим, клиент знает, что в серверной реализации есть методы безопасности, и пытается сопоставить подпись, надеясь, что, например, все будет отключено. Это явно указывает на то, что вы ожидаете. И вы явно ожидаете только определения контракта и ничего больше.

Выдержка:

Массовое присвоение обычно происходит во время привязки модели как части MVC. Простым примером может быть то, что у вас на веб-сайте есть форма, в которой вы редактируете некоторые данные. У вас также есть некоторые свойства в вашей модели, которые недоступны для редактирования как часть формы, но вместо этого используются для управления отображением формы или могут вообще не использоваться.

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

Итак, идея здесь в том, что вы отображаете только один входной тег в разметку, но вы отправляете его в метод, который использует ту же модель, что и вы использовали для рендеринга:

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

Однако с помощью простых манипуляций с HTML или с помощью Postman / Fiddler злоумышленник может присвоить полю isAdmin значение true. Привязка модели будет послушно привязывать значение, и вы только что стали жертвой массового присвоения / перепостинга:

Итак, как вы можете предотвратить эту атаку? К счастью, существует множество различных способов, и они, как правило, такие же, как подходы, которые вы могли использовать в предыдущей версии ASP.NET. Здесь я рассмотрю ряд ваших вариантов.

Продолжайте статью…

Комментарии:

1. Завершено обновление на основе ваших последних комментариев. Я надеюсь, что вы беспокоитесь только о чрезмерной публикации.