4

I'm facing a situation where I've to read the form data from incoming request in ASP.NET Web API twice (from model binder and filter). I've tried using LoadIntoBufferAsync but no luck.

// from model binder
Request.Content.LoadIntoBufferAsync().Wait();
var formData = Request.Content.ReadAsFormDataAsync().Result;

// from filter
var formData = Request.Content.ReadAsFormDataAsync().Result;
4

3 回答 3

2

The problem is that the underlying buffer for content is a forward-only stream that can only be read once.

Why do you need to read it twice? A little more context would help. Is it that you are reading from two separate filters?

EDIT: might try reading directly from MS_HttpContext and using that as your content stream (don't think this works in a self hosted environment).

using (var s = new System.IO.MemoryStream()) {   
  var ctx = (HttpContextBase)actionContext.Request.Properties["MS_HttpContext"];  
  ctx.Request.InputStream.Seek(0, System.IO.SeekOrigin.Begin);  
  ctx.Request.InputStream.CopyTo(s);   var body =
  System.Text.Encoding.UTF8.GetString(s.ToArray()); 
}
于 2013-08-27T11:26:34.813 回答
1

During the development of a REST API, we had a need to authenticate a request prior to allowing the response to be processed within the controller, and so this created a need to be able to read the header as well as the form (if any) to determine if the credentials were passed into the request within the body of the form rather than through the request header.

A few lines of code reset the stream pointer to the beginning of the stream so that MVC would be able to read the form and populate the view model in the controller

    public class WebServiceAuthenticationAttribute : AuthorizationFilterAttribute
    {
        public override void OnAuthorization(HttpActionContext actionContext)
        {
            var authenticationHeaderValue = actionContext.Request.Headers.Authorization;

            try
            {
                if (authenticationHeaderValue != null)
                {
                    var webRequestInfo = new WebRequestInfo(actionContext.Request.Method, actionContext.Request.RequestUri);
                    this.AuthenticationHeaderService.LogOnUsingAuthenticationHeader(authenticationHeaderValue, webRequestInfo);
                }
                else if (actionContext.Request.Content.IsFormData())
                {
                    Task<NameValueCollection> formVals = actionContext.Request.Content.ReadAsFormDataAsync();
                    this.AuthenticationFormService.LogOnUsingFormsAuthentication(formVals.Result);

                    // reset the underlying stream to the beginning so that others may use it in the future...
                    using (var s = new System.IO.MemoryStream())
                    {
                        var ctx = (HttpContextBase) actionContext.Request.Properties["MS_HttpContext"];
                        ctx.Request.InputStream.Seek(0, System.IO.SeekOrigin.Begin);
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
        }
    }

Initially the data model was not being created by MVC and a null was passed into the controller method. After resetting the stream, MVC was able to read the form, create and populate the data model, and pass it into the controller method.

[WebServiceAuthentication]
public HttpResponseMessage Get(DocumentRequestModel requestForm)
{
    var response = CreateResponse(HttpStatusCode.OK);
    response.Content = new ByteArrayContent(this.documentService.GetDocument(requestForm.DocumentId.ToString()));
    response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
    return response;
}
于 2017-01-25T21:11:55.097 回答
0

You really should not need to do that. At the end of the day, HttpContext Stream points to the same stream Web API reads from.

You can try to put LoadIntoBufferAsync in both places as one could trigger before the other and it was already in the buffer, calling LoadIntoBufferAsync has no side effect.

// from model binder
Request.Content.LoadIntoBufferAsync().Wait();
var formData = Request.Content.ReadAsFormDataAsync().Result;

// from filter
Request.Content.LoadIntoBufferAsync().Wait();
var formData = Request.Content.ReadAsFormDataAsync().Result;
于 2013-08-28T08:30:31.543 回答