2010-12-21 20 views
11

W ASP.NET MVC 3 RC2 domyślny moduł modelowania automatycznie analizuje treść żądania, jeśli wartość Content-Type jest ustawiona na application/json. Problem polega na tym, że na końcu strumienia pozostaje Request.InputStream. Oznacza to, że jeśli spróbujesz odczytać strumień wejściowy za pomocą własnego kodu, najpierw trzeba przywrócić go z powrotem do początku:Czy istnieje sposób wyłączenia obiektu JSON ModelBinder w środowisku ASP.NET MVC 3 RC2?

// client sends HTTP request with Content-Type: application/json and a JSON 
// string in the body 

// requestBody is null because the stream is already at the end 
var requestBody = new StreamReader(Request.InputStream).ReadToEnd(); 

// resets the position back to the beginning of the input stream 
var reader = new StreamReader(Request.InputStream); 
reader.BaseStream.Position = 0; 
var requestBody = reader.ReadToEnd(); 

Ponieważ używam Json.NET zrobić mój serializacji/deserializacji, chciałbym aby wyłączyć domyślną funkcję ModelBinder podczas wykonywania dodatkowej analizy. Czy istnieje jakiś sposób, aby to zrobić?

Odpowiedz

15

można umieścić następujące w Application_Start w Global.asax:

ValueProviderFactories.Factories.Remove(
      ValueProviderFactories.Factories.OfType<JsonValueProviderFactory>().First()); 

zakłada to jest tylko jeden tego typu (które domyślnie nie jest), ale może być łatwo zmieniona do pracy, jeśli jest więcej niż jeden. Nie wierzę, że istnieje czystszy sposób, jeśli tego właśnie szukasz.

+0

Dzięki, to się udało! Czy to jest nieudokumentowana funkcja, czy po prostu coś mi umknęło? –

+0

MVC domyślnie zawiera zestaw fabryk dostawców wartości. W MVC3 uwzględnili JsonValueProviderFactory jako jedną z wartości domyślnych. Cały powyższy kod wykonuje go i usuwa, gdy aplikacja się uruchomi. Innym sposobem obejścia tego problemu może być nieuzyskanie żądań ajaxowych za pomocą aplikacji typu content/json, ale uważam, że usunięcie fabryki dostawców wartości jest prawdopodobnie bardziej poprawne. –

+0

Dzięki za wyjaśnienie. Nie wiedziałem nawet, że istnieją fabryki dostawców dla takich rzeczy, jak zapytania i trasy. Zakładałem, że modelBinder to wszystko obsługuje. –

0

Jestem oczywiście dość późno, odpowiadając na to, ale opracowałem sposób na zmianę IValueProvider dla określonej akcji w MVC5. Nie starałem się sprawdzić, czy jest to możliwe w MVC3, ponieważ to pytanie jest stare, ale zakładam, że jest nieco podobny.

Nota prawna: Nie jest ładna.

Najpierw tworzymy nowy interfejs możemy realizować w atrybucie zrobić konfiguracje działania specyficzne:

internal interface IActionConfigurator 
{ 
    void Configure(ControllerContext controllerContext, ActionDescriptor actionDescriptor); 
} 

Następnie tworzymy niestandardowe ControllerActionInvoker (lub AsyncControllerActionInvoker jeśli używasz async) zahaczyć nasz nowy interfejs:

internal sealed class CustomControllerActionInvoker : AsyncControllerActionInvoker 
{ 
    protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName) 
    { 
     var actionDescriptor = base.FindAction(controllerContext, controllerDescriptor, actionName); 
     var configurators = actionDescriptor.GetCustomAttributes(typeof(IActionConfigurator), true).Cast<IActionConfigurator>(); 
     foreach (var configurator in configurators) 
      configurator.Configure(controllerContext, actionDescriptor); 
     return actionDescriptor; 
    } 
} 

teraz musimy wdrożyć zwyczaj DefaultControllerFactory ustawić Controller.ActionInvoker:

internal sealed class CustomControllerFactory : DefaultControllerFactory 
{ 
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) 
    { 
     var instance = base.GetControllerInstance(requestContext, controllerType); 
     var controller = instance as Controller; 
     if (controller != null) 
      controller.ActionInvoker = new CustomControllerActionInvoker(); 
     return instance; 
    } 
} 

Wreszcie ruszyliśmy naszą fabrykę zwyczaj kontrolera jako domyślny w kodzie startowym:

ControllerBuilder.Current.SetControllerFactory(typeof(CustomControllerFactory)); 

i realizować naszą IActionConfigurator interfejs w atrybutu niestandardowego:

internal sealed class IgnoreJsonActionConfiguratorAttribute : Attribute, IActionConfigurator 
{ 
    public void Configure(ControllerContext controllerContext, ActionDescriptor actionDescriptor) 
    { 
     // Here we can configure action-specific stuff on the controller 
     var factories = ValueProviderFactories.Factories.Where(f => !(f is JsonValueProviderFactory)).ToList(); 
     controllerContext.Controller.ValueProvider = new ValueProviderFactoryCollection(factories).GetValueProvider(controllerContext); 
    } 
} 

Od nowej instancji kontrolera jest tworzony na każde żądanie, jesteśmy w stanie ustawić na sterowniku określone wartości, aby zmodyfikować sposób, w jaki MVC przetwarza akcję.

[AcceptVerbs(HttpVerbs.Post)] 
[IgnoreJsonActionConfigurator] 
public async Task<ActionResult> Foo() { ... } 
Powiązane problemy