What is wrong with this ASP.Net and Fiddler example? - asp.net-web-api

I am using Visual Studio 2012 RC. I am using the default routes and have the following Web API controller:
public class FooController : ApiController
{
// GET api/foo
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/foo/5
public string Get(int id)
{
return "value";
}
// POST api/foo
public string Post(string abc)
{
Console.WriteLine("value: {0}", abc);
return "foo" + abc;
}
// PUT api/foo/5
public void Put(int id, string value)
{
}
// DELETE api/foo/5
public void Delete(int id)
{
}
}
I wanted to do a simple test of POST in Fiddler, so I have
Request Headers
User-Agent: Fiddler
Content-Type: application/json
Request Body
{"abc": "def"}
When I debug the request, the parameter abc comes in as null, not "def". Is there something wrong with my Fiddler syntax?

(1) By default, simple types are taken from the URI. To read a simple type from the request body, add the [FromBody] attribute to the parameter.
public string Post([FromBody] string abc)
(2) '{"abc": "def"}' defines an object with a property named "abc" - to send a JSON string, the request body should just be "def"

This answer comes from a link on the ASP.Net Web API site sending-html-form-data , which turns out to be Mike's blog post (I didn't realize that at first). The Web API team has made a few decisions with parameter binding that are quite different from normal MVC controllers.
The correct syntax for sending "simple types" is
public HttpResponseMessage PostSimple([FromBody] string value)
{
// code goes here
And in Fiddler, you put
=def //THIS CANNOT HAVE QUOTES AND = IS MANDATORY
OK, so here are the parts that work very differently from MVC.
You must use [FromBody], as Mike says.
You can only have 1 parameter. If you want more than 1 parameter, you have 2 choices: i) use url query parameters, instead of request body or ii) use a complex object (i.e. your own class).
The request body should be a simple =def and cannot use named parameters.

Related

DataSourceRequest is not deserializing for a WebAPI Get method

I am trying to call a WebAPI method from Angular 5 like this:
selectClaims(state: DataSourceRequestState):Observable<DataResult>
{
return this.http.get<GridDataResult>(`${this.apiUrl}/SelectClaims?${toDataSourceRequestString(state)}`);
}
Which calls the API method as expected. The API method is:
[Route("SelectClaims")]
[HttpGet]
public IHttpActionResult SelectClaims([FromUri][DataSourceRequest]DataSourceRequest ClaimsRequest)
{
if(ClaimsRequest == null)
ClaimsRequest=new DataSourceRequest { Page=1, PageSize=20 };
var result = _db.Claims.ToDataSourceResult(ClaimsRequest, c => { c.SortHistory(); return c; });
return Ok(result);
}
The trouble is that ClaimsRequest only de-serializes Page and PageSize correctly. Filters and Sorts don't come through:
Fiddler tells me that the URL from Angular is:
GET /api/v1/Claims/SelectClaims?filter=id~eq~2&page=1&sort=firstName-asc&pageSize=20 HTTP/1.1, but in the controller both filter and sort are null.
If I create a URL through Swagger like: 'http://localhost:50223/api/v1/Claims/SelectClaims?ClaimsRequest.page=1&ClaimsRequest.pageSize=11&ClaimsRequest.sorts=firstName-desc' I do see a sort array in the API method, but the "Member" field is null.
Any attempt to add a filter through Swagger like 'http://localhost:50223/api/v1/Claims/SelectClaims?ClaimsRequest.page=1&ClaimsRequest.pageSize=11&ClaimsRequest.filters=id~eq~2' results in a "Cannot create an instance of an interface." error.
The state is a DataSourceRequestState in the angular component from a Kendo Grid for Angular.
I have simulated this in a simple test program and everything works fine there. The only difference in my test program is that the API controller targets .Net Core and the real system targets .Net 4.6.1.
Do I have to de-serialize manually in .Net 4.6.1 for some reason, or is something else going on here?
It should be a POST not a GET. Something like this:
return this.http.post<GridDataResult>(`${this.apiUrl}/SelectClaims`, toDataSourceRequestString(state)});
I needed it to be a GET (URL) so i created a new object
public class GridParamaterBinder
{
public int Page { get; set; }
public int PageSize { get; set; }
public string Filter { get; set; }
public string Sort { get; set; }
public DataSourceRequest ToDataSourceRequest(IConfigurationProvider mapper, Func<string, string> OverDefaultParamaterMapping)
{
DataSourceRequest result = new DataSourceRequest();
result.Page = Page;
result.PageSize = PageSize;
result.Sorts = GridDescriptorSerializer.Deserialize<SortDescriptor>(Sort);
result.Filters = FilterDescriptorFactory.Create(Filter);
return result;
}
}
and used it instead of the Telerik effort.
in API I Bind it like so
public virtual DataSourceResult Get([FromUri]GridParamaterBinder request)
And then used it like
DataSourceResult results = query.ToDataSourceResult(request.ToDataSourceRequest(), r => (r)));
Thanks #KevDevMan for your solution. I found this example,
then I changed my API controller like this and it worked like a charm :
[HttpGet, Route("for-kendo-grid")]
public DataSourceResult GetProducts([System.Web.Http.ModelBinding.ModelBinder(typeof(WebApiDataSourceRequestModelBinder))] DataSourceRequest request)
explanation here

Allowing parameter name to have "[" in ASP.Net Core WEB API

My action method should look like:
public IActionResult GetAll([FromQuery]string page[Number],[FromQuery]string page[Size])
{
//code
}
HTTP Request is like: "GetAll?page%5Bnumber%5D=0&page%5Bsize%5D=100".
Problem : the parameter name doesn't allow me to have a square bracket.
Besides, that I'd go for simpler names of the parameters, you can solve this by using a dictionary:
public IActionResult GetAll(Dictionary<string, string> page) {
var x = page["Number"];
var y = page["Size"];
}

Update data with Microsoft.AspNet.WebApi

Hey i am having a big trouble updating data in my client side REST application.
I made a Web API controller.
// PUT: api/Contacts/5
[ResponseType(typeof(void))]
public IHttpActionResult PutContact(Contact contact, int id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != contact.ContactId)
{
return BadRequest();
}
_contactService.Update(contact);
return StatusCode(HttpStatusCode.NoContent);
}
And also client side service method:
public async Task<T> PutData<T>(T data, int dataId)
{
HttpResponseMessage resp = await this._client.PutAsJsonAsync(_serviceUrl + "/" + dataId, data);
resp.EnsureSuccessStatusCode();
return await resp.Content.ReadAsAsync<T>();
}
Service URL shows in debug mode that i goes to endpoint:
http://localhost:21855/api/Contacts/8
But it does not even go to breakpoint when i debug my server controller PutContact method.
What i am doint wrong? I need to update the data but i cant, because my client-side application won't even go to servers breakpoint on debug mode!!!
It gives me an error response 405 : Method not allowed
You can't have two different body parameters in the same method.
What you need to do is to set the id parameter to come from the URI and the Contact parameter from the body, like this:
public IHttpActionResult PutContact([FromBody]Contact contact, [FromUri]int id)
{
// method code
}
BTW, I suppose you have a GET method in your controller which looks like this:
public IHttpActionResult GetContact(int id)
{
// method code
return Contact; // pseudo-code
}
The error you getting comes from the fact that the system is not really calling your PUT method but the GET one (the system is ignoring the Contact parameter for the reason I expressed before): calling a GET method with a PUT verb results in a 405 Method Not Allowed exception.

How to pass multiple parameters via query string into a webapi controller

I am working on a legacy system that has been using .Net remoting for its communication.Now I want to write new client and server side web-api code to do the same thing.
Here's a sample code that I am dealing with :
public SearchResult Search(Condition condition,Sort sort,PageInfo pageInfo)
{
......
}
I would like to be able to have a web-api action with the same signature that gets its value form Uri , thus :
[HttpGet()]
public SearchResult Search([FromUri]Condition condition,[FromUri]Sort sort,[FromUri]PageInfo pageInfo)
{
......
}
Here are my questions :
Is it possible to have such an action in a web-api ?
If yes, How can I pass these parameters using HttpClient ?
You can setup your Route attribute to accept as many parameters as you like.
[Route("/api/search/{condition}/{sort}/{pageInfo}")]
[HttpGet]
public HttpResponseMessage Search( string condition, string sort, string pageInfo) {
...
}
This would mean that your url changes from
/Search?condition=test&sort=first&pageInfo=5
to
/Search/test/first/5
Alternatively, bundle the Condition, Sort and PageInfo objects into single Json class, and pass this object to your route:
[Route("/api/search/{SortParams}")]
[HttpGet]
public HttpResponseMessage Search( object sortParams) {
// deserialize sortParams into C# object structure
}
Have a look at this question : C# .net how to deserialize complex object of JSON

How To Pass formdata parameters into ASP.NET WebAPI without creating a record structure

I have data coming into my form that looks like the image below (sessionsId: 1367,1368).
I've create c# in my webapi controller that works as below. when I've tried ot just make use SessionIds as the parameter (or sessionIds) by saying something like PostChargeForSessions(string SessionIds) either null gets passed in or I get a 404.
What is the proper way to catch a form parameter like in my request without declaring a structure.
(the code below works, but I'm not happy with it)
public class ChargeForSessionRec
{
public string SessionIds { get; set; }
}
[HttpPost]
[ActionName("ChargeForSessions")]
public HttpResponseMessage PostChargeForSessions(ChargeForSessionRec rec)
{
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK, new ShirtSizeReturn()
{
Success = true,
//Data = shirtSizeRecs
});
return response;
}
You can declare the action method like this.
public HttpResponseMessage Post(string[] sessionIds) { }
If you don't want to define a class, the above code is the way to go. Having said that, the above code will not work with the request body you have. It must be like this.
=1381&=1380

Resources