ASP.NET WebAPI unable to post - f#

I am unable to post data to a ASP.NET WebAPI server.
I can get data from the WEBAPI server. However, I am unable to post.
The following code fails to post:
response = await client.PostAsync("api/Cars", content);
Error:
StatusCode: 415, ReasonPhrase: 'Unsupported Media Type', Version: 1.1
Client:
[TestClass]
public class UnitTest1
{
// http://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client
[TestMethod]
public async Task TestMethod1()
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:48213/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// HTTP GET
var response = await client.GetAsync("api/Cars");
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsStringAsync();
}
// HTTP POST
using (var content = new StringContent(#"some_value"))
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
response = await client.PostAsync("api/Cars", content);
if (response.IsSuccessStatusCode)
{
Debug.WriteLine("Post was successful.");
}
}
}
WebAPI:
namespace FSharpWebAPI.Controllers
open System.Web.Http
open FSharpWebAPI.Models
type CarsController() =
inherit ApiController()
let values = [| { Make = "Ford"; Model = "Mustang" }; { Make = "Nissan"; Model = "Titan" } |]
member x.Get() = values
member x.Post(data) =
ignore
What updates do I need to make just to make a simple post?

As #Fyodor Soikin has pointed out, the Post method is generic ('a -> 'b -> unit), and ASP.NET Web API refuses to wire up such methods.
ASP.NET Web API uses convention over configuration in order to figure out how to route and handle incoming requests. If an HTTP POST arrives, it'll go hunting for a method (partially) named Post, and attempt to call it. While you could argue that if a generic method could handle the input, it'd be possible for Web API to still call it, it doesn't do that.
You'll need to add a type annotation - for example:
member x.Post(data : Car) =
ignore
That's probably still not going to be enough, because the type of that version of Post is Car -> 'a -> unit, so the return value is a generic function. I'd be surprised if Web API knows what to do with that.
The reason is that ignore is a function, and since Post doesn't invoke the function, the return value is the function itself.
If you want to ignore the input and return unit, you can simply do this:
member x.Post(data : Car) = ()
This version has the type Car -> unit, which I expect Web API will find acceptable.

Related

Async tast not working as it should

I am trying to do webservice async so ui can respond and websevice call is done in background, but since i am little inexpirienced with async stuff i need little help.
Here is my code:
Inside my action result i have call to method which have asyinc stuff in it
public ActionResult SavePackage(string ParcelNumber)
{
/////some other stuff
SaveAsync(ParcelNumber);
}
And
then i have async method :
public async Task SaveAsync(string ParcelNumber)
{
await api.RegisterPackage(ParcelNumber);
}
Which calls api:
public async Task RegisterPackage(string ParcelNumber)
{
var rk = "some api http";
HttpWebRequest request = WebRequest.Create(rk) as HttpWebRequest;
request.Headers.Add("cache-control", "no-cache");
request.Method = "POST";
request.ContentType = "application/json";
string data = "{\n \"ParcelNumber\": \"" + ParcelNumber+ "\"}";
byte[] dataStream = Encoding.UTF8.GetBytes(data);
Stream newStream = request.GetRequestStream();
// Send the data.
newStream.Write(dataStream, 0, dataStream.Length);
newStream.Close();
Task<WebResponse> getResponseTask = Task<WebResponse>.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, null);
await getResponseTask.ContinueWith(getResponseAntecedent =>
{
WebResponse webResponse = getResponseAntecedent.Result;
using (var reader = new StreamReader(webResponse.GetResponseStream()))
{
string error = reader.ReadToEnd();
//TODO: use JSON.net to parse this string and look at the error message
var myDeserializedObjList3 = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObjectAtt>(error);
var isValid = myDeserializedObjList3.IsValid;
var ModelErrors = myDeserializedObjList3.ModelErrors;
var ValidationErrors = myDeserializedObjList3.ValidationErrors;
}
});
}
My problem is that the above code is still waiting for response to finish (and that can take about 20 second), and i would like if possible when i call the api i could go back to my ui and let ppl do their stuff while api is proccessed in background.
Anybody have any suggestion?
As I describe on my blog, async does not change the HTTP protocol.
First, the best solution to your problem is to not change your ASP.NET action method at all. Instead, have the actual UI application issue the call asynchronously. If your UI app is a .NET app, then it can use async/await; if it's a web app (i.e., JavaScript), then it can use an AJAX call. Another good option is to introduce SignalR, which allows the server to signal when the work is done.
If you really want to run ASP.NET code outside of a request (which is never recommended), then you can use one of the techniques I describe on my blog for ASP.NET fire-and-forget.

WebApi Client. PostAsJson

I have some strange problem. I am using Microsoft.AspNet.WebApi.Client.5.2.3 for simple .NET client for API. I want to post some data to API. I am using PostAsJsonAsync method for it.
using (var client = new HttpClient())
{
var adress = new Uri("http://localhost:28906/v1/things?access_token=SOMETOKEN");
var result = await client.PostAsJsonAsync(adress, new ThingModel() { Name = "test"});
}
When I am sending request, my uri is transformed from "http://localhost:28906/v1/things?access_token=SOMETOKEN" to "http://localhost:28906/v1/things/?access_token=SOMETOKEN" ( '/' is inserted before '?'). And request becomes wrong. How can I overcome this? In fact, how can I pass query string and json body?

Simple.OData.Client - Unable to invoke Action that accepts entity collection parameter

I get error "The parameter 'wheels' is of Edm type kind 'Collection'.
You cannot call CreateCollectionWriter on a parameter that is not of
Edm type kind 'Collection'."
Below are details of my setup:
Web API 2.2 OData v4 service : I have defined Action in WheelsController class in my service like this:
public async Task<IHttpActionResult> UpdateWheels(ODataActionParameters parameters)
{
object value;
parameters.TryGetValue("carId", out value);
int carId= (int)value;
parameters.TryGetValue("wheels", out value)
IEnumerable<Wheel> wheels = (IEnumerable<Wheel>)value;
// logic goes here....
return OK();
}
In WebApiConfig.cs files, the Action configuration is defined as below:
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Car>("Cars");
builder.EntitySet<Wheel>("Wheels");
var action = builder.EntityType<Wheel>().Collection.Action("UpdateWheels");
action.Parameter<int>("carId");
action.CollectionParameter<Wheel>("wheels");
I get success in invoking the above action from RESTClient extenstion in FireFox browser as POST request to URL "http://localhost/Service/Wheels/UpdateWheels" with Request Body as
{"carId":2,
"wheels":[{"Id":1,"Name":"Wheel Front 1","Description":"Front wheel left", "PositionEnum":"FrontLeft"},
{"Id":2,"Name":"Wheel Front 2","Description":"Front wheel right", "PositionEnum":"FrontRight"}]
}
However, it gives error when I try to invoke the above service action using Simple.OData.Client in client application such as
public async void TestUpdateWheels(List<Wheel> wheelList)
{
// client is derived from ODataClient from assembly Simple.OData.Client.Core.dll, v4.3.0.0
await client.For<Wheel>()
.Action("UpdateWheels")
.Set(new { carId = 2, wheels = wheelList})
.ExecuteAsync();
}
Error message: The parameter 'wheels' is of Edm type kind
'Collection'. You cannot call CreateCollectionWriter on a parameter
that is not of Edm type kind 'Collection'.
How can I call successfully the above Action from ODataClient?
This turn out to be a bug in Simple.OData.Client version 4.3.0 when I reported to the project site. For details, visit the link https://github.com/object/Simple.OData.Client/issues/117
The new bug fix version 4.7.2 of Simple.OData.Client has fixed the
issue for me!
Try out in this way. It works for me in one of my project.
public async Task<string> TestUpdateWheels(List<Wheel> wheelList)
{
string getRules = await client.ExecuteActionAsScalarAsync<string>
("UpdateWheels", new Dictionary<string, object>
{
{ "YourParamater", wheelList}
});
return getRules ;
}

Web API 2.2 - OData v4 (Manually Parsing Uri + Expanding)

I have an ODataController with a Get method as such:
public IHttpActionResult Get(ODataQueryOptions<MyModel> queryOptions) {
IQueryable<MyModel> models = _Models.AsQueryable(); // _Models Defined in Controller as List<MyModel> and is already populated with nested data for both .LevelOne and .LevelOne.LevelTwo which are two other Lists.
Uri fullrequest = Request.GetRequestContext().Url.Request.RequestUri; // http://localhost:8080/odata/Root?$expand=LevelOne($expand=LevelTwo)
Uri serviceroot = new Uri(controller.GetLeftPart(UriPartial.Path).Replace("/Root", "")); // http://localhost:8080/odata
String metadata = service + "/$metadata"; // http://localhost:8080/odata/$metadata
IEdmModel model = EdmxReader.Parse(XmlTextReader.Create(metadata));
ODataUriParser parser = new ODataUriParser(model, serviceroot, fullrequest);
SelectExpandClause selectAndExpand = parser.ParseSelectAndExpand();
//Only one of the two below lines is ever commented in...
Request.ODataProperties().SelectExpandClause = queryOptions.SelectExpand.SelectExpandClause; // This line will work
Request.ODataProperties().SelectExpandClause = selectAndExpand; // This line will not work
return Ok(models);
}
using my manually parsed selectAndExpand does not expand the dataset, but using the predefined queryOptions one does. Any ideas why? Both objects appear to contain the same information while viewed in the debugger, but I must be missing something. I want to be able to parse the URI myself, without the need for the ODataQueryOptions at all.
What I ended up doing, was building a new ODataQueryOptions object based off the original request, and then pulling just the SelectExpandClause from that. It doesn't answer my initial question, but it is a somewhat working solution for not having to pass in a ODataQueryOptions parameter. See my Code below:
public IHttpActionResult Get() {
//Get Queryable Item (in this case just a list made queryable)
IQueryable<MyModel> models = _Models.AsQueryable();
//Create new ODataQueryContext based off initial request (required to create ODataQueryOptions)
ODataQueryContext selectAndExpandContext = new ODataQueryContext(Request.ODataProperties().Model, typeof(MyModel), Request.ODataProperties().Path);
//Create new ODataQueryOptions based off new context and original request
ODataQueryOptions<Employee> selectAndExpandOptions = new ODataQueryOptions<Employee>(selectAndExpandContext, Request);
//Attach Select + Expand options to be processed
if (selectAndExpandOptions.SelectExpand != null) {
Request.ODataProperties().SelectExpandClause = selectAndExpandOptions.SelectExpand.SelectExpandClause;
}
return Ok(models);
}

Posting with multiple parameters to webapi using RestSharp

I just started playing around with RestSharp and WebApi and I've run into a little bit of an issue.
I'm not sure if this is best practice or even possible, but I'll demonstrate through code (this isn't my exact code but it's the exact same concept)
[HttpPost]
public HttpResponseMessage CreateEmployee(Employee emp, int employeeType)
{
// CREATE EMPLOYEE
return Request.CreateResponse(HttpStatusCode.Created, emp.id);
}
I've created a console app to test this using RestSharp. Here's what I have:
var client = new RestClient();
client.BaseUrl = #"http://localhost:15507";
var employee = new Employee();
//populate employee model
postrequest.Method = Method.POST;
postrequest.Resource = "api/Employee/CreateEmployee";
postrequest.AddHeader("Accept", "application/json");
postrequest.AddHeader("Content-Type", "application/json");
postrequest.RequestFormat = DataFormat.Json;
postrequest.AddBody(new { emp = employee, listId = 2 });
var res = client.Execute(postrequest);
The error that I get is that employeeType parameter comes in as null. Am I formatting this properly? Is this something that's even possible to do?
When i remove the employeeType parameter from the WebApi action method and modify the request to:
postrequest.AddBody(employee);
everything works fine.
any ideas? Thanks
if you are expecting employeetype from uri and if its not part of defined route, you can send it as query string parameter...Ex:
api/Employee/CreateEmployee?employeeType=

Resources