First of all, I'm a new developer, so I apologize in advance if I'm missing something obvious.
I'm developing a web application to work offline with large amounts of data in an IndexedDB. When the user goes to the webapp, the client grabs the entire database from the server and stores it for use in the indexeddb. That works fine, but when I'm trying to use a post method to send the data (again multiple records) back to WCF, I get method not allowed or bad request when trying to send an ajax body parameter, and when I do use uri parameters, it hits the server, but not all the data is sent. I thought perhaps invalid characters may be a factor so I used the encodeURIComponent method in javascript to convert invalid characters to be valid in a uri parameter. I've also tried compressing the data with a javascript compression api called LZString. I've tried using XMLHttpRequest(which I don't fully understand). This webapp has to work offline so I can't make a server call except for initially getting data when the client first opens and for syncing data back to the server, which is why I have to send large amounts of data at a time.
I'm also using an IndexedDB wrapper called Dexie.js.
Samples of my code is below. Some code is commented, but is left to show what I've tried.
This is what I have on the server..
[OperationContract]
[WebInvoke(Method = "POST",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "REST_SendCompletedServiceOrders",
BodyStyle = WebMessageBodyStyle.Wrapped)]
[FaultContract(typeof (Exception))]
bool REST_SendCompletedServiceOrders(string compressedWebData);
This is the click event on the client used to sync back..
$('#syncCompletedData').on('click', function() {
db.ServiceOrder
.toArray(function(so) {
var completedServiceOrders = [];
for (var i = 0; i < so.length; i++) {
if (so[i].IsCompleted) {
completedServiceOrders.push(so[i]);
};
}
var customerId = sessionStorage.getItem("customerId");
var companyId = sessionStorage.getItem("companyId");
var computerId = sessionStorage.getItem("computerId");
var webData = JSON.stringify({ webCustomerId: customerId, webCompanyId: companyId, webComputerId: computerId, webServiceOrder: completedServiceOrders });
alert(webData);
alert("before compression is " + webData.length);
var URIEncodedWebData = encodeURIComponent(webData);
var JSONWebData = JSON.stringify(URIEncodedWebData);
var compressedWebData = LZString.compressToUTF16(JSONWebData);
alert("after compression is " + compressedWebData.length);
debugger;
try {
$.ajax({
type: "POST",
url: "MFSRemoteDataService/REST_SendCompletedServiceOrders",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: { compressedWebData: compressedWebData },
success: function(data) { alert(JSON.stringify(data)); },
failure: function(errMsg) {
alert(errMsg);
}
});
} catch (e) {
alert(e);
}
});
});
Before compression data length is 7707.
After compression data length is 1831.
Thanks in advance for any help, feedback, criticism, etc..
In the shown snippet, you are composing the ajax data for use in a get, which usually means you prepare a uri. However, since he is both using post and ajax, the information will be sent in the post request body and as such does not need to be encoded.
The encoding is bloating the stringified json. You can stop at webdata and post that all by itself, remove the dataType parameter in the ajax options, switch to using traditional:true in the ajax options, and it should all properly model bind.
It is hard to tell what your server side view model looks like, but if the accepting parameter is named compressedWebData (names must be exact, same goes with structure), then it would probably work like this
//code as shown in OP
//replace var webData = with the following
var compressedWebData = { webCustomerId: customerId, webCompanyId: companyId, webComputerId: computerId, webServiceOrder: completedServiceOrders };
try {
$.ajax({
type: "POST",
url: "MFSRemoteDataService/REST_SendCompletedServiceOrders",
contentType: "application/json",
data: JSON.stringify(compressedWebData),
traditional:true,
success: function(data) { alert(JSON.stringify(data)); },
failure: function(errMsg) {
alert(errMsg);
}
});
} catch (e) {
alert(e);
}
I figured out my problem. I've been trying to pass a string to the contract method, and I kept getting bad request errors. Instead, I wrapped the Json string and sent it to an object instead of a string that I created on the server.
I wrapped the JSON and sent it in the body of the ajax request..
var rawWebData = {
WebCustomerID: customerId,
WebCompanyID: companyId,
WebComputerID: computerId,
WebServiceOrders: completedServiceOrders
};
var rawData = { webData: rawWebData };
var webData = JSON.stringify(rawData);
try {
$.ajax({
type: "POST",
url: "MFSRemoteDataService/REST_SendCompletedServiceOrders",
contentType: "application/json; charset=utf-8",
dataType: "json",
traditional: true,
data: webData,
success: function (data) {
alert(JSON.stringify(data));
},
failure: function (errMsg) {
alert(errMsg);
}
});
} catch (e) {
alert(e);
}
});
Then I created a class to collect the data...
[DataContract]
public class WebServiceOrder
{
[DataMember]
public Int32 WebCustomerID { get; set; }
[DataMember]
public Int32 WebCompanyID { get; set; }
[DataMember]
public Int32 WebComputerID { get; set; }
[DataMember]
public virtual List<ServiceOrder> WebServiceOrders { get; set; }
}
Then I changed the contract method to accept the object I created instead of a string. WCF deserialized the JSON string.
[OperationContract]
[WebInvoke(Method = "POST",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "REST_SendCompletedServiceOrders",
BodyStyle = WebMessageBodyStyle.WrappedRequest)]
[FaultContract(typeof (Exception))]
bool REST_SendCompletedServiceOrders(WebServiceOrder webData);
Related
In my web service (order.asmx) I have a method which accept a viewmodel as a parameter. Method signature is below.
public string CreateOrder(OrderModel order)
and my order.cs is like below,
public class OrderModel: BaseClass
{
public OrderModel()
{
this.RestaurantIDs = new List<long>();
}
public long OrderID { get; set; }
public List<long> RestaurantIDs { get; set; }
}
I'm passing all the order related details using ajax call to this service method. To pass the RestaurantIDs I'm using an array which gives me 500 server error. I tried with stringify as well but didnt work. Anyone can help me with this?
ajax call
function createOrderObject()
{
var order = new object;
order.CustomerName = $("#txtName").val();
order.RestaurantIDs = selectedRestaurants.map(function (obj) {
return obj.RestaurantID;
});
}
// here the selectedRestaurants is object array.
function createOrder()
{
var order = createOrderObject();
var url = '/Service/OrderService.asmx/CreateOrder';
var dataSet = '{orderModel:' + JSON.stringify(order) + '}';
var orderInfo = $.ajax({
type: 'POST',
contentType: "application/json; charset=utf-8",
url: url,
data: dataSet,
dataType: "json",
error: function () {
giveNotification({ NotificationCode: 500, Message: "Internal Server Error, Please try again." });
loadingImg(ele, "hide");
});
}
the error message }
I have been working with Web API and found an interesting observation that I am not able to understand.
controller:
public class UserController: ApiController
{
public void Post(MyViewModel data)
{
//data is null here if pass in FormData but available if its sent through Request Payload
}
}
viewModel
public class MyViewModel{
public long SenderId { get; set; }
public string MessageText { get; set; }
public long[] Receivers { get; set; }
}
JS that is not working
var usr = {};
usr.SenderId = "10";
usr.MessageText = "test message";
usr.Receivers = new Array();
usr.Receivers.push("4");
usr.Receivers.push("5");
usr.Receivers.push("6");
$.ajax(
{
url: '/api/User',
type: 'POST',
data: JSON.stringify(usr),
success: function(response) { debugger; },
error: function(error) {debugger;}
});
JS that is working
var usr = {};
usr.SenderId = "10";
usr.MessageText = "test message";
usr.Receivers = new Array();
usr.Receivers.push("4");
usr.Receivers.push("5");
usr.Receivers.push("6");
$.post( "/api/User", usr)
.done(function( data ) {
debugger;
});
So, if I pass on $.ajax with lots of other configuration like type, contentType, accept etc, it still don't bind model correctly but in case of $.post it works.
Can anybody explain WHY?
Try looking at what gets POSTed when you try it with $.ajax (e.g. with Fiddler of F12 tools of your choice). It can very well be that jQuery passes the data as URL-encoded string rather that as JSON literal.
To fix the issue try specifying dataType together with contentType parameter. Also, I don't think you need JSON.stringify, just pass the JSON literal you're creating:
$.ajax({
data: usr,
dataType: 'json',
contentType: 'application/json',
/* The rest of your configuration. */
});
Here's the TypeScript method that we use in one of our projects (ko.toJSON returns a string representing a JSON literal passed as a method parameter):
public static callApi(url: string, type?: string, data?: any): RSVP.Promise {
return new RSVP.Promise((resolve, reject) => {
$.ajax('/api/' + url, {
type: type || 'get',
data: data != null ? ko.toJSON(data) : null,
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: () => {
resolve.apply(this, arguments);
},
error: () => {
reject.apply(this, arguments);
}
});
});
}
Hope this helps.
my problem is following.
I try to parse some data via ajax, passing the data to my controller:
AJAX
$.ajax({
type: "GET",
url: "ParseOrganizaitonPath",
data: {
organizationPath: $('#organizationPath').val()
},
success:
function (data) {
//data is from type string with value "System.string[]"
//but should be from type System.string[]
});
}
});
Controller
public string[] ParseOrganizaitonPath(string organizationPath)
{
List<string> organizations = organizationPath.Split('/').ToList();
return organizations.ToArray();
}
I am reaching the controller method and in it everything is fine, but the data that is comming back (ajax part, success method) is just a string ("System.string[]", data[0] S, data[1]y data[2]s...) but not the data I want. (For example if i pass the input "test/one" I want to have as result data[0] test, data[1] one)
Hope you understand what my Problem is.
Thanks in advance!
Julian
Have to tried to use the JavaScriptSerializer? Have a look at this example:
public string ParseOrganizaitonPath(string organizationPath)
{
List<string> organizations = organizationPath.Split('/').ToList();
System.Web.Script.Serialization.JavaScriptSerializer oSerializer =
new System.Web.Script.Serialization.JavaScriptSerializer();
return oSerializer.Serialize(organizations);
}
To deserialize the JSON string with JavaScript you can use the parse function:
var array = JSON.parse(data);
I found a way where you don't have to serialize it (on c# site) and parse it (on javascript site)
Just use the JSON Method that is inherited from the Controller and return an JsonResult:
public JsonResult ParseOrganizaitonPath(string organizationPath)
{
List<string> organizations = organizationPath.Split('/').ToList();
return JSON(organizations);
}
On the client site (javascript) you have to use JSON.stringfy(dataObject) for data that you want to send to your controller-method.
$.ajax({
type: "POST",
dataType: "json",
contentType: "application/json; charset=utf-8",
url: "ParseOrganizaitonPath",
data: JSON.stringify(myDataObject),
success:
function (data) {
//use your data
});
}
});
That's how it worked for me.
Good luck!
Julian
when i try to get JSON from http://api-v3.deezer.com/1.0/search/album/?q=beethoven&index=2&nb_items=2&output=json with:
(jQuery 1.6.2)
$.ajax({
type: "GET",
url: url,
dataType: "jsonp",
success: function (result) {
alert("SUCCESS!!!");
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.statusText);
alert(xhr.responseText);
alert(xhr.status);
alert(thrownError);
}
});
I get: parsererror; 200; undefined; jquery162******************** was not called
but with the JSON from http://search.twitter.com/search.json?q=beethoven&callback=?&count=5 works fine.
Both are valid JSON formats. So what is this error about?
[UPDATE]
#3ngima, i have implemented this in asp.net, it works fine:
$.ajax({
type: "POST",
url: "WebService.asmx/GetTestData",
data: "{}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (result) {
alert(result.d);
}
});
WebService.asmx:
[WebMethod]
public string GetTestData()
{
try
{
var req = System.Net.HttpWebRequest.Create("http://api-v3.deezer.com/1.0/search/album/?q=beethoven&index=2&nb_items=2&output=json");
using (var resp = req.GetResponse())
using (var stream = resp.GetResponseStream())
using (var reader = new System.IO.StreamReader(stream))
return reader.ReadToEnd();
}
catch (Exception) { return null; }
}
It's because you're telling jQuery that you're expecting JSON-P, not JSON, back. But the return is JSON. JSON-P is horribly mis-named, named in a way that causes no end of confusion. It's a convention for conveying data to a function via a script tag. In contrast, JSON is a data format.
Example of JSON:
{"foo": "bar"}
Example of JSON-P:
yourCallback({"foo": "bar"});
JSON-P works because JSON is a subset of JavaScript literal notation. JSON-P is nothing more than a promise that if you tell the service you're calling what function name to call back (usually by putting a callback parameter in the request), the response will be in the form of functionname(data), where data will be "JSON" (or more usually, a JavaScript literal, which may not be the quite the same thing). You're meant to use a JSON-P URL in a script tag's src (which jQuery does for you), to get around the Same Origin Policy which prevents ajax requests from requesting data from origins other than the document they originate in (unless the server supports CORS and your browser does as well).
in case the server does not support the cross domain request you can:
create a server side proxy
do ajax request to your proxy which in turn will get json from the service, and
return the response and then you can manipulate it ...
in php you can do it like this
proxy.php contains the following code
<?php
if(isset($_POST['geturl']) and !empty($_POST['geturl'])) {
$data = file_get_contents($_POST['geturl']);
print $data;
}
?>
and you do the ajax request to you proxy like this
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(function(){
$("#btn").click(function(){
alert("abt to do ajax");
$.ajax({
url:'proxy.php',
type:"POST",
data:{geturl:'http://api-v3.deezer.com/1.0/search/album/?q=beethoven&index=2&nb_items=2&output=json'},
success:function(data){
alert("success");
alert(data);
}
});
});
});
</script>
tried and tested i get the json response back...
At last i have found the solution. First of all, the webmethods in a webservice or page doesn't work for me, it always returns xml, in local works fine but in a service provider like godaddy it doesn't.
My solution was to create an .ahsx, a handler in .net and wrap the content with the jquery callback function that pass the jsonp, and it works .
[System.Web.Script.Services.ScriptService]
public class HandlerExterno : IHttpHandler
{
string respuesta = string.Empty;
public void ProcessRequest ( HttpContext context )
{
string calls= context.Request.QueryString["callback"].ToString();
respuesta = ObtenerRespuesta();
context.Response.ContentType = "application/json; charset=utf-8";
context.Response.Write( calls +"("+ respuesta +")");
}
public bool IsReusable
{
get
{
return false;
}
}
[System.Web.Services.WebMethod]
private string ObtenerRespuesta ()
{
System.Web.Script.Serialization.JavaScriptSerializer j = new System.Web.Script.Serialization.JavaScriptSerializer();
Employee[] e = new Employee[2];
e[0] = new Employee();
e[0].Name = "Ajay Singh";
e[0].Company = "Birlasoft Ltd.";
e[0].Address = "LosAngeles California";
e[0].Phone = "1204675";
e[0].Country = "US";
e[1] = new Employee();
e[1].Name = "Ajay Singh";
e[1].Company = "Birlasoft Ltd.";
e[1].Address = "D-195 Sector Noida";
e[1].Phone = "1204675";
e[1].Country = "India";
respuesta = j.Serialize(e).ToString();
return respuesta;
}
}//class
public class Employee
{
public string Name
{
get;
set;
}
public string Company
{
get;
set;
}
public string Address
{
get;
set;
}
public string Phone
{
get;
set;
}
public string Country
{
get;
set;
}
}
And here is the call with jquery:
$(document).ready(function () {
$.ajax({
// url: "http://www.wookmark.com/api/json",
url: 'http://www.gjgsoftware.com/handlerexterno.ashx',
dataType: "jsonp",
success: function (data) {
alert(data[0].Name);
},
error: function (data, status, errorThrown) {
$('p').html(status + ">> " + errorThrown);
}
});
});
and works perfectly
Gabriel
I am using the following JQuery\JavaScript code to communicate with a WCF 4 REST service.
<script>
var serviceUrl = "http://services.xiine.com/Xiine/Live/AccountService/rest/json/Login";
var userInfo = {
"IsNotEncrypted":true,
"Password":null,
"UserName":null
};
var loginSuccess = function(data, textStatus, jqXHR){
console.log("yay");
};
var loginError = function(){
console.log("oh no");
};
$.post(serviceUrl , userInfo, loginSuccess);
$.post(serviceUrl , loginSuccess);
</script>
I am trying to establish why it is that the service will correctly return a false value when I do not pass the user data:
$.post(serviceUrl , loginSuccess);
As opposed to when I do pass user data, at which point it gives a POST 400 (Bad Request) error.
$.post(serviceUrl , userInfo, loginSuccess);
I am sure it has to do with how the JSON object, userInfo, is being built or interpreted, and I can post Fiddler 2 or WireShark dumps, if that will help. Just let me know...
I don't really have access to changing the WCF side of the service, so hopefully there is something I can do on my end to make this work.
Thanks!
Edit
I got a little more information... Apparently, the problem is that the server is responding with the following error:
The server encountered an error processing the request. The exception message is 'The incoming message >has an unexpected message format 'Raw'. The expected message formats for the operation are 'Xml', >'Json'. This can be because a WebContentTypeMapper has not been configured on the binding. See the >documentation of WebContentTypeMapper for more details.'. See server logs for more details. The >exception stack trace is:
at System.ServiceModel.Dispatcher.DemultiplexingDispatchMessageFormatter.DeserializeRequest(Message message, Object[] parameters) at System.ServiceModel.Dispatcher.UriTemplateDispatchFormatter.DeserializeRequest(Message message, Object[] parameters) at System.ServiceModel.Dispatcher.CompositeDispatchFormatter.DeserializeRequest(Message message, Object[] parameters) at System.ServiceModel.Dispatcher.DispatchOperationRuntime.DeserializeInputs(MessageRpc& rpc) at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc) at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc& rpc) at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
So I am thinking I need to figure out how to get the object to go across the wire as a JSON object via a JQuery.post() method.
More information --- Again...
ok... There is no app.config or web.config, as such.
Here is what I can get as far as the contracts and code and what-not.
[ServiceContract]
public interface IAccount
{
[OperationContract]
bool Login(UserInformation user);
}
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class AccountService : IAccount
{
public bool Login(UserInformation user)
{
//stuff...
}
}
[DataContract]
public class UserInformation
{
[DataMember(IsRequired = true)]
public string UserName;
[DataMember(IsRequired = true)]
public string Password;
[DataMember(IsRequired = true)]
public bool IsNotEncrypted;
}
public interface IUserInformation
{
UserInformation UserInformation { get; set; }
}
So, I found the answer to my question.
Someone attempted to point me in the right direction with a mention of the JSON.stringify() method, but it took me a little effort to get it implemented correctly.
In the end, I used WireShark to sniff out the http request, see what was actually being sent and received, and observe that not only did I need to stingify the data, but I also needed to specify the content type.
Here is the final code.
// This is the URL that is used for the service.
var serviceUrl = "http://services.xiine.com/Xiine/Live/AccountService/rest/json/Login";
// This is the JSON object passed to the service
var userInfo = {
"UserName": null,
"Password": null,
"IsNotEncrypted": true
};
// $.ajax is the longhand ajax call in JQuery
$.ajax({
type: "POST",
url: serviceUrl,
contentType: "application/json; charset=utf-8",
// Stringify the userInfo to send a string representation of the JSON Object
data: JSON.stringify(userInfo),
dataType: "json",
success: function(msg) {
console.log(msg);
}});
in the interface for your service, you need to have the OperationalContract attribute and in that attribute, you need to set the RequestFormat. Here is an example of what it might look like.
[OperationContract, WebInvoke(UriTemplate = "/runGenericMethodJSON", Method = "POST",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.WrappedRequest)]