Accessing Cross-Domain WCF-Webservice with jQuery or Javascript - javascript

I'm sorry to ask this question again, but I didn't find a fitting answer to my problem.
I try to build the following:
I want to connect to a WCF-Webservice with a standard Webpage. Both website and web service are hosted in an IIS on the same machine.
I enabled cross-domain to the web.config:
<webHttpBinding>
<binding name="webHttpBindingWithJSONP" crossDomainScriptAccessEnabled="true" />
</webHttpBinding>
And my AJAX request:
$.ajax({
type: "POST",
url: config.endpoints.db.production + "AddData/",
data: JSON.stringify(data),
contentType: "application/json",
dataType: "json",
crossDomain: true,
// processData: true,
success: function(data, status, jqXHR) {
// alert("success..." + data);
// loadingVisible(false);
// loadingFinished = true;
},
error: function(xhr) {
alert("failure..." + xhr.responseText);
// loadingFinished = true;
// loadingVisible(false);
}
});
After firing the request I got an "Access denied"-Error in the visual studio from jquery.
After searching in the web I found out that this is a very common cross-domain problem, but I didn't find a solution. I tried to set "crossDomain: true" in the ajax-request, tried to use jsonp (which worked for my GET-requests) but nothing helped.
Is there a proper way to solve this? I read that this problem might be solved with an ajax authentication. Is that correct and how can I achieve that?

To solve the problem do the following
Create a Global.asax and add following to enable Ajax cross-domain POST
public void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST,OPTIONS");
if ((HttpContext.Current.Request.HttpMethod == "OPTIONS"))
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
HttpContext.Current.Response.End();
}
}
}
At your service set
[AspNetCompatibilityRequirements(RequirementsMode =
AspNetCompatibilityRequirementsMode.Allowed)]
public class YourserviceName : YourserviceNameInterface

Related

400 Bad Request error when sending POST request to WCF service using JavaScript

I have a very simple self-hosted WCF application initialized this way:
Host = new WebServiceHost(this, new Uri(serviceAddress));
var binding = new WebHttpBinding();
binding.ReceiveTimeout = TimeSpan.MaxValue;
binding.MaxReceivedMessageSize = int.MaxValue;
binding.CrossDomainScriptAccessEnabled = true; // TODO: Remove this?
var endpoint = Host.AddServiceEndpoint(typeof(HttpService), binding, "");
Host.Open();
My HttpService class has this method:
[OperationContract, WebInvoke(Method = "*")]
public string Evaluate(string query)
{
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
if (WebOperationContext.Current.IncomingRequest.Method == "OPTIONS")
{
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Methods", "POST, PUT, DELETE");
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With");
WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Max-Age", "1728000");
return "";
}
else
{
try
{
return "12345";
}
catch (ProcessNotFoundException ex)
{
throw new WebFaultException<string>(ex.Message, System.Net.HttpStatusCode.NotFound);
}
catch (Exception ex)
{
throw new WebFaultException<string>(ex.Message, System.Net.HttpStatusCode.InternalServerError);
}
}
}
I added the CORS stuff above so that anyone can use this API from any website using JavaScript. And I'm trying to run some POST requests using fetch because once it works, I'll pass large amounts of data so a GET method won't work. Doing the following from JavaScript works OK:
fetch('http://localhost:8001/HeliumScraperService/Evaluate', { method: 'POST', headers: { "Content-Type": 'application/json' }, body: '1232'});
When I do that, my Evaluate method is called first with OPTIONS and then with POST, and an XML string is returned with no errors. The following also works (note the quotes inside the body):
fetch('http://localhost:8001/HeliumScraperService/Evaluate', { method: 'POST', headers: { "Content-Type": 'application/json' }, body: '"hello world"'});
Since both are valid JSON, I though any JSON string would work, so I did this but I got a 400 Bad Request error:
fetch('http://localhost:8001/HeliumScraperService/Evaluate', { method: 'POST', headers: { "Content-Type": 'application/json' }, body: '[10]'});
I thought maybe it wants a JSON that it can convert to my method arguments, but this also doesn't work (gives me a 400 error):
fetch('http://localhost:8001/HeliumScraperService/Evaluate', { method: 'POST', headers: { "Content-Type": 'application/json' }, body: JSON.stringify({ query: "hey" })})
So it can take numbers and strings but nothing else, and I have no idea why. I tried using all these other attribute combinations and got the exact same results:
[OperationContract, WebInvoke(Method = "*", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json)]
[OperationContract, WebInvoke(Method = "*", BodyStyle = WebMessageBodyStyle.Wrapped, RequestFormat = WebMessageFormat.Json)]
[OperationContract, WebInvoke(Method = "*", BodyStyle = WebMessageBodyStyle.Bare)]
[OperationContract, WebInvoke(Method = "*", BodyStyle = WebMessageBodyStyle.Wrapped)]
I just want to be able to send any string, without something looking into it and deciding whether it's good or bad, while also being able to do the CORS thing above. Is there a way to do this?
I found a workaround but I'm still not sure why it was letting me pass numbers and strings but not arrays, so if someone comes up with an actual explanation I'll be happy to accept that as an answer. My solution was to take a Stream instead of a string in my Evaluate method, and then read the stream like:
using (var memoryStream = new MemoryStream())
{
query.CopyTo(memoryStream);
var array = memoryStream.ToArray();
var lole = Encoding.UTF8.GetString(array);
}
I think the idea is that, since I'm doing the CORS thing and the browser sends an empty body when sending an OPTIONS request, WCF can't handle that as JSON so I have to handle everything myself and the way to do that is by taking a war Stream. Still no idea why it was letting me send numbers and strings (and booleans too BTW) but no arrays (or objects).
At first, the following definition only supports to accept the parameters by using request body, instead of the URL Params.
[OperationContract, WebInvoke(Method = "*")]
public string Evaluate(string query)
Moreover, the body style of the parameters is bare by default.
BodyStyle =WebMessageBodyStyle.Bare
It means that the server accepts the string without wrapping the name of the parameter.
body: '1232'
body: '"hello world"'
When we manually switch the value to BodyStyle=WebMessageBodyStyle.Wrapped, the server will accept the below form. Basically, your attempts are correct. There is just a small problem here.
{"value":"abcd"}
Here is my example, wish it is helpful to you.
Server contract.
[OperationContract]
[WebInvoke(Method ="*",BodyStyle =WebMessageBodyStyle.Wrapped,ResponseFormat =WebMessageFormat.Json)]
string GetData(string value);
JS
<script>
var para1 = { "value": "abcdefg" };
$.ajax({
type: "POST",
url: "http://localhost:8864/Service1.svc/getdata",
data: JSON.stringify(para1),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: OnSuccessCall,
error: OnErrorCall
});
function OnSuccessCall(response) {
console.log(response);
}
function OnErrorCall(response) {
console.log(response.status + " " + response.statusText);
}
</script>
About handing the CORS issue, I add an extra global.asax file.
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
{
HttpContext.Current.Response.AddHeader("Cache-Control", "no-cache");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With,Accept");
HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
HttpContext.Current.Response.End();
}
}
Here is a related document.
https://learn.microsoft.com/en-us/dotnet/api/system.servicemodel.web.webmessagebodystyle?view=netframework-4.8
Feel free to let me know if there is anything I can help with.

Angular - How to work with HTTPS using $http

just wanted to ask on how to deal with https + cross-domain url using $http?
I've used $.ajax to solve my problem, but the reason why I wanted to use $http more is because I have an $http interceptor since using $.ajax takes a lot more line of codes rather than having an interceptor to automate some process.
I've read similar posts [link1, link2, link3] but neither of them have solved my problem.
I have an attached screenshot of my console, I hope it makes sense:
.
The weird thing is the OPTION Method being used by $http which I have set it to POST
Here's my code snapshot:
$http({
method: 'POST',
url: '/HearingCentre',
data: {
Type: ['P', 'V']
},
success: function (res) {
console.log(res);
d.resolve(res);
},
error: function (res) {
console.log(res);
d.reject(res);
}
});
NOTE: I'm using an $http interceptor here so basically the URL will be appended with the base API URL
$.ajax version
$.ajax({
method: 'POST',
crossDomain: true,
url: 'https://ahtstest.hearing.com.au/services/api/HearingCentre',
data: {
Type: ['P', 'V']
},
success: function (res) {
console.log(res);
},
error: function (res) {
console.log(res);
}
});
Do anyone have a solution for this?
var userApp = angular.module('userApp', []);
userApp.controller('UserController', function ($scope,$http) {
//server call with angular
$scope.makeServerCall = function() {
var url = '/root/jsp/catalog/xml/getJsp.jsp?PatientId=9175;
$http({
crossDomain: true,
xhrFields: {withCredentials: false},
headers: {'Accept': 'application/json','Content-Type': 'application/json'},
method: 'GET',
url: url
}).then(function(response) {
console.log("Success: "+JSON.stringify(response));
},function(response) {
console.log("Error: "+JSON.stringify(response));
});
};
}
And make sure that at your server side you can accessible to cross domain reuqest. Lets say I am using Spring MVC so I have added one filter and configure cross domain request to allow as follow. If you using any other language lets say PHP, then you have to set response header.you can search for the same.
My Server filter Spring mvc
#Component
public class EcwCORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With,isAjaxRequest");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {
}
public void destroy() {
}
}

Crossdomain Ajax call to Service isn't working

I'm using ASP MVC and I'm trying to call a service that's on another one of my MVC websites.
I'm trying to use the following Ajax call.
function SomeFunction(decision) {
if (decision == false)
decision = "no";
else
decision = "yes";
var input = {
LogEventType:"PageView",
CurrentPage: "invitation/?decision=" + decision + "&id=" + 32323,
};
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "https://somewebsite.com/api/ClickStream/LogWebEvent/",
data: JSON.stringify(input),
crossDomain: true,
dataType: 'jsonp',
headers: {
'Access-Control-Allow-Origin': '*'
},
success: function (data) {
alert("We're awesome")
},
error: function () { console.log("Call to SomeFunction failed.") }
});
}
I don't get any visible errors and I also put breakpoints on the service and inside of the success/error function of the ajax call but it never reaches any of them.
Anyone see where I messed up?
EDIT:
Here is the Web Apis function I'm trying to access
[ActionName("LogWebEvent")]
[HttpPost]
public void LogWebEvent(ClickStreamEventDto data)
{
try
{
_clickstreamLogger.LogWebEvent(data);
}
catch (Exception ex)
{
}
}
Where ClickStreamEventDto is
public class ClickStreamEventDto: Analytics.IAnalyticEventDto
{
public string LogEventType { get; set; }
public string CurrentPage { get; set; }
}
When hitting cross domain sites, with either CORS or JSONP, make sure they are both HTTP or both HTTPS.
Make sure that when HTTPS is involved, that both site's certificates have been accepted. You may need to hit the HTTPS site in another window, and accept the certificate if there is no trust relationship to the issuer.

Ajax crossdomain request with jquery and BASIC-authentication?

I really appreciated if somebody could tell me what is wrong in that authentication? I take the authentication off from the server to test without it, but javascript is broken.
$('#btnSignIn').click(function() {
var username = $("#username").val();
var password = $("#password").val();
function make_base_auth(user, password) {
var tok = user + ':' + password;
var final = "Basic " + $.base64.encode(tok);
console.log("FINAL---->" +final);
alert("FINAL---->" +final);
return final;
}
$.ajax({
type: "GET",
contentType: "application/json",
url: "http://localhost:8080/SesameService/webresources/users/secured/login",
crossDomain: true,
dataType: "text",
async: false,
data: {},
beforeSend: function (xhr){
xhr.setRequestHeader('authorization', make_base_auth(username, password));
},
success: function() {
alert('Thanks for your signin in! ');
},
error: function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
alert(' Error in signIn-process!! ' +textStatus);
}
});
});
ERROR: If I take the
beforeSend: function (xhr){
xhr.setRequestHeader('authorization', make_base_auth(username, password));
-part off from the function, I can get into REST-service. I haven't got authentication on at the moment. Could this be the reason or shoud I have the authentication on in the server when using that header?
contentType: "application/json",
#GET
#Path("/secured/login")
#Produces({"text/plain"})
public String login() {
return "Is it working or not?";
}
When using beforeSend-part in JS, I got an error:
[Exception... "Failure" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: http://localhost:8383/Sesame/assets/js/jquery.min.js :: .send :: line 3" data: no] { message="Failure"
, result=2147500037
, name="NS_ERROR_FAILURE"
,
If I have understood correctly, "authorization + Basic " in header tells the Glassfish-server, that basic-authentication will be done. After authentication it goes to REST-service and in my case returns json-object to HTML5-client. HTML5-client is running in localhost:8383 and the rest services are running in localhost:8080.
If I run secured rest-services straight in localhost:8080, it is working, so that is not the problem. The problem is that when I am using or trying to use rest-services from different domain localhost:8383, I get get the JS-error console.log('----ERROR IN ------Siging IN----------'); I am not 100 % sure, but I think that the problem is 401 unauthorized, so cross domain authentication is not working.
Shoud I insert crossDomain: true? I have seen it somewhere, maybe that can be the case?
In serverside I've got the filter:
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>util.CrossOriginResourceSharingFilter</param-value>
</init-param>
public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
response.getHttpHeaders().putSingle("Access-Control-Allow-Origin", "*");
response.getHttpHeaders().putSingle("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.getHttpHeaders().putSingle("Access-Control-Allow-Headers", "content-type");
return response;
}
QUESTIONS
1) is it possbile that if server is not configured for authentication and I still use the authorization-header breaks the app and cause the error?
2) What that JS-error means?
Cheers,
Sami

Cross domain jQuery ajax calls not working

I'm having trouble with this code and I can't seem to get it to work. The typical error that I get back for this call is a "Failed to load resource: the server responded with a status of 401 (Unauthorized) " .
$('#btnZendesk').click(function () {
$.ajax({
url: "https://flatlandsoftware.zendesk.com/api/v2/topics/22505987.json",
type: 'GET',
crossDomain: true,
xhrFields: {
withCredentials: true
},
cache: false,
dataType: 'jsonp',
processData: false,
data: 'get=login',
timeout: 2000,
username: "test#test.com",
password: "test",
success: function (data, textStatus, response) {
alert("success");
},
error: function (data, textStatus, response) {
alert(data);
}
});
Problem is that the resource you are trying to access is protected with Basic Authentication.
You can use beforeSend in jQuery callback to add a HTTP header with the authentication details e.g.:
beforeSend: function (xhr) {
xhr.setRequestHeader ("Authorization", "Basic XXXXXX");
}
Alternatively you can do it using jQuery ajaxSetup
$.ajaxSetup({
headers: { 'Authorization': "Basic XXXXX" }
});
EDIT
A few links to the mentioned functions
jQuery.ajaxSetup
jQuery.ajax
EDIT 2
The Authorization header is constructed as follows:
Username and password are joined into a string "username:password" and the result string is encoded using Base64
Example:
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
I too got this problem and somehow all solutions from internet either failed or were not applicable due to client webservice restrictions (JSONP, XDR, CORS=true)
For this, I added an iframe in my page which resided in the client;s server. So when we post our data to the iframe and the iframe then posts it to the webservice. Hence the cross-domain referencing is eliminated.
We added a 2-way origin check to confirm only authorized page posts data to and from the iframe.
Hope it helps
<iframe style="display:none;" id='receiver' name="receiver" src="https://iframe-address-at-client-server">
</iframe>
//send data to iframe
var hiddenFrame = document.getElementById('receiver').contentWindow;
hiddenFrame.postMessage(JSON.stringify(message), 'https://client-server-url');
//The iframe receives the data using the code:
window.onload = function () {
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function (e) {
var origin = e.origin;
//if origin not in pre-defined list, break and return
var messageFromParent = JSON.parse(e.data);
var json = messageFromParent.data;
//send json to web service using AJAX
//return the response back to source
e.source.postMessage(JSON.stringify(aJAXResponse), e.origin);
}, false);
}

Categories

Resources