Как передать данные контроллера WebAPI таким образом, чтобы у опубликованной модели был доступ во время привязки / десериализации или проверки - PullRequest
1 голос
/ 27 сентября 2019

Рассмотрим этот контроллер WebAPI, где некоторые метаданные извлекаются при создании экземпляра.

public class MyController : ApiController
{
    private readonly MetaData _metaData;

    public MyController(IService service) // IService is injected via Unity Container DI
    {
        _metaData = service.GetMetaData();
    }

    [ValidateModel]
    public IHttpActionResult Post([FromBody] PostModel model)
    {
        // do something
        return new ResponseMessageResult(null);
    }
}

И предположим, что класс PostModel действия Post определен следующим образом:

public class PostModel : IValidatableObject
{
    ComplexObject Data { get; set; }

    [OnDeserialized]
    internal void OnDeserialized(StreamingContext context)
    {
        // do something, using _metaData
    }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        // validate, using _metaData
    }
}

Я хочу свой PostModelиметь идеальный доступ к объекту MyController _metaData к моменту вызова OnDeserialized (), но я бы согласился, по крайней мере, к моменту выполнения Validate ().

Я рассмотрел:

  • ActionFilter - невозможно, поскольку привязка / проверка модели происходит до выполнения ActionFilter.
  • Пользовательский ValueProvider / ModelBinder. Я подумал, что, возможно, они вместе позволят мне определить StreamingContext, используемый в OnDeserialized (), и установить для его свойства Context значение_metaData.Но даже если это возможно, я не смог бы найти способ сделать это правильно.
  • MessageHandler - я думаю, что обработчик сообщений для каждого маршрута не идеален, но я мог бы использовать внедрение зависимостей для получения _metaData изатем ... добавить данные этого объекта к опубликованным данным?Это кажется хакерским, и я не очень восторженно отношусь к этому типу решений.
  • Внедрение зависимости - я мог бы дать PostModel ссылку на сервис во время его собственной реализации, однако Контроллер и Модель живут в отдельных проектах,с модельным проектом, не имеющим зависимости Unity Container в настоящее время.Вместо этого он использует проект Controller и другие ссылочные проекты, использующие собственный DI для передачи любых необходимых данных.

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

Два дополнительных примечания:

  • Я использую Asp.Net WebAPI и не имею доступа к основным атрибутам WebAPI, фильтрам и т. Д.
  • Действие Post находится в ProjectA, но PostModel находится вProjectB.ProjectA ссылается на projectB, но обратное неверно.ProjectB в настоящее время не зависит от Unity Container DI.

Заранее благодарен за помощь!

1 Ответ

0 голосов
/ 30 сентября 2019

Вы можете использовать IModelBinder для десериализации и проверки вашей PostModel.Делая это таким образом, вы получите доступ к вашему экземпляру Controller во время десериализации.Этот код довольно грубый, но вы должны понять.

Реализация IModelBinder

public class PostDataModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType != typeof(PostDataModel))
        {
            return false;
        }

        try
        {
          // Get the content of the request, deserialize into your model object
          Task<string> bodyTask = actionContext.Request.Content.ReadAsStringAsync();
          PostDataModel vm = JsonConvert.DeserializeObject<PostDataModel>(bodyTask.Result);

          // Retrieve your metadata from your controller.  In order to know it is there, have your controller implement an interface with a Metadata property that is accessible.
          // Your code shows it as private, you'll need to loosen that up so the ModelBinder has access
          var metadata = ((IMetadata)actionContext.ControllerContext.Controller).Metadata;

          // do stuff with metadata to validate your object however you wish
          vm.PopulateValidationErrors();

          // return true if you were able to deserialize this object, false if you couldn't 
          return vm != null;
        }
        catch {
          // do logging
          return false;
        }
    }
}

Использование IModelBinder глобально, добавив его в метод Register WebApiConfig config.BindParameter(typeof(PostDataModel), new PostDataModelBinder());

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...