How do I retrieve body values from an HTTP POST request in an ASP.NET Web API ValueProvider?
Asked Answered
V

3

20

I want to send a HTTP POST request with the body containing information that makes up a simple blog post, nothing fancy.

I've read here that when you want to bind a complex type (i.e. a type that is not string, int etc) in Web API, a good approach is to create a custom model binder.

I have a custom model binder (BlogPostModelBinder) that in turn uses a custom Value Provider (BlogPostValueProvider). What I don't understand is that how and where shall I be able to retrieve the data from the request body in the BlogPostValueProvider?

Inside the model binder this is what I thought would be the right way to for example retrieve the title.

public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
   ...
   var title= bindingContext.ValueProvider.GetValue("Title");
   ...
}

while the BlogPostValueProvider looks like this:

 public class BlogPostValueProvider : IValueProvider
 {
    public BlogPostValueProvider(HttpActionContext actionContext)
    {
       // I can find request header information in the actionContext, but not the body.
    }

    public ValueProviderResult GetValue(string key)
    {
       // In some way return the value from the body with the given key.
    }
 }

This might be solvable in an easier way, but since i'm exploring Web API it would be nice to get it to work.

My problem is simply that I cannot find where the request body is stored.

Thanks for any guidance!

Vindication answered 5/4, 2014 at 17:56 Comment(5)
What is your request content type...i am assuming formurlencoded?...could you give more details as to why you need a custom model binder...Kozhikode
If your are posting a json of BlogPostVM then you just need an action that accepts BlogPostVM, no need for custom binders.Brigitta
I think you're both correct. The ContentType in the request is set to json @KiranChalla. Having an action with a parameter of type BlogPost might do it. I would still like to know why I cannot access the body of the request from within the value provider.Vindication
@KiranChalla I dont think I neccessarily need a custom model binder. I'm new to Web API so it was more like in an exploratory way.Vindication
Model binding or deserialization of request/response in Web API is driven by Content-Type header and Media type formatters. When a request's content-type is formurlencoded, FormUrlEncodedMediaTypeFormatter is handled the request body to do whatever it wants. Its in this process that the IModelBinder & IValueProvider come into picture whereas in case of json, it is handled by JsonMediaTypeFormatter which in turn depends on let's say JSON.Net's deserializer. This deserialzer does not have the concept of modelbinders or value providers.Kozhikode
L
23

Here's a blog post about this from Rick Strahl. His post almost answers your question. To adapt his code to your needs, you would do the following.

Within your value provider's constructor, read the request body like this.

Task<string> content = actionContext.Request.Content.ReadAsStringAsync();
string body = content.Result;
Ledford answered 29/8, 2014 at 20:53 Comment(1)
Never use .Result, always use await or you'll get deadlocks/starvationWindhover
C
2

I needed to do this in an ActionFilterAttribute for logging, and the solution I found was to use the ActionArguments in the actionContext as in

public class ExternalApiTraceAttribute : ActionFilterAttribute
{


    public override void OnActionExecuting(HttpActionContext actionContext)
    {
       ...

        var externalApiAudit = new ExternalApiAudit()
        {

            Method = actionContext.Request.Method.ToString(),
            RequestPath = actionContext.Request.RequestUri.AbsolutePath,
            IpAddresss = ((HttpContextWrapper)actionContext.Request.Properties["MS_HttpContext"]).Request.UserHostAddress,
            DateOccurred = DateTime.UtcNow,
            Arguments = Serialize(actionContext.ActionArguments)
        };
Cuisine answered 4/11, 2016 at 7:10 Comment(0)
P
1

To build on Alex's answer, after you get the Raw body, you can then use Newtonsoft to deserealize it (without doing any of that complicated stuff in the blog post):

 Task<string> content = actionContext.Request.Content.ReadAsStringAsync();
 string body = content.Result;
 var deserializedObject = JsonConvert.DeserializeObject<YourClassHere>(body.ToString());

https://www.newtonsoft.com/json/help/html/deserializeobject.htm

Predator answered 3/6, 2022 at 1:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.