WCF javascript call cross domain - javascript

I am new to javascript and I have a problem that is giving me hard times.
I want to put in a page a tracking javascript. This script will call a wcf service to track client browser information, page visited and timestamp. The problem is that I get: "405 Method Not Allowed" error on javascript call. The service will be on another domain. Now I test this on localhost. The service is working fine because I can call it from a new page in browser. Did you experienced the same problem? How can I fix this?
My javascript code is:
Wcf service code:
[ServiceContract(Name = "CustomersAssistantService", Namespace = "CustomersAssistantService")]
public interface ICustomersAssistantService
{
[OperationContract]
[WebGet]
string DoWork();
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Wrapped, ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
string Sum(int n1, int n2);
}
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class CustomersAssistantService : ICustomersAssistantService
{
public string DoWork()
{
return "work done";
}
public string Sum(int n1, int n2)
{
return (n1 + n2).ToString();
}
}
Javascript call:
function CallWcf1(){
var _I = this;
var url = "http://localhost:58399/CustomersAssistantService.svc/customersAssistantService/";
var methodUrl = _I.serviceUrl + 'dowork';
$.ajax( {
url: methodUrl,
data: '',
type: "GET",
processData: false,
contentType: "application/json",
timeout: 10000,
dataType: "text", // not "json" we'll parse
success:
function(res)
{
if (!callback) return;
// *** Use json library so we can fix up MS AJAX dates
var result = JSON2.parse(res);
// *** Bare message IS result
if (bare)
{ callback(result); return; }
// *** Wrapped message contains top level object node
// *** strip it off
for(var property in result)
{
callback( result[property] );
break;
}
},
error:
function(xhr)
{
if (!error) return;
if (xhr.responseText)
{
var err = JSON2.parse(xhr.responseText);
if (err)
error(err);
else
error( { Message: "Unknown server error." })
}
return;
}
});
}
Am I doing something wrong?
Thanks a lot, Radu D

You're running into the Same Origin Policy. Ajax requests are limited to talking to the same origin; cross-domain requests will typically be denied.
Depending on your needs, there are various approaches:
If your users will be using only quite modern browsers (so, not IE7), you could implement Cross Origin Resource Sharing on your server. This is a standard from the W3C that allows a server to open up access to its resources cross-origin, but it's only supported in modern browsers. (And on IE, it's supported, but not via the usual XMLHttpRequest object; instead, you have to use the XDomainRequest object.)
You could implement a JSON-P interface.
If all you want to do is send a notification to the other domain, you can just load a resource from it (an img, a script, whatever) by adding those elements with the relevant src attribute; this means, though, that you're limited to using the GET method which is supposed to be for idempotent resources.

As far as I know, request for WCF must use the POST method, so change to:
type: "POST",
In the AJAX call.

Related

Why ajax call send null to servlet? [duplicate]

This question already has answers here:
How should I use servlets and Ajax?
(7 answers)
HTTP request parameters are not available by request.getAttribute()
(1 answer)
Closed 3 years ago.
I'm trying to send the username to the servlet through an ajax call to check its availability, but the servlet show a null pointer exception.
I've also tried with the XMLHttpRequest instead of $.ajax.
This is my Javascript file:
$(document).ready(function() {
$("#reg-form").submit(function() {
var res = true;
if (!testUser()) {
res = false;
$("#erruser").css("display", "block");
$("#username").addClass("errclass");
} else {
$("#erruser").css("display", "none");
$("#username").removeClass("errclass");
}
return res;
});
});
function testUser() {
var el = $("#username").val();
var b = false;
$.ajax({
type: "POST",
url: "CheckUserServlet",
data: { user: el },
dataType: "json",
success: function(bool) {
alert(bool);
if (bool == "si") b = true;
},
error: function() {
alert("errore");
}
});
return b;
}
This is my servlet doPost method:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username=request.getAttribute("user").toString();
System.out.println("username servlet= "+username);
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
if (!ud.doRetrieveByUser(username)) {
response.getWriter().write("si");
return;
}
response.getWriter().write("no");
return;
}
Thanks!
CLIENT SIDE
Your test user function will always return false regardless of if the server is operating correctly because $.ajax() is an async function. There are a few ways around this. In your case, without knowing much more about what you are building, I would suggest removing the return value from your test user function, and moving your logic into the success/failure areas in the ajax callback. This way, the ajax call just does it's thing and lets the success function modify your page however you want.
function testUser() {
var el = $("#username").val();
$.ajax({
type: "POST",
url: "CheckUserServlet",
data: { user: el },
dataType: "json",
success: function(bool) {
alert(bool);
// put logic here
if (bool === "si") {
$("#erruser").css("display", "block");
$("#username").addClass("errclass");
} else {
$("#erruser").css("display", "none");
$("#username").removeClass("errclass");
}
},
error: function() {
alert("errore");
}
});
}
I would also suggest setting up the initial state of your page so that while this request is happening the user is shown something that makes sense. Answer the following question: "what do I show my users when the page does not know yet if it is a test user" and then set the initial state of the page accordingly
SERVER SIDE
I've always found interacting with java & JSON data a bit clunky, and your issue seems like something I've grappled with in the past.
Your question is "why is ajax sending null to the server". It may seem like that but what is really happening is that your server doesn't understand how to interpret the data it is getting. Take a look at this question about getting a JSON payload.. You need to tell your server how to parse the data coming from the client. If you were to inspect the data being sent, I would expect it looks something like this {"user":"blablabla"}.
If you have a class definition already, use that. For this I am using something that looks like this:
public class UserRequest {
String user;
}
// get the body as a string. Requires https://commons.apache.org/proper/commons-io/
String body = IOUtils.toString(request.getReader())
// parse the json with gson. Requires https://github.com/google/gson
Gson g = new Gson();
User u = g.fromJson(body, UserRequest.class);
String username = u.user;

ASP.NET Core - Client-side AJAX not working for IE11/Edge

I've literally been researching for 5+ days for a problem that only exists within IE and Edge (works in Chrome, FireFox, etc). I've gone through pretty much all of the links and tried nearly everything, but it seems that a lot of the content/answers out there from older posts are very outdated.
Background: I created an ASP.NET Core web service - it is running on some port on localhost. Ideally, I'd have my users open an locally-residing html document (meaning living on their local filesystem and not hosted on a server) that pings the web service through AJAX (that returns some JSON) - allowing for that content from the web service to be displayed in the html document. It literally works in all browsers except IE and Edge (I'm using IE11).
Here is my Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
...
services.AddCors(o => o.AddPolicy("ContentServices", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
services.AddMvc();
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
app.UseCors("ContentServices");
...
}
Here is my SearchController.cs:
[EnableCors("ContentServices")]
public SearchResponse Query(string index, string input, SearchType searchType, int page = 0, int pageSize = 20)
{
SearchResponse sr = new SearchResponse;
...
return sr;
}
Here is my JavaScript code:
function WebService(url, successMsg, errorMsg) {
return $.ajax({
url: searchDestinationUrl + url,
type: "GET",
crossDomain: true,
dataType: "json",
success: function (results) {
return results;
},
error: function (xhr, status, error) {
console.error(error + " - " + errorMsg);
}
});
}
function RunGeneralSearch(input, options) {
var search = WebService("search/query?index=" + index + "&input=" + input + "&page=" + options.content.pageIndex + "&pageSize=" + options.content.pageSize, "", "Search failed.")
.then(function (response) {
var searchResults = [];
searchResults.content = [];
searchResults.contentTotal = response.total;
searchResults.clientPaging = false;
var results = response.results;
if (results.length > 0) {
var Search = MadCap.CreateNamespace("WebHelp.Search");
for (var i = 0; i < results.length; i++) {
var result = results[i];
var rank = result["rank"];
var title = result["title"];
var link = result["link"];
var linkUrl = GetLinkUrl(link);
var abstractText = result["abstractText"];
var searchResult = new Search.SearchResult(rank, title, linkUrl.FullPath, unescape(abstractText));
searchResults.content.push(searchResult);
}
}
return searchResults;
});
return search;
};
}
Things I've tried:
Using $.support.cors = true will work for IE with the exception
that it will prompt an warning for every ping that is made to the
web service regarding security: "This page is accessing information that is not under its control. This poses a security risk. Do you want to continue?" This is absolutely annoying, and I will not have my customers modify their security settings for this. This also does not work for Edge at all.
Changing the crossDomain attribute in the AJAX call to false - behavior is the same as the above except that it will not work for all other browsers now.
I've read a lot about using the dataType: 'jsonp' - but as far as I know, it's deprecated and only supports GET requests. So I did not try this and want to stay away from it.
I've also read that because I'm trying to access the web service through a locally-residing unhosted file (i.e. url is file:// instead of http://) that it is technically not supported as an origin - so allowing any origin wouldn't help in this case. I'm not sure how true this is or even how to test this. Regardless, I have not tried serving the html file over localhost because I do not want to require my clients to have to do this just for IE/Edge when it works completely fine with all other browsers.
I've tried using an XMLHttpRequest instead of and AJAX call. It requires me to then do JSON.parse(result); but seems to work perfectly for all browsers including IE except Edge...
I understand to some extent about CORS and the limitations of the same origin policy, but I absolutely don't understand what the difference is between Microsoft browsers and the rest.
Please help!

Can't make JSONP call

this problem is really driving me crazy... I have 2 java web application: appA that publish a REST service which returns some object (which contains an image) in json format, and appB that consumes the service and shows the image. In my local machine it works (though I see the CORS error) but now I'm using a web environment (Layershift) and I'm getting an error "NetworkError: 404 Not Found - http://app-demo.j.layershift.co.uk/myservice/get/c80e9306105f4448?callback=myCallback&_=1477167892656"
I've been reading lot of examples of CORS and JSONP but I can't find what's wrong with my code, can anybody give a hint?
Controller in appA:
#RestController
public class MyController {
#RequestMapping(value="/myservice/get/{somevar}")
public #ResponseBody MyObject getMyObject (#PathVariable String somevar, HttpServletRequest request, HttpServletResponse response) {
MyObject obj = new MyObject();
//some logic
return obj;
}
}
ControllerAdvice in appA:
#ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice() {
super("myCallback");
}
}
javascript code with jsonp call in appB:
(function($) {
function myCallback(data) {
if (data.status == "OK") {
//shows the image contained
}
}
$.fn.callWithJsonP = function(somevar) {
$.ajax({
url: "myservice/get/" + somevar,
type: 'GET',
dataType: "jsonp",
jsonpCallback: "myCallback"
});
};
})(jQuery);
Any help would be appreciated, regards
PS: also tried adding the directive "Header add Access-Control-Allow-Origin "*"" in Apache, but same result.
Here's how to perform a JsonP ajax call using Jquery.
E.g.:
$.ajax({
url: "http://localhost/ny-app/getJSfunction",
jsonp: "myfunctionName",
dataType: "jsonp",
data: soomedata,
success: function( response ) {
console.log( response ); // server response
}
});
Handle this call on the server side, and return a function encapsulated javascript code that returns some data. The returned javascript will look like this:
function myfunctioname(){
// some code
return 'whatever you want' ;
}
JQuery ajax will automatically execute the returned javascript function.
Hope this helps.
Finally can make the JSONP call, didn't solve my problem, but I'm able to make the call, so I'll post the changes.
First of all, in the main controller I'm not using #ResponseBody, I've changed it like this:
#RestController
public class MyController {
#RequestMapping(value="/myservice/get/{somevar}")
public MappingJacksonValue getMyObject (#RequestParam String callback, #PathVariable String somevar, HttpServletRequest request, HttpServletResponse response) {
MyObject obj = new MyObject();
//some logic
MappingJacksonValue value = new MappingJacksonValue(obj);
value.setJsonpFunction(callback);
return value;
}
}
Notice the return type is MappingJacksonValue (thanks to JSONP with Spring 3.0 and Jackson), and the javascript code is this:
$.fn.callWithJsonP = function(somevar) {
var url = "/myservice/get/" + somevar + "?callback=myCallback";
$.getJSON(url, function(data) {
if (data.value.status == "OK") {
//show the image contained
}
});
}
There was no need to define a function called "myCallback". I've tested in my local machine and it is working.
Thanks everybody for your suggestions.

Google Calendar API Freebusy JQuery $.post gets 400 (Bad Request) Error

I am new to javascript, JQuery and Google API, so the answer to this question may be a very simple thing that I am overlooking. I've checked all available Google Calendar Freebusy Questions on this site, but I can't manage to make their answers fit my code in any way.
I am trying to write a script for an html page that checks a public calendar's freebusy query. Google says that the HTTP Request should be
POST https://www.googleapis.com/calendar/v3/freeBusy
with a request body of
{
"timeMin": datetime,
"timeMax": datetime,
"timeZone": string,
"groupExpansionMax": integer,
"calendarExpansionMax": integer,
"items": [
{
"id": string
}
]
}
My current html page includes the latest jquery library, and the script I'm writing. Calling the script on the page results in a Failed to load resource: the server responded with a status of 400 (Bad Request) error. A further dig into the error information returns a parse error with "This API does not support parsing form-encoded input."
My script looks like this:
(function ($) {
$.GoogleCalendarFreebusy = function (options) {
var defaults = {
apiKey: '[projectkey]',
getID: '[id]#group.calendar.google.com',
element: '#div'
};
options = $.extend(defaults, options);
$.post('https://www.googleapis.com/calendar/v3/freeBusy?key=' + options.apiKey,
{"items":[{"id": getID }],"timeMin":"2015-04-10T14:15:00.000Z","timeMax":"2015-04-20T23:30:00.000Z"}, "null", "json")
.done(function(data) {
loaded(data);
});
function loaded(data) {
var status = data.calendars[getID].busy;
console.log(status);
if(status.length !== 0 ) {
for(var i = 0; i < status.length; i++) {
var statusEntry = status[i];
var startTime = statusEntry.start;
var endTime = statusEntry.end;
}
var now = new Date().toISOString();
var element = options.element ;
var name = element.substr(1);
if (now > startTime && now < endTime){
$(options.element).append( 'Available!');
}
else {
$(options.element).append( 'Unavailable!');
}
} else {
$(options.element).append('Unavailable!');
}
}
};
})(jQuery);
My request is receiving the proper response in the Google Explorer "Try It", so I think it may be javascript error/json request I'm overlooking? Thanks in advance for your help and advice.
Google Calendar API Post requests need to have the content-type specified as JSON to avoid the above error. Processing the POST as an AJAX request with contentType specified solves this error.
$.ajax({
url: 'https://www.googleapis.com/calendar/v3/freeBusy?key=' + options.apiKey,
type: 'POST',
data: '{"items":[{"id": "[id]#group.calendar.google.com"}], "timeMin": "2015-04-10T14:15:00.000Z", "timeMax": "2015-04-20T23:30:00.000Z"}',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: 'null'
})
Thank you for the suggestions!

I can't to process response to a jsonp request

I have url http://translate.google.ru/translate_a/t?client=x&text=enter text&sl=en&tl=pl
If you will go through this link in response you will have js file
with text:
{"sentences":[{"trans":"wprowadzania tekstu","orig":"enter text","translit":"","src_translit":""}],"src":"en","server_time":80}
I created ajax request
function GoogleTranslateItem(sourceText, langFrom, langTo) {
$.ajax({
url: 'http://translate.google.ru/translate_a/t',
data: { client: "x", text: sourceText, sl: langFrom, tl: langTo },
dataType: 'jsonp',
jsonpCallback: "getData",
success: function (data) {
alert("Success");
}
});
function getData(data) {
var dataJson = data;
alert('bingo');
}
when the answer comes from server. I can't to process it
in browser shows js error.
Syntax error at line 1 while loading:
{"sentences":[{"trans":"вход вых
------------^
expected ';', got ':'
Linked script compilation
How can i process this response?
I think you should take a look at this (http://javascriptweblog.wordpress.com/2010/11/29/json-and-jsonp/)
var jsonp = {
callbackCounter: 0,
fetch: function(url, callback) {
var fn = 'JSONPCallback_' + this.callbackCounter++;
window[fn] = this.evalJSONP(callback);
url = url.replace('=JSONPCallback', '=' + fn);
var scriptTag = document.createElement('SCRIPT');
scriptTag.src = url;
document.getElementsByTagName('HEAD')[0].appendChild(scriptTag);
},
evalJSONP: function(callback) {
return function(data) {
var validJSON = false;
if (typeof data == "string") {
try {validJSON = JSON.parse(data);} catch (e) {
/*invalid JSON*/}
} else {
validJSON = JSON.parse(JSON.stringify(data));
window.console && console.warn(
'response data was not a JSON string');
}
if (validJSON) {
callback(validJSON);
} else {
throw("JSONP call returned invalid or empty JSON");
}
}
}
}
The response from http://translate.google.ru/translate_a/t?client=x&text=entertext&sl=en&tl=pl i JSON, not JSON-P. Accessing JSON-data this way is against the cross-site policies, as the browsers prevent such responses to be returned to the client.
As you are allowed to include scripts from other domains, JSON-P is a way of transfering data as javascript (not JSON). You need to find an API supporting JSON-P (I'm not sure if the Translate API supports JSON-P) or create a proxy on the same domain as your client application to access the JSON data.
Read more about the JSON-P protocol here: http://json-p.org/
To create a proxy, you'll need to implement a service that fetches the content of the Translate API and reprint it in the response.
Example:
/jsonProxy?text=foo
Should return the contents of http://translate.google.ru/translate_a/t?client=x&text=entertext&sl=en&tl=pl
...but you won't have to access it from another domain.
I think the MIME type of the response should be 'application/json'

Categories

Resources