Calling WCF services from Javascript with UriTemplate and POST data - json

I've got a WCF 4.0 service that's currently set up for HTTP GET requests. I'm trying to modify it so that it uses POST, but keep backwards compatibility with the existing GET URIs. The web service is being called with jQuery and JSON data. Therefore, my requirements are the following:
Match the existing GET URI, presumably by using the UriTemplate parameter of the WebInvoke attribute.
Get the existing parameters from JSON data in the POST body, presumably by using WebMessageBodyStyle.Wrapped and WebMessageFormat.Json with the WebInvoke attribute.
Preferably have a way to pull large blobs of a data out of the POST body, probably also wrapped in a JSON object.
Alright, with that out of the way, here's what I've got so far. My little test service is called AjaxService and it's got one method called ToUpper. First, my web.config:
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="WebApplication2.AjaxServiceAspNetAjaxBehavior">
<!--<enableWebScript />-->
<webHttp helpEnabled="true" />
</behavior>
</endpointBehaviors>
</behaviors>
<protocolMapping>
<remove scheme="http" />
<add scheme="http" binding="webHttpBinding" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
<service name="WebApplication2.AjaxService">
<endpoint address=""
behaviorConfiguration="WebApplication2.AjaxServiceAspNetAjaxBehavior"
binding="webHttpBinding"
contract="WebApplication2.AjaxService" />
</service>
</services>
</system.serviceModel>
</configuration>
The following version of my ToUpper function allows me to pass its argument in the URI just like an HTTP GET.
[OperationContract]
[WebInvoke( UriTemplate="ToUpper?str={str}",
Method = "POST",
BodyStyle = WebMessageBodyStyle.WrappedRequest,
RequestFormat=WebMessageFormat.Json,
ResponseFormat=WebMessageFormat.Json)]
public string ToUpper(string str)
{
return str.ToUpper();
}
It's used in Javascript as follows and correctly returns "THIS IS FROM THE URI".
$(document).ready(function () {
$.ajax({
type: "POST",
url: "AjaxService.svc/ToUpper?str=this%20is%20from%20the%20uri",
contentType: "application/json; charset=utf-8",
success: function (data) {
console.log("result: " + JSON.stringify(data));
},
error: function (jqXHR, textStatus, errorThrown) {
console.log("error", jqXHR, textStatus, errorThrown);
}
});
});
I can put the parameter in the POST data instead of the URI by removing UriTemplate="ToUpper?str={str}" from the WebInvoke parameters and using this javascript instead. It correctly returns "THIS IS FROM THE POST".
$(document).ready(function () {
$.ajax({
type: "POST",
url: "AjaxService.svc/ToUpper",
data: JSON.stringify({str:"This is from the POST"}),
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function (data) {
console.log("result: " + JSON.stringify(data));
},
error: function (jqXHR, textStatus, errorThrown) {
console.log("error", jqXHR, textStatus, errorThrown);
}
});
});
I was hoping that if I left the UriTemplate="ToUpper?str={str}" in place and use the above javascript, it would be smart enough to pull the str parameter out of the POST data instead of the URI. Unfortunately, it just gives me an error 400. Is there a way to make that work?
One other thing I tried is using the optional Stream parameter to get at the raw contents of the POST data. But as this blog post points out, you can only get that stream if you set the content type to plain text instead of JSON. I could do that and manually parse the stream, but I think I'd also need to manually examine the URL to figure out if I got real values or defaults. Yuck.
If there's no way to make this work with the system bindings/settings, I was thinking about trying to write a custom binding that would be smart about pulling parameters out of the POST and URI, and also give you that raw stream even when you're using JSON. I spent a few hours trying to figure out how to do that but I'm stumped!
Has anyone else tackled this one or have any ideas on how to make it work? I can't imagine I'm the first one to have tried doing this, but I've come up empty handed after doing lots of searching.

Related

Ajax call fails when passing Json over certain size

I have the following code that passes a JSON object to an AJAX call...
var jsonResultStr = $j("#HiddenLiveJson").val();
var jsonResult = JSON.parse(jsonResultStr);
var serviceURL = appRoot + 'Register/ImportTasks'
$j.ajax({
type: "get",
url: serviceURL,
data: { 'jsonResultsStr': jsonResultStr },
contentType: "application/json; charset=utf-8",
dataType: "json",
success: successFunc,
error: errorFunc
});
The Json is sourced from an XLSX file so could potentially be any size.
If a relatively small JSON set is passed, say 7 items in the JSON, then the AJAX call passes successfully, the controller action is hit and my data is imported. However, if a larger JSON dataset is passed then it consistently fails (HTTP Status 400) without even going anywhere near the controller action. I don't seem to be able to find any suggestion anywhere what is causing this. My first guess, obviously, is the size of the XLSX file.
btw... The JSON used in my testing should be sound as the data that is failing is just the same data that succeeds but duplicated to double its size.
So far, I have tried
Adding this to my web.config:
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="1000000000">
</jsonSerialization>
</webServices>
</scripting>
</system.web.extensions>
And also this to the appSettings in web.config:
<add key="aspnet:MaxJsonDeserializerMembers" value="1500000000" />
And also setting the following:
<httpRuntime maxRequestLength="1000000000" .../>
None of which have made any difference.
Any more suggestions are greatly welcomed!
You need to use a POST request for large JSON object and also stringify the JSON data with contentType as "application/json" and dataType as "json".
Replace the 'data' parameter in the ajax call as :
data: JSON.stringify(jsonResult)
also in the server size script catch the client side data with a get POST method.
Hope this helps :)

Asmx response to json and call via ajax

I have the following problem
I am trying to connect a HTML page with a asmx service, using jquery.
The service is in my local IIS, and I have configured it for allow cross-domain request:
This is my service code:
[WebService(Namespace = "http://myservices.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
[ScriptService]
public class Test : WebService
{
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public Employee TestMethod()
{
return new Employee { Id = 1, Name = "John"};
}
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
}
This is my web.config service code:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<webServices>
<protocols>
<add name="HttpGet"/>
<add name="HttpPost"/>
</protocols>
</webServices>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
<handlers>
<add name="ScriptHandlerFactory"
verb="*" path="*.asmx"
type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
resourceType="Unspecified" />
</handlers>
</system.webServer>
</configuration>
So, with that I can call to service method from anywhere (different domains) using this url
http://localhost/WsTest/Test.asmx/TestMethod
But, the response is in xml, why? ... I don't know what I need
<Employee xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://myservices.org/">
<Id>1</Id>
<Name>John</Name>
</Employee>
That is my first question.
Second, When I try to get the service response using ajax with the following code:
$.ajax({
url: "http://localhost/WsTest/Test.asmx/TestMethod",
beforeSend: function () {},
data: {},
contentType: "application/json; charset=utf-8",
type: "POST",
dataType: "jsonp",
processData: false,
cache: false,
success: function(data) {
alert(data)
},
error: function(jqXHR, textStatus, errorThrown) {
console.log("Retrieve Failed (AJAX Error): " + jqXHR.statusText + " | " + textStatus + " | " + errorThrown);
}
});
I get the following response:
TestMethod?callback=jQuery220045970173459500074_1454084649739&[object Object]&_=1454084649740:1
Uncaught SyntaxError: Unexpected token <
Retrieve Failed (AJAX Error): load | parsererror | Error: jQuery220045970173459500074_1454084649739 was not called
I have read several answers about this question, but i cannot find the appropriate answer for my problem.
Thanks in advance!
I think you are not properly serializing the response before sending it back to the client.
You have to make your class serializable into a Json string. To do that follow the instructions provided in the following MSDN link:
How to: Serialize and Deserialize JSON Data
Then, you should change your web method to return a String value, and just before returning the response to the client, do the serialization trick, something like this:
var objectSerializer = new JavascriptSerializer();
//serialization
return objectSerializer.Serialize(data); //this will return a Json string
For more details about the JavascriptSerializer class check out the following link:
JavaScriptSerializer Class
And about your error, it is because your response is returning in XML format (the standard SOAP response) thus the javascript code you are using is expecting a Json format. The trick is to simply return a Json string.
The other option you have is to use WCF instead of ASMX, but that is another path you can check out here:
How do I return clean JSON from a WCF Service?
Beware about the security risks involved in exposing your service like that. Use unique ids or other mechanisms to secure your web service invocations from Javascript.

jQuery $.ajax failing no error message, server responded with 200 OK

I have an ajax call that works fine locally, but when I published it into the server, the code is never executed but returns "200 OK" anyway. No "success" code is ejecuted, nor the "error" code. Here's my ajax call:
$.ajax({
type: "POST",
url: "/CargadorContadores.aspx/ObtenerContadores",
contentType: "application/json; charset=utf-8",
data: "",
async: false,
dataType: "json",
success: function(data) {
var myObj = JSON.parse(data.d);
for (var i = 0; i < myObj.length; i++) {
var element = document.getElementById("LbItem " + myObj[i].MenuItemID);
element.innerHTML = "<b>" + element.innerHTML + " (" + myObj[i].Cantidad + ")</b>";
}
},
error: function(xhr, status, error) {
var err = eval("(" + xhr.responseText + ")");
alert(err.Message);
}
});
});
I also tried to put the complete url in the "url" field but still no luck.
The weird thing is that when I put the complete url directly into the browser, it is still not executing the code. And it works fine locally!!
I know there's a similar post (jQuery $.ajax failing silently, no error messages, and server responded with 200 OK) but is not resolved and it's driving crazy here.
PD: this is NOT cross-domain, my ajax code is in a .ascx and I am trying to call a method on an .aspx
Thanks very much!!!!
EDIT: I tried to remove the System.web.extensions reference of my web project and add the dll to my bin, but it's still not working (the 1.0.61025.0 version). Besides, I am running the ASP.NET website in IIS with framework 2.0, and I don't have framework 3.5 installed on my server (but locally I do). Maybe that's the problem? I can't install it because of the client security policies, what shall I do?
EDIT 2: I tried doing a simple response from my .aspx, just to test if the problem was in that method, but it is still not executing the success function. Here's my .aspx code:
[WebMethod]
public static string ObtenerContadores()
{
return new JavaScriptSerializer().Serialize("true");
}
and adapted the .ascx
$.ajax({
type: "POST",
url: "/CargadorContadores.aspx/ObtenerContadores",
contentType: "application/json; charset=utf-8",
data: "",
async: false,
dataType: "json",
success: function(dictionary) {
alert("hello");
},
error: function(xhr, status, error) {
var err = eval("(" + xhr.responseText + ")");
alert(err.Message);
}
});
});
Here's your problem: "The weird thing is that when I put the complete url directly into the browser, it is still not executing the code." Your JavaScript is expecting -- requiring -- a JSON response, and is apparently getting no response at all. This is not a JavaScript problem, but a problem with your .aspx page.
EDIT: Based on your comments below, it's clear that the problem is that your .aspx page is not returning what you expect. Because the .aspx doesn't produce the expected output even when you go to it directly, the problem is in that page, not in the jQuery.
Pretend the .aspx produces {}, false, or true. Your success code will run, but not do anything. Try putting an alert("Foo"); in there and watch what happens. The problem isn't that your jQuery is busted; it's that it isn't getting meaningful input from your .aspx page.
P.S. I would recommend using a variable name other than data in your success function, just in case it's getting messed up by the data object attribute in your .ajax call.
Okay, I believe I've figured this out, so I'm posting a second answer to preserve the prior discussion.
Here's the fix. Remove this line:
contentType: "application/json; charset=utf-8",
In my test script, this caused jQuery to send the Content-Type header application/json; charset=utf-8, as expected, but the actual contents of the request were not properly sent as JSON. For example, rather than sending {"foo":"bar"}, it was sending foo=bar. As a result, the receiving script acted as if no data was posted at all. When I commented out the line above, my test script worked fine and did whatever I expected with the values received from the target URL.
Note that this will post your data as normal post data. For example, if your $.ajax() call looks like this, the posted data will contain a variable named foo and one named who:
$.ajax({
...
data: {foo : "bar",
who : "what"},
...
});
If you want to post already-serialized JSON, do it like this:
$.ajax({
...
data: { somevariablename: '{"foo":"bar","who":"what"}' },
...
});
Then, you just have to parse the POST parameter somevariablename on the server side.
Hope this helps!
If its VS2012, try for adding Mime Type Handler, since VS2012 has IIS express and it need to add Mime type handler externally,
Add below code to web.config
<system.webServer>
<staticContent>
<mimeMap fileExtension=".json" mimeType="application/json" />
</staticContent>
</system.webServer>
.. and if its not VS2012 then follow this link
add .json handler support in IIS 7
I want to thank everyone for your help.
I finally had to install the 3.5 Framework on the server, I had no choice.
The second I installed it, and updated the web.config dll's references to 3.5, it worked like a charm.
Thanks again!!!!!

Cross domain ajax request

I want to get the HTML response page from the cross-domain URL.
for this I am using the ajax request as,
$.ajax({
type: 'GET',
url: "http://wcidevapps.com/salescentral/idisk/0001000383/iDisk",
dataType: "json",
success: function (response) {
$(response).find('li a').each(function () {
listHref.push($(this).attr('href'));
});
}
});
But after requesting it doesn't respond with any result back.
<script src="Scripts/jquery-1.4.1.js" type="text/javascript"></script>
<script type="text/javascript">
function NameAFunctionName() {
$.ajax({
url: 'http://wcidevapps.com/salescentral/idisk/0001000383/iDisk',
type: 'GET',
dataType: 'json',
headers: {
//WRITE IF THEIR HAVE SOME HEADER REQUEST OR DATA
},
crossDomain: true,
success: function (data, textStatus, xhr) {
console.log(data);
},
error: function (xhr, textStatus, errorThrown) {
console.log(errorThrown);
}
});
}
</script>
Check documentation :
http://api.jquery.com/jQuery.ajax/
crossDomain (default: false for same-domain requests, true for cross-domain requests)
Type: Boolean
If you wish to force a crossDomain request (such as JSONP) on the same domain, set the value of crossDomain to true. This allows, for example, server-side redirection to another domain. (version added: 1.5)
My suspicion is that you see the issue because the page you're requesting does not respond with a json(p) response, but responds with a redirect to:
http://wcidevapps.com/salescentral/idisk/0001000383/iDisk/
(note the trailing slash)
which then returns content type:
Content-Type:text/html;charset=ISO-8859-1
Edit: If your intention is to retrieve the above site's data cross-domain, for further parsing by your script, I suggest that you choose one of the following:
Assumption 1: YOU are in control of the pages on server "http://wcidevapps.com"
In that case, you have two options: Either add CORS header "Access-Control-Allow-Origin: *" to the response (and configure the client ajax() call with dataType:"html"), or create a special JSON(P) page that delivers the same data as JSON (with padding) (and configure the client ajax() call like in the OP, with dataType:"jsonp")
Assumption 2: YOU are NOT in control of the pages on server http://wcidevapps.com
In that case, the only option I can think of is setup a proxy on a site that you control. Have that proxy "proxy" the requests/responses to "http://wcidevapps.com", but add the CORS header "Access-Control-Allow-Origin: *" to the response (and configure the client ajax() call with dataType:"html")
If you are using asp.net web service then you need to add this to webconfig file;
<system.webServer>
<directoryBrowse enabled="true"/>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Headers" value="Content-Type" />
</customHeaders>
</httpProtocol>
</system.webServer>

Restful WCF POST issue with application/json content type request

I've configured a RESTful WCF with the following POST "operation":
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "/Test", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json)]
void PostTest(Stream stream);
In my web.config, I've configured the following:
<service name="MyTest.TestSvc" behaviorConfiguration="MyTest.TestBehavior" >
<endpoint address="" behaviorConfiguration="MyBehavior" binding="webHttpBinding" contract="MyTest.ITestSvc"/>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
<endpointBehaviors>
<behavior name="MyBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="MyTest.TestBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
When I sent a POST message using "text/plain" or "json" everything works fine.
However, when I try to send a POST message with
ContentType = "application/json"
It fails with the following message:
The remote server returned an error: (400) Bad Request
The only solution which I found was to define the Factory class : System.ServiceModel.Activation.WebServiceHostFactory int the Svc definition markup.
I found this solution in the following link:
Send JSON to WCF 3.5 using Ajax
As I understood , defining the WebServiceHostFactory is only useful if you don't want to edit the web.config.
How can I make it work without defining the WebServiceHostFactory?
Notice that I succeed to get "json" content type POST message but not "application/json" content type.
The problem is that to use the raw programming model (using a Stream parameter), you'll need to tell WCF to not try to understand the request as JSON, instead just pass the raw request body to the parameter. You can do that using a WebContentTypeMapper. The post at http://blogs.msdn.com/b/carlosfigueira/archive/2008/04/17/wcf-raw-programming-model-receiving-arbitrary-data.aspx shows how this can be done. The solution with the factory works because it does that when it creates the endpoint.