2012-08-10 19 views
15

Tengo una clase que hereda de ApiController. Tiene un Put-método como este:Cómo obtener contenido que ya está leído

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

El método funciona bien, ya que es anterior. Ahora necesito validar la firma de la llamada al método, pero aquí me encuentro con un problema. La firma es esencialmente una combinación de método + url + cuerpo. El método que puedo obtener llamando a Request.Method y la URL que puedo obtener llamando a Request.RequestUri.ToString(), pero no puedo obtener el cuerpo como lo fue antes de se deserializó automáticamente en una Objeto PaymentRequest por el framework asp.net MVC4.

Mi primer intento:. ya que ahora tengo entendido Request.Content.ReadAsStringAsync() Resultado no devuelve nada. Esto se debe a que el contenido solo se puede leer una vez.

Mi segundo intento: Intenté serializarlo de nuevo en una cadena JSON.

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

El problema es que el formato es ligeramente diferente de la parte del cuerpo de la firma. Tiene los mismos datos, pero algunos espacios más.

No puedo cambiar lo que hace la persona que llama de mi método Put, ya que este es un componente de un tercero. ¿Que debería hacer?

Respuesta

27

Se podía leer en la solicitud subyacente:

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

¿Por qué doesnt simplemente 'Request.InputStream' funciona aquí? ¿Por qué la necesidad de contexto? –

+3

Porque no hay tal propiedad 'Request.InputStream'. No olvide que dentro de un ApiController, la propiedad Request es de tipo 'HttpRequestMessage' y no' HttpRequestBase'. –

+0

Oh - Me estoy confundiendo al escribir estos ejemplos en MVC3, mi mal sin duda ... –

13

no incluyen el parámetro de cuerpo en la firma y que le permitirá a amortiguar el contenido y leer el contenido tantas veces como desee.

[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

Esto también funciona. Y parece un poco más elegante. ¡Gracias! – Halvard

+1

@Halvard También funcionará en el modo de host independiente. No creo que se pueda acceder a la HttpContextBase de la misma forma en el modo autohospedado. –

0

Una respuesta muy tardía, pero recientemente tuve el mismo desafío que superar.

Me he aproximado a eso un poco diferente sin tener que obtener los datos del httpContext (puede ser bastante costoso para la aplicación web de transacciones de gran volumen).

creé una interfaz sencilla e hizo implementarlo cada controlador:

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

entonces he establecer la propiedad de la entidad del controlador de la carga útil JSON para cada puesto y poner acción. Por último, fui a buscar los datos de la entidad dentro del método anulado ActionFilterAttribute.OnActionExecuted y serializado que a JSON antes de inyectar en MongoDB:

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

Espero que ayude!

Cheers

Cuestiones relacionadas