POST JSON from Angular frontend to ASP.NET backend - c#

I would like to send data (originally arrays) as JSONs to my MVC-controller in the backend. I was trying this:
my-ng-service.ts
//...
setEmployees(employees) {
var employeesJSON = JSON.stringify(employees);
console.log(employeesJSON); //working
this.http.post('/api/employees', employeesJSON).subscribe();
}
//...
EmployeesController.cs
//...
[Route("api/[controller]")]
[HttpPost]
public void Post(JsonResult data)
{
Console.Write("hallo: " + data); //error see below
}
//...
I don't know how I have to write my controller. Can anybody help me?
Current error:
System.Argument.Exception: Type 'Microsoft-AspNetCore.Mvc:JsonResult'
does not have a default constructor Parameter name: type
Thanks in forward!

JsonResult is a type you use to output JSON from an action method, not as input parameter.
See ASP.NET Core MVC : How to get raw JSON bound to a string without a type? and ASP.NET MVC Read Raw JSON Post Data: you can change the parameter type to dynamic or JObject, or you can manually read the request body as string.
But you should really reconsider this. Creating a model so you can bind your model strongly-typed is a matter of seconds work, and will benefit you greatly in the future.

Actually write the model name that you want to get from http request
[Route("api/[controller]")]
[HttpPost]
public void Post(YourJsonModel ModelObject)
{
Console.Write("hallo: " + data); //error see below
}

Related

Passing data between two Method in Controller [duplicate]

I'm using ASP.NET MVC 4. I am trying to pass data from one controller to another controller. I'm not getting this right. I'm not sure if this is possible?
Here is my source action method where I want to pass the data from:
public class ServerController : Controller
{
[HttpPost]
public ActionResult ApplicationPoolsUpdate(ServiceViewModel viewModel)
{
XDocument updatedResultsDocument = myService.UpdateApplicationPools();
// Redirect to ApplicationPool controller and pass
// updatedResultsDocument to be used in UpdateConfirmation action method
}
}
I need to pass it to this action method in this controller:
public class ApplicationPoolController : Controller
{
public ActionResult UpdateConfirmation(XDocument xDocument)
{
// Will add implementation code
return View();
}
}
I have tried the following in the ApplicationPoolsUpdate action method but it doesn't work:
return RedirectToAction("UpdateConfirmation", "ApplicationPool", new { xDocument = updatedResultsDocument });
return RedirectToAction("UpdateConfirmation", new { controller = "ApplicationPool", xDocument = updatedResultsDocument });
How would I achieve this?
HTTP and redirects
Let's first recap how ASP.NET MVC works:
When an HTTP request comes in, it is matched against a set of routes. If a route matches the request, the controller action corresponding to the route will be invoked.
Before invoking the action method, ASP.NET MVC performs model binding. Model binding is the process of mapping the content of the HTTP request, which is basically just text, to the strongly typed arguments of your action method
Let's also remind ourselves what a redirect is:
An HTTP redirect is a response that the webserver can send to the client, telling the client to look for the requested content under a different URL. The new URL is contained in a Location header that the webserver returns to the client. In ASP.NET MVC, you do an HTTP redirect by returning a RedirectResult from an action.
Passing data
If you were just passing simple values like strings and/or integers, you could pass them as query parameters in the URL in the Location header. This is what would happen if you used something like
return RedirectToAction("ActionName", "Controller", new { arg = updatedResultsDocument });
as others have suggested
The reason that this will not work is that the XDocument is a potentially very complex object. There is no straightforward way for the ASP.NET MVC framework to serialize the document into something that will fit in a URL and then model bind from the URL value back to your XDocument action parameter.
In general, passing the document to the client in order for the client to pass it back to the server on the next request, is a very brittle procedure: it would require all sorts of serialisation and deserialisation and all sorts of things could go wrong. If the document is large, it might also be a substantial waste of bandwidth and might severely impact the performance of your application.
Instead, what you want to do is keep the document around on the server and pass an identifier back to the client. The client then passes the identifier along with the next request and the server retrieves the document using this identifier.
Storing data for retrieval on the next request
So, the question now becomes, where does the server store the document in the meantime? Well, that is for you to decide and the best choice will depend upon your particular scenario. If this document needs to be available in the long run, you may want to store it on disk or in a database. If it contains only transient information, keeping it in the webserver's memory, in the ASP.NET cache or the Session (or TempData, which is more or less the same as the Session in the end) may be the right solution. Either way, you store the document under a key that will allow you to retrieve the document later:
int documentId = _myDocumentRepository.Save(updatedResultsDocument);
and then you return that key to the client:
return RedirectToAction("UpdateConfirmation", "ApplicationPoolController ", new { id = documentId });
When you want to retrieve the document, you simply fetch it based on the key:
public ActionResult UpdateConfirmation(int id)
{
XDocument doc = _myDocumentRepository.GetById(id);
ConfirmationModel model = new ConfirmationModel(doc);
return View(model);
}
Have you tried using ASP.NET MVC TempData ?
ASP.NET MVC TempData dictionary is used to share data between controller actions. The value of TempData persists until it is read or until the current user’s session times out. Persisting data in TempData is useful in scenarios such as redirection, when values are needed beyond a single request.
The code would be something like this:
[HttpPost]
public ActionResult ApplicationPoolsUpdate(ServiceViewModel viewModel)
{
XDocument updatedResultsDocument = myService.UpdateApplicationPools();
TempData["doc"] = updatedResultsDocument;
return RedirectToAction("UpdateConfirmation");
}
And in the ApplicationPoolController:
public ActionResult UpdateConfirmation()
{
if (TempData["doc"] != null)
{
XDocument updatedResultsDocument = (XDocument) TempData["doc"];
...
return View();
}
}
Personally I don't like to use TempData, but I prefer to pass a strongly typed object as explained in Passing Information Between Controllers in ASP.Net-MVC.
You should always find a way to make it explicit and expected.
I prefer to use this instead of TempData
public class Home1Controller : Controller
{
[HttpPost]
public ActionResult CheckBox(string date)
{
return RedirectToAction("ActionName", "Home2", new { Date =date });
}
}
and another controller Action is
public class Home2Controller : Controller
{
[HttpPost]
Public ActionResult ActionName(string Date)
{
// do whatever with Date
return View();
}
}
it is too late but i hope to be helpful for any one in the future
If you need to pass data from one controller to another you must pass data by route values.Because both are different request.if you send data from one page to another then you have to user query string(same as route values).
But you can do one trick :
In your calling action call the called action as a simple method :
public class ServerController : Controller
{
[HttpPost]
public ActionResult ApplicationPoolsUpdate(ServiceViewModel viewModel)
{
XDocument updatedResultsDocument = myService.UpdateApplicationPools();
ApplicationPoolController pool=new ApplicationPoolController(); //make an object of ApplicationPoolController class.
return pool.UpdateConfirmation(updatedResultsDocument); // call the ActionMethod you want as a simple method and pass the model as an argument.
// Redirect to ApplicationPool controller and pass
// updatedResultsDocument to be used in UpdateConfirmation action method
}
}

Can't get HTTP PUT request to work in ASP.NET Core

I'm trying to update an entry in the game table. However, my PUT request in ASP.NET never seems to trigger, and I can't figure out why.
This is controller in ASP.NET:
[Route("game/{update.GameID}")]
[HttpPut]
public IActionResult updateGame([FromBody]Game update)
{
var result = context.Games.SingleOrDefault(g => g.GameID == update.GameID);
if (result != null)
{
result = update;
context.SaveChanges();
}
return Created("", result);
}
And this is the code I use in Angular:
url:string;
constructor(private _http: HttpClient) {
this.url = "https://localhost:44359/api/v1/"
};
putGame(id:number, game:Game){
return this._http.put(this.url + "game/" + id, game);
}
Edit 1: I do have a list of GET-requests, which all work just fine. It's only the PUT-request that fails.
If you are using PUT request you need to add a resource id either to update or create new - so just don't combine your id with your object
[HttpPut("game/{id}")]
public IActionResult UpdateGame(int id, [FromBody]Game update) {
//...
}
If you are using Asp.net Core you can just re-write your URL on your HTTP verbs attribute like the code above - So pass your resource id in the URL and bind your object in the body - Your URL should read as https://localhost:44359/api/v1/game/2
Hope this helps you - Happy coding !!
The route template parameter {update.GameID} is not standard to what is suggested by documentation
Assuming the game id is an integer review the following
//PUT .../game/5
[Route("game/{id:int}")]
[HttpPut]
public IActionResult updateGame(int id, [FromBody]Game update) {
//...
}
Reference Routing to controller actions in ASP.NET Core
I would also suggest you review the logic of the action as I do not believe it is doing what you think it does with updating the entity returned from the context.
Can you modify your defining route just like
[Route("game")]
[HttpPut]
public IActionResult updateGame([FromBody]Game update)
{
//your code
}
And call from angular like
putGame(game:Game){
return this._http.put(this.url + "game", game);
}
you can receive gameid from game object so don't need from url

how to pass Parameters Info from the Body in ASP.NET Web APi?

I'm trying to pass a lengthy string through my POST methond from the actual body, it works perfectly fine if I pass it through url but I dont know what to change so I can insert data from body instead.
public void PostMethod(string id, [FromBody]string data)
{
if (ModelState.IsValid)
{
var result = client.Store(StoreMode.Add, id, data);
}
else
{
}
}
if I use it like this:
http://localhost:8888/api/data?id=2&data=MybigString
It works perfectly, but I don't want to pass data from URL, any suggestion would be highly appreciated.
Given your action method, which is public void PostMethod(string id, [FromBody]string data), you can use the URI of http://localhost:8888/api/data/2 and the message body of =MyBigString. If you use jQuery, you can use something like this: $.post('api/data/2', { '': 'MyBigString' }); to ensure the correct message body is sent.
EDIT:
Use a hidden field and set the value of it before the post. Just make sure it's inside your form.
#Html.HiddenFor(Model.data)
To set the value in javascript using jQuery:
$("#data").val('my big string');
Alternatively, if you're not binding to a strongly-typed view, a simple HTML hidden input will work instead of the #Html.HiddenFor():
<input type="hidden" id="data" name="data" />

Post parameter is always null

Since upgrading to RC for WebAPI I'm having some real odd issue when calling POST on my WebAPI.
I've even gone back to the basic version generated on new project. So:
public void Post(string value)
{
}
and calling from Fiddler:
Header:
User-Agent: Fiddler
Host: localhost:60725
Content-Type: application/json
Content-Length: 29
Body:
{
"value": "test"
}
When I debug, the string "value" is never being assigned to. It's just always NULL.
Anyone having this issue?
(I first saw the issue with a more complex type)
The problem is not only bound to ASP.NET MVC 4, the same problem occurs for a fresh ASP.NET MVC 3 project after RC installation
I have been scratching my head over this today.
My solution is to change the [FromBody] to a HttpRequestMessage, essentially moving up the HTTP stack.
In my case I am sending data across the wire which is zipped json which is then base64'd. All this from an android app.
The original signature of my web endpoint looked like this (using [FromBody]) :
My fix for this issue was to revert to using a HttpRequestMessage for the signature of my endpoint.
You can then get access to the post data using this line of code:
This works and allows you access to the raw untouched post data. You don't have to mess around with fiddler putting an = sign at the beginning of your string or changing the content-type.
As an aside, I first tried to following one of the answers above which was to change the content type to: "Content-Type: application/x-www-form-urlencoded". For raw data this is bad advice because it strips out + characters.
So a base64 string that starts like this: "MQ0AAB+LCAAAAAA" ends up like this "MQ0AAB LCAAAAAA"! Not what you want.
Another benefit of using HttpRequestMessage is that you get access to all the http headers from within your endpoint.
Since you have only one parameter, you could try decorating it with the [FromBody] attribute, or change the method to accept a DTO with value as a property, as I suggested here: MVC4 RC WebApi parameter binding
UPDATE: The official ASP.NET site was updated today with an excellent explanation: https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/sending-html-form-data-part-1
In a nutshell, when sending a single simple type in the body, send just the value prefixed with an equal sign (=), e.g. body:
=test
I've just had this occur using Fiddler. The problem was that I hadn't specified Content-Type.
Try including a header for Content-Type in your POST request.
Content-Type: application/x-www-form-urlencoded
Alternatively, as per comments below, you may need to include a JSON header
Content-Type: application/json
I've ran into this problem as well, and this is how I solved my problem
webapi code:
public void Post([FromBody] dynamic data)
{
string value = data.value;
/* do stuff */
}
client code:
$.post( "webapi/address", { value: "some value" } );
I was using Postman and I was doing the same mistake.. passing the value as json object instead of string
{
"value": "test"
}
Clearly the above one is wrong when the api parameter is of type string.
So, just pass the string in double quotes in the api body:
"test"
Try creating a class to serve as the data model, then send a JSON object with properties matching the properties of your data model class. (Note: I have tested this and it works with the newest MVC 4 RC 2012 that I just downloaded today).
public HttpResponseMessage Post(ValueModel model)
{
return Request.CreateResponse<string>(HttpStatusCode.OK, "Value Recieved: " + model.Value);
}
public class ValueModel
{
public string Value { get; set; }
}
The below JSON object is sent in HTTP-POST body, content-type is application/json
{ "value": "In MVC4 Beta you could map to simple types like string, but testing with RC 2012 I have only been able to map to DataModels and only JSON (application/json) and url-encoded (application/x-www-form-urlencoded body formats have worked. XML is not working for some reason" }
I believe the reason why you have to create a data model class is because simple values are assumed to be from the url parameters, and a single complex value is assumed to be from the body. They do have the [FromBody] and [FromUrl] attributes, but using [FromBody] string value still did not work for me. Seems like they are still working out a lot of bugs so I'm sure this will change in the future.
Edit:
Got XML to work in the body. The default XML serializer was changed to DataContractSerializer instead of XmlSerializer. Putting the following line in my Global.asax file fixed this issue (reference)
GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true;
After some tries, I think the default behavior is correct and there is nothing to hack.
The only trick is: if your post method argument is string like below, you should send a plain string with double quotes in the body (when using ajax or postman), e.g.,
//send "{\"a\":1}" in body to me, note the outer double quotes
[HttpPost("api1")]
public String PostMethod1([FromBody]string value)
{
return "received " + value; // "received {\"a\":1}"
}
Otherwise if you send a json string in the post body without outer double quotes and escaped inner quotes, then it should be able to be parsed to the model class (the argument type), e.g., {"a":1, "b":2}
public class MyPoco{
public int a;
public int b;
}
//send {"a":1, "b":2} in body to me
[HttpPost("api2")]
public String PostMethod2([FromBody]MyPoco value)
{
return "received " + value.ToString(); //"received your_namespace+MyPoco"
}
I was looking for a solution to this problem for some minutes now, so I'll share my solution.
If you post a model your model needs to have an empty/default constructor, otherwise the model can't be created, obviously.
Be careful while refactoring. ;)
For those who are having the same issue with Swagger or Postman like I did, if you are passing a simple attribute as string in a post, even with the "ContentType" specified, you still going to get a null value.
Passing just:
MyValue
Will get in the controller as null.
But if you pass:
"MyValue"
The value will get right.
The quotes made the difference here. Of course, this is only for Swagger and Postman. For instance, in a Frontend app using Angular this should be resolved by the framework automaticly.
This worked for me:
Create a C# DTO class, with a property for every attribute you want to pass from jQuery/Ajax
public class EntityData
{
public string Attr1 { get; set; }
public string Attr2 { get; set; }
}
Define the web api method:
[HttpPost()]
public JObject AddNewEntity([FromBody] EntityData entityData)
{
Call the web api as such:
var entityData = {
"attr1": "value1",
"attr2": "value2"
};
$.ajax({
type: "POST",
url: "/api/YOURCONTROLLER/addnewentity",
async: true,
cache: false,
data: JSON.stringify(entityData),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
...
}
});
I had the same issue and found that when changing the Content Type to "application/json" did not fix the problem. However "application/json; charset=utf-8" worked.
I had a similar issue where the request object for my Web API method was always null. I noticed that since the controller action name was prefixed with "Get", Web API treated this as a HTTP GET rather than a POST. After renaming the controller action, it now works as intended.
With Angular, I was able to pass data in this format:
data: '=' + JSON.stringify({ u: $scope.usrname1, p: $scope.pwd1 }),
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8' }
And in Web API Controler:
[HttpPost]
public Hashtable Post([FromBody]string jsonString)
{
IDictionary<string, string> data = JsonConvert.DeserializeObject<IDictionary<string, string>>(jsonString);
string username = data["u"];
string pwd = data["p"];
......
Alternatively, I could also post JSON data like this:
data: { PaintingId: 1, Title: "Animal show", Price: 10.50 }
And, in the controller, accept a class type like this:
[HttpPost]
public string POST(Models.PostModel pm)
{
....
}
Either way works, if you have an established public class in the API then post JSON, otherwise post '=' + JSON.stringify({..: ..., .. : ... })
In my case the problem was that the parameter was a string and not an object, i changed the parameter to be JObject of Newsoft.Json and it works.
If you are sure about your sent JSON then you must trace your API carefully:
Install Microsoft.AspNet.WebApi.Tracing package
Add config.EnableSystemDiagnosticsTracing(); in the WebApiConfig class inside Register method.
Now look at the Debug output and you will probably find an invalid ModelState log entry.
If ModelState is invalid you may find the real cause in its Errors:
No one can even guess such an exception:
Could not load file or assembly 'Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
Met with a similar problem in ASP.NET Core and another possible cause is ASP.NET binding (silent) failure due to various reasons such as sending null to be bound to a not null property:
{
"prop1":1139357,
"prop2":1139356,
"items":[
{"key":"15","someprop":34,"notnullprop":null},
{"key":"16","someprop":34,"notnullprop":null},
{"key":"22","someprop":34,"notnullprop":null}]
}
On such case there is no exception being thrown and the whole model will be null, even if this happens deep inside the object hierarchy.
Adding line
ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
to the end of function protected void Application_Start() in Global.asax.cs fixed similar problem for me in ASP.NET MVC3.
If you are using a DataContractSerializer for your Xml Formatter or JSON Formatter, you need to get rid of it.
I had this in my WebApiConfig file:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.UseDataContractJsonSerializer = true;
}
Simply I comment out jsonFormatter.UseDataContractJsonSerializer = true;and my input parameter isn't null anymore. Thanks to 'Despertar' for giving me a clue.
For complex types, Web API tries to read the value from the message body, using a media-type formatter.
Please check if you got any [Serializable] attribute decorating your model class.
Remove the attribute to see if it works. This worked for me.
Double check your data types. The dotnet model binder will not convert a float to an integer (and I'm assuming other related concepts). This will cause the entire model to be rejected.
If you have json like this:
{
"shoeSize": 10.5
}
but your c# model looks like this:
class Shoe{
public int shoeSize;
}
the model binder will reject the model and you will get null.
I had the same issue of getting null as parameter, but it was related to large objects. It turned out the problem was related to IIS max length. It can be configured in web.config.
<system.web>
<httpRuntime targetFramework="4.7" maxRequestLength="1073741824" />
</system.web>
I wonder why Web API suppressed the error and sends null objects to my APIs. I found the error using Microsoft.AspNet.WebApi.Tracing.
JSON.stringify(...) solved my issues
I know this is not an answer to this question, but I came across it when searching for a solution to my problem.
In my case, the complex type was not being bound but I was not doing a POST, I was doing a GET with querystring parameters. The solution was to add [FromUri] to the arg:
public class MyController : ApiController
{
public IEnumerable<MyModel> Get([FromUri] MyComplexType input)
{
// input is not null as long as [FromUri] is present in the method arg
}
}
I had the same problem in Fiddler. I already had Content-Type: application/json; charset=utf-8 or Content-Type: application/json in the request header.
My request body was also a plain string, and in Fiddler I had written: {'controller':'ctrl'}. This made the string parameter in my POST method be null.
Fix: remember to use quotation marks, thereby indicating a string. That is, I fixed it by writing "{'controller':'ctrl'}". (Note: when writing JSON, either be sure to use apostrophes or escape the quotation marks like this: "{\"controller\":\"ctrl\"}").
The most simple way I found to deal with simple JSON object that I pass into MVC 6 is getting the the type of the post parameter like NewtonSoft jObject:
public ActionResult Test2([FromBody] jObject str)
{
return Json(new { message = "Test1 Returned: "+ str }); ;
}
The best solution for me is going full HTTP as below:
[Route("api/open")]
[HttpPost]
public async Task<string> open(HttpRequestMessage request)
{
var json = await request.Content.ReadAsStringAsync();
JavaScriptSerializer jss = new JavaScriptSerializer();
WS_OpenSession param = jss.Deserialize<WS_OpenSession>(json);
return param.sessionid;
}
and then deserializing the string to the object you expect in the post body.
For me, WS_OpenSession is a class that contained sessionid, user and key.
You can from there use the param object and access its properties.
Very very effective.
I did say sourced from this url:
http://bizcoder.com/posting-raw-json-to-web-api
I'm a little late to the party, but anyone who stumbles across a NULL value passed when using a controller simply add "=" to the front of your POST request.
The controller also passed a NULL value when I used the application/json Content-Type. Note the "application/x-www-form-urlencoded" Content-Type below. The return type from the API however is "application/json".
public static string HttpPostRequest(string url, Dictionary<string, string> postParameters)
{
string postData = "=";
foreach (string key in postParameters.Keys)
{
postData += HttpUtility.UrlEncode(key) + "="
+ HttpUtility.UrlEncode(postParameters[key]) + ",";
}
HttpWebRequest myHttpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
myHttpWebRequest.Method = "POST";
byte[] data = System.Text.Encoding.ASCII.GetBytes(postData);
myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
myHttpWebRequest.ContentLength = data.Length;
Stream requestStream = myHttpWebRequest.GetRequestStream();
requestStream.Write(data, 0, data.Length);
requestStream.Close();
HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
Stream responseStream = myHttpWebResponse.GetResponseStream();
StreamReader myStreamReader = new StreamReader(responseStream, System.Text.Encoding.Default);
string pageContent = myStreamReader.ReadToEnd();
myStreamReader.Close();
responseStream.Close();
myHttpWebResponse.Close();
return pageContent;
}
it doesn't matter what type of value you wish to post, just enclose it within the quotation marks, to get it as string. Not for complex types.
javascript:
var myData = null, url = 'api/' + 'Named/' + 'NamedMethod';
myData = 7;
$http.post(url, "'" + myData + "'")
.then(function (response) { console.log(response.data); });
myData = "some sentence";
$http.post(url, "'" + myData + "'")
.then(function (response) { console.log(response.data); });
myData = { name: 'person name', age: 21 };
$http.post(url, "'" + JSON.stringify(myData) + "'")
.then(function (response) { console.log(response.data); });
$http.post(url, "'" + angular.toJson(myData) + "'")
.then(function (response) { console.log(response.data); });
c#:
public class NamedController : ApiController
{
[HttpPost]
public int NamedMethod([FromBody] string value)
{
return value == null ? 1 : 0;
}
}
If you put [FromBody] annotation and you have a Dto object as a parameter to your method and still not able to get the data through, start looking into the properties and fields of your DTO.
I had this same problem, where my DTO was coming null. I found the reason was that one of the properties was pointing into an object that cannot be serialised :( which causes the media-formatter to fail to parse the data. Thus the object was always null.
Hope it helps others too
I am pretty late to this but was having similar issues and after a day of going through a lot of the answers here and getting background I have found the easiest/lightweight solution to pass back one or more parameters to a Web API 2 Action is as follows:
This assumes that you know how to setup a Web API controller/action with correct routing, if not refer to: https://learn.microsoft.com/en-us/aspnet/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api.
First the Controller Action, this solution also requires the Newtonsoft.Json library.
[HttpPost]
public string PostProcessData([FromBody]string parameters) {
if (!String.IsNullOrEmpty(parameters)) {
JObject json = JObject.Parse(parameters);
// Code logic below
// Can access params via json["paramName"].ToString();
}
return "";
}
Client Side using jQuery
var dataToSend = JSON.stringify({ param1: "value1", param2: "value2"...});
$.post('/Web_API_URI', { '': dataToSend }).done(function (data) {
console.debug(data); // returned data from Web API
});
The key issue I found was making sure you only send a single overall parameter back to the Web API and make sure it has no name just the value { '': dataToSend }otherwise your value will be null on the server side.
With this you can send one or many parameters to the Web API in a JSON structure and you don't need to declare any extra objects server side to handle complex data. The JObject also allows you to dynamically iterate over all parameters passed in allowing easier scalability should your parameters change over time. Hopefully that helps someone out that was struggling like me.

Can I pass a non-primitive data type to a WebMethod?

I am having an issue where I am attempting to pass a non-primitive (and user defined) data type into a WebMethod. Is there a certain way to do this? Here is an example of my code:
[WebMethod]
public bool GetTableRecordCnt(int i, String str, DateTime? lastUpdatedDate, UserDefinedType udt, out FDT_SCHEDULER_STATUS[] schedulerTable)
{
//code
}
When I try to call this function from my client application I get the following error:
"Unable to read data from the transport connection."
If I replace the UserDefineType parameter with a primitive data type (an int for example) the client is able to get a response from the WebMethod.
Thanks in advance for you help.
EDIT:
Calling code from client application:
UserDefinedType udt = new UserDefinedType();
UserDefinedType1[] tableRecords = ThisApplication.FillArray();
bool result = WebServiceReferenceName.GetTableRecordCnt(1, "tableName", "10/10/2010 12:00:00", udt, out tableRecords);
That is a gross oversimplification of the parameters that are being passed to the web method, but the data that is in each parameter is what would be passed.
Yes, you can. You would need to create an object which matched the public properties of your UserDefinedType. This is fairly simply if you are using .Net webservices and have marked the method as [ScriptMethod()] which will then respond to JSON.
Here's an example:
public class UserDefinedType
{
public int Property1 { get; set; }
public string Property2 { get; set; }
}
Which you could pass as a param using the following javascript:
var param = "{ Property1 : '"+prop1Val+"', Property2 : '"+prop2Val+"'}";
Note, you have to declare the JSON object being passed as a string as otherwise if you are using jQuery.ajax(...) it will serialise your param object to an encoded param string rather than pass it as a native JSON string.
There are also some nice libraries out there that will take care of the JSON data object to string for you such as jquery-json 2.2. Using this you can then simply pass the param as $.toJSON(param).
Finally, here's an example of sending the request to an ASP .Net web service using jquery:
$.ajax({
type: "POST",
url: "/Services/YourService.asmx/YourMethod",
cache: false,
data: $.toJSON(param), // Convert JSON object to String for Post
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (response) {
HandleResponse(response.d)
},
error: function (e) {
alert('error during web service call');
}
});
For now I have an answer to my own question. I have changed the web service method signature to:
[WebMethod]
public bool GetTableRecordCnt(int i, String str, DateTime? lastUpdatedDate, object udt, out FDT_SCHEDULER_STATUS[] schedulerTable)
{
//code
}
Notice that I changed the UserDefinedType type to object. Now I am able to get my client to talk to my web service. Does anyone have any reason that this should not be done?
This explain your problem: Passing a custom object to the web service
Probably your UserDefinedType contains "fancy" stuff like List, Dictionary or something that can't be serialized thus you get error.
I'm surprised that you can pass the UserDefinedType like this, make sure you get all the properties you need, I suspect you'll get lot of null values for the non primitive parts.
Yes this is possible as long as your type is serializable.
OK, so finding out what the problem is has finally given me some relief =)
Apparently there is a limit to the number of characters that a variable can be named when you pass a UserDefinedType to a web method. After testing with each piece that I added for this new function in my code. I found that shortening the following variable name:
SchedulerRecordCount
to:
SchedRecCnt
is now allowing data to be passed between my windows mobile application and the c# web service. GO FIGURE!
Now, does anyone know if there is a specific numeric limit to the number of characters that can be used to name a variable in a UserDefinedType that will be passed to a Web Method, or could this be an environment issue on my end?

Categories