Model Binder Not Finding Post Data - asp.net-web-api

I have a simple jquery post
function saveImage(base64) {
$.post("http://localhost:50575/api/images", {base64Data: base64});
}
That is going against a .net core controller
[HttpPost]
public async Task<ActionResult> Post(string base64Data)
{
var base64 = Request.Form["base64Data"];
return Ok();
}
When the data is posted, base64Data is null. However, base64, which is populated from the form variables has a value.
Is there any reason why this shouldn't work?

What sent by $.post("http://localhost:50575/api/images", {base64Data: base64}) is:
POST /api/images HTTP/1.1
Content-Type: application/x-www-form-urlencoded
base64Data=xxxxxxxx
Since you send the request with a content-type of application/x-www-form-urlencoded and have the request processed by a ApiController , you should decorate the parameter with a [FromForm]
public async Task<ActionResult> Post([FromForm] string base64Data)
{
// ...
}
Or if you would like to send the request encoded with application/json , you should firstly create a DTO to hold the whole playload :
public class Base64Dto{
public string Base64Data{get;set;}
}
and decorate the parameter with a [FromBody] at the same time :
public async Task<ActionResult> Post([FromBody] Base64Dto base64Data)
{
// var base64 = Request.Form["base64Data"];
return Ok();
}
Another way to hold the whole payload with Base64Dto , is to send the request with a header of Content-Type: application/x-www-form-urlencoded , and use a [FromForm] attribute at the same time :
public async Task<ActionResult> Post([FromForm] Base64Dto base64Data)
{
// var base64 = Request.Form["base64Data"];
return Ok();
}

Related

How to return xml or json with ASP.NET web mvc 6 depending on Accept Header

I have implemented a web api controller using ASP.NET mvc 6 and I would like to return the result of the controller as json or xml, depending on the client's Accept header. For example, if the client sends a GET request with "Accept: application/xml", then the returned response should be xml. If the header is "Accept: application/json", then it should be json. At the moment the controller always returns json. Is there a way of configuring this? Note: this question is indeed a duplicate of How to return XML from an ASP.NET 5 MVC 6 controller action. However the solution provided there did not solve my problem. The accepted answer below worked for me.
The controller is given below and is the one provided by the ASP.NET 5 web api template:
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET: api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id:int}")]
public string Get(int id)
{
return "value";
}
// POST api/values
[HttpPost]
public void Post([FromBody]string value)
{
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
Thanks for your help!
I did the research for you, you may continue alone:
needed:
"Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
"Microsoft.AspNet.Mvc.Core": "6.0.0-rc1-final",
"Microsoft.AspNet.Mvc.Formatters.Xml": "6.0.0-rc1-final"
startup:
services.Configure<MvcOptions>(options =>
{
options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
});
go on from here
another
and antoher

Custom AuthorizeAttribute on WebApi OData EntitySetController

I created custom authorize attribute to handle my custom permissions on WebAPI odata controller inherited from EntitySetController, here is the code for my attribute
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public class RequirePermissionsAttribute : System.Web.Http.AuthorizeAttribute
{
public Permissions[] Permissions { get; set; }
public RequirePermissionsAttribute()
{ }
public RequirePermissionsAttribute(params Permissions[] permissions)
{
this.Permissions = permissions;
}
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
// Custom authorization logic
}
Now I try to add this attribute on Get() method, it get invoked
public class ItemsController : EntitySetController<Item, Guid>
{
[EnableQuery(MaxExpansionDepth = 5)]
[RequirePermissionsAttribute(Permissions.ViewAll)]
public override IQueryable<Item> Get()
{
//Code go here
}
}
But when I add the same attribute on CreateEntity() it never get invoked
[RequirePermissionsAttribute(Permissions.Add)]
protected override Item CreateEntity(Item item)
{
// Create item
}
Any help appreciated
You should use your attribute as RequirePermissions without the tailed "Attribute" word, So change your code to be like this
[RequirePermissions(Permissions.Add)]
protected override Item CreateEntity(Item item)
{
// Create item
}
Islam
From the WebAPI source codes, the internal virtual function CreateEntity() is called in POST request. Here's the source codes in EntitySetController:
public virtual HttpResponseMessage Post([FromBody] TEntity entity)
{
TEntity createdEntity = CreateEntity(entity);
TKey entityKey = GetKey(entity);
return EntitySetControllerHelpers.PostResponse(this, createdEntity, entityKey);
}
I use your sample codes and send a POST request, the CreateEntity() can be invoked as:
POST ~/odata/Items
Content-type: application/json
{"Id":"9daf653f-212c-42e3-80a4-4778e445c092"}
However, if you want to get the correct response, you should override GetKey() because GetKey() is called after CreateEntity() in the Post() method. The same information is also mentioned in the remarks of CreateEntity() as below:
Sample Test
I create the following two functions in ItemsController:
protected override Guid GetKey(Item entity)
{
return entity.Id;
}
[RequirePermissionsAttribute(Permissions.Add)]
protected override Item CreateEntity(Item item)
{
// Create item
return item;
}
And send the same POST request mentioned above, I can get the following response:
HTTP/1.1 201 Created
Cache-Control: no-cache
.....
Content-Length: 124
{
"odata.metadata":"http://localhost:47794/odata/$metadata#Items/#Element","Id":"9daf653f-212c-42e3-80a4-4778e445c092"
}
Hope it can help you. Thanks.

Getting No Response When Calling WebAPI Post with application/json header

I've got a very simple WebAPI controller (all system defaults) and when I do a post to it, the fact that it has Content-Type: application/json makes the post (from fiddler) hang (not return).
My headers are as follows:
Content-Length: 2
Content-Type: application/json
and the post body is simply
[]
My WebAPI controller just looks like this:
namespace WebAPI.rest
{
public class SendGridController : ApiController
{
public HttpResponseMessage Post()
{
try
{
HttpContent requestContent = Request.Content;
string json = requestContent.ReadAsStringAsync().Result.Trim();
}
catch (Exception ex)
{
throw ex;
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
When I make the same post (with fiddler) to http://respondto.it/ it returns no problem
If you are running under ASP.NET the the .Result is probably not a wise idea. I ran your code under self-host and it worked fine.
Try this,
public class SendGridController : ApiController
{
public async Task<HttpResponseMessage> Post()
{
try
{
HttpContent requestContent = Request.Content;
string json = await requestContent.ReadAsStringAsync();
}
catch (Exception ex)
{
throw ex;
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
The problem turned out to be an old version of webapi. Once I updated to webapi2, the problem went away.

In a WebAPI Formatter, how can you get the URL from HttpContent?

In a WebAPI service, we are using a Formatter to read a content parameter on a request. We need access to the URL in order to transform the content parameter correctly. HttpRequestMessage isn't available, and we can't use HttpContext.Current.Request because HttpContext.Current is null. Accessing the HttpRequestMessage on a Read was requested at http://aspnetwebstack.codeplex.com/workitem/82, but this issue was closed because HttpContent is available on a Read. However, I don't know how to get the URL from HttpContent, or even if it's possible.
There is a method called GetPerRequestFormatterInstance on the formatter which you can override to create a new instance of the formatter with the stateful information about the request in it. By the way, this method GetPerRequestFormatterInstance is only called during the request's deserialization stage. Example below:
public class TextPlainFormatter : BufferedMediaTypeFormatter
{
public TextPlainFormatter()
{
this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain"));
}
public HttpRequestMessage CurrentRequest
{
get;
private set;
}
public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, HttpRequestMessage request, MediaTypeHeaderValue mediaType)
{
TextPlainFormatter frmtr = new TextPlainFormatter();
frmtr.CurrentRequest = request;
//Copy from the original formatter instance to the new instance
frmtr.MediaTypeMappings.Clear();
foreach (MediaTypeMapping mediaTypeMapping in this.MediaTypeMappings)
{
frmtr.MediaTypeMappings.Add(mediaTypeMapping);
}
frmtr.RequiredMemberSelector = this.RequiredMemberSelector;
frmtr.SupportedEncodings.Clear();
foreach (Encoding supportedEncoding in this.SupportedEncodings)
{
frmtr.SupportedEncodings.Add(supportedEncoding);
}
frmtr.SupportedMediaTypes.Clear();
foreach (MediaTypeHeaderValue supportedMediaType in this.SupportedMediaTypes)
{
frmtr.SupportedMediaTypes.Add(supportedMediaType);
}
return frmtr;
}

Why am I getting a 404 response from my POST in web api?

I have the following action in my Web api controller:
// POST api/<controller>
[AllowAnonymous]
[HttpPost]
public bool Post(string user, string password)
{
return true;
}
I am getting the following error with a 404 status when hitting it with either fiddler or a test jQuery script:
{"Message":"No HTTP resource was found that matches the request URI 'http://localhost/amsi-v8.0.0/api/account'.","MessageDetail":"No action was found on the controller 'Account' that matches the request."}
My http route is as follows:
RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Gets work fine. I found another question here which talks about removing WebDAV from IIS. I tried that, still same issue.
Why do I get a 404?
The default action selection behavior in ASP.NET Web API cares about your action method parameters as well. If they are simple type objects and they are not optional, you will need to supply them in order to invoke that particular action method. In your case, you should send a request against a URI as below:
/api/account?user=Foo&password=bar
If you wanna get these values inside the request body rather than the query string (which is a better idea), just create a User object and send the request accordingly:
public class User {
public string Name {get;set;}
public string Password {get;set;}
}
Request:
POST http://localhost:8181/api/account HTTP/1.1
Content-Type: application/json
Host: localhost:8181
Content-Length: 33
{"Name": "foo", "Password":"bar"}
And your action method should look like something below:
public HttpResponseMessage Post(User user) {
//do what u need to do here
//return back the proper response.
//e.g: If you have created something, return back 201
return new HttpResponseMessage(HttpStatusCode.Created);
}
When we are posting a json it expect a class so create class in model folder like this
public class Credential
{
public string username { get; set; }
public string password { get;set; }
}
and now change the parameter
[HttpPost]
public bool Post(Credential credential)
{
return true;
}
Try now everything will work smooth

Resources