Simple.Odata: how to call a function passing parameters in asp.net mvc request body - asp.net-mvc

The title is a bit misleading, here is my situation: with postman, i can call the following url issuing a post and my function works:
http://localhost/odataservice/odata/Evaluations(9)/CreateEmptyForm
For it to work, i have to send the following in the body portion:
{
"#odata.type": "#Common.Data.Client",
"ClientId": 1
}
My issue arises when i try to replicate this inside my asp.net mvc application. There, im doing
public int CreateRvaForm(int clientId, int evalId, int type)
{
var key = Task.Run(async () =>
{
var data = await
client
.For<Evaluation>(Constants.DataService.PLURAL_EVALUATIONS)
.Key(evalId)
.Function( type==0 ? Constants.DataService.FUNCTION_CREATE_RVA_EMPTY : Constants.DataService.FUNCTION_CREATE_RVA_DUPLICATE)
.Set( new{ClientId=clientId} )
.ExecuteAsScalarAsync<int>();
return data;
}).GetAwaiter().GetResult();
return key;
}
Here, the variable client is the Simple.Odata.Client object.
The error i'm getting is something related to route not found; debug shows me that the library is trying to execute the url
http://localhost/odataservice/odata/Evaluations(9)/CreateEmptyForm(clientId=XX).
I dont have access to modify the odata service.

According to this text ("Executing functions and actions"), you should rather try using Action instead of Function since action is POST-based call while function translates to HTTP GET

Related

Breeze EntityQuery.from() withParameters POST to API not working

I have this peace of code :
function getSopMatches(data) {
return EntityQuery.from('GetSopMatches')
.withParameters({
$method: 'POST',
$encoding: 'JSON',
$data: data
})
.using(manager).execute()
.then(success)
.catch(_queryFailed);
function success(response) {
return response.results;
}
}
and it is called through this :
playerService.getSopMatches({ playerId: vm.player.id, competitionId: career.competitionId, seasonId: career.seasonId, teamId: career.teamId }).then(function (results) { //do something with it });
In my MVC controller (BreezeController) the method is like :
[HttpPost]
public IQueryable<object> GetSopMatches(SopMatch sop)
{
//this method is not called, I get a 405 Method not Allowed
}
Somehow, the actual call is a GET, instead of a POST and therefore I get the 405 Method not Allowed error message.
I have other peaces of code in the same project (client side javascript/breeze calls and server side mvc controller methods) that do work.
Does anybody knows what I'm doing wrong, or why it is changed to a GET method ?
After a day of trying and failing, I found a solution.
It appeared that in the angular controller I was injecting breeze, and that was causing the issue. When breeze is injected into the controller or service, it messes up the URL of the POST and therefore you receive a 405 (because the URL is changed).

URL invalid error whenever calling from twilio

I want to make an application which can make a call to other peoples using Twilio in vb.net. whenever i am passing the URL parameter it is showing me invalid URL error. I have made a service which is returning Twimil response. And i am passing this the service URL as a parameter to Twilio URL to Initiate a call.
Not Sure is it the correct way to make call from vb.net
Twilio callvar to = new PhoneNumber("+917715*****");
var from = new PhoneNumber("+1*********");
var call = CallResource.Create(to, from,
url: new Uri("demo.twilio.com/docs/voice.xml"));
service public class Service1 : IService1
{
public void GetDataJSONFormat()
{
var response = new VoiceResponse(); response.Say("Hello Shrikant");
}
}

ASP.NET WebAPI unable to post

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.

custom OData v4 function always returns 406

So I'm trying to map this method as an Odata function ...
[HttpGet]
[EnableQuery]
public IHttpActionResult ForBuyerOrganisations([FromUri]int[] orgIds)
{
// this returns IQueryable<TRDetails>
// i tried throwing a ToList on there too to ensure it works and it does
var result = service.ForBuyerOrgs(orgIds);
return Ok(result);
}
I have mapped it like this ...
var forBuyers = Builder.EntityType<TRDetails>().Collection.Function("ForBuyerOrganisations");
forBuyers.ReturnsCollection<TRDetails>();
forBuyers.CollectionParameter<int>("orgIds");
... I also tried this ...
var forBuyers = Builder.EntityType<TRDetails>().Collection.Function("ForBuyerOrganisations");
forBuyers.ReturnsCollectionFromEntitySet<TRDetails>();
forBuyers.CollectionParameter<int>("orgIds");
I can call it with the url:
~/TRDetails/ForBuyerOrganisations?orgIds=1
The Problem:
The request executes my code and returns from this method, then the client gets a 406.
Any Ideas?
Yes. #Ouyang indicates the config error. Besides, as I seem, there are other four errors:
you should use [FromODataUri], not [FromUri] for the parameter.
you should call the function with namespace-qualified.
TRDetails/Namespace.ForBuyerOrganisations(...)
you should set the argument as list, because you config it as collection.
you should call the function using the () for parameter.
So, the following request is sample request:
~/TRDetails/Default.ForBuyerOrganisations(orgIds=[5,4,2,3,1])
If TRDetails is an entityset,
forBuyers.ReturnsCollectionFromEntitySet<TRDetails>("TRDetails");
I think you miss the entityset's name.

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 ;
}

Resources