2012-08-10 16 views
15

Ho una classe che eredita da ApiController. Ha un metodo Put come questo:Come ottenere il contenuto già letto

[PUT("user/{UserId}")] 
public HttpResponseMessage Put(string userId, PaymentRequest paymentRequest) 
{ 
    // Calling business logic and so forth here 
    // Return proper HttpResponseMessage here 
} 

Il metodo funziona bene come sopra. Ora ho bisogno di convalidare la firma della chiamata al metodo, ma qui mi imbatto in un problema. La firma è essenzialmente una combinazione di metodo + url + corpo. Il metodo che posso ottenere chiamando Request.Method e l'url che posso ottenere chiamando Request.RequestUri.ToString(), ma non riesco a ottenere il corpo come era prima dello è stato automaticamente deserializzato in un Oggetto PaymentRequest dal framework asp.net MVC4.

Il mio primo tentativo:. Come ho ormai capito Request.Content.ReadAsStringAsync() Risultato restituisce nulla. Questo perché il contenuto può essere letto solo una volta.

Il mio secondo tentativo: Ho provato a serializzarlo su una stringa JSON.

var serializer = new JavaScriptSerializer(); 
var paymentRequestAsJson = serializer.Serialize(paymentRequest); 

Il problema con questo è che la formattazione risulta leggermente diversa dalla parte del corpo della firma. Ha gli stessi dati, ma alcuni più spazi.

Non riesco a modificare ciò che chiama il chiamante del mio metodo Put, poiché si tratta di un componente di terze parti. Cosa dovrei fare?

risposta

27

Si potrebbe leggere dalla richiesta sottostante:

using (var stream = new MemoryStream()) 
{ 
    var context = (HttpContextBase)Request.Properties["MS_HttpContext"]; 
    context.Request.InputStream.Seek(0, SeekOrigin.Begin); 
    context.Request.InputStream.CopyTo(stream); 
    string requestBody = Encoding.UTF8.GetString(stream.ToArray()); 
} 
+0

Perché "Request.InputStream" non funziona qui? Perché la necessità di un contesto? –

+3

Perché non esiste tale proprietà 'Request.InputStream'. Non dimenticare che all'interno di un ApiController la proprietà Request è di tipo 'HttpRequestMessage' e non' HttpRequestBase'. –

+0

Oh - Mi sto confondendo scrivendo questi esempi in MVC3, il mio male senza dubbio ... –

13

non si include il parametro di corpo nella firma e che vi permetterà di tamponare il contenuto e leggere i contenuti tutte le volte che vuoi.

[PUT("user/{UserId}")] 
public HttpResponseMessage Put(string userId) 
{ 
    Request.Content.LoadIntoBufferAsync().Wait(); 
    var paymentRequest = Request.Content.ReadAsAsync<PaymentRequest>().Result; 
    var requestBody = Request.Content.ReadAsStringAsync().Result; 
    // Calling business logic and so forth here 
    // Return proper HttpResponseMessage here 
} 
+0

Anche questo funziona. E sembra leggermente più elegante. Grazie! – Halvard

+1

@Halvard Funzionerà anche in modalità self host. Non penso che HttpContextBase sia accessibile allo stesso modo in modalità self-host. –

0

una risposta molto in ritardo, ma di recente ho avuto la stessa sfida da superare.

Mi sono avvicinato a questo un po 'diverso senza dover ottenere i dati da httpContext (può essere abbastanza costoso per l'applicazione web di transazione ad alto volume).

ho creato una semplice interfaccia e reso ogni controller sua attuazione:

public interface IBaseControllerData 
{ 
    object Entity { get; set; } 
} 

Ho quindi impostare la proprietà entità del controllore al carico utile JSON per ogni post e mettere l'azione. Infine, ho recuperato i dati entità all'interno del metodo ignorato ActionFilterAttribute.OnActionExecuted e serializzato a JSON prima di iniettare nel MongoDB:

object entity = ((IBaseControllerData)actionExecutedContext.ActionContext.ControllerContext.Controller).Entity; 
        requestBody = Newtonsoft.Json.JsonConvert.SerializeObject(entity); 

Speranza che aiuta!

Cheers

Problemi correlati