5

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; 
1
  • Would help to create a repro. Commented Aug 27, 2013 at 8:25

3 Answers 3

3

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()); } 
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for answering Alex. I'm reading from model binder and filter.
Maybe you could read directly from the HttpContext - I don't think the would work in a self-hosted scenario. I'll add some code to my answer.
I added some code above, maybe you use it in your filter and then read the content in your model binder? If the above does not work, I think you can also do direct seeks against the underlying buffer.
It's not self-hosted. Will try and if works mark it as answer.
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; } 

Comments

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; 

2 Comments

I'll try this when I reach office today.
@Mark there could be something else reading it. Do you think you please create a simple repro?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.