I am in the process of testing some web services on my local machine. Because the test page sits on the root at port 80, and the web-services on different ports, I get the following error from Chrome's diagnostic tool:
XMLHttpRequest cannot load http://PCNAME:8083/PackageSearch. Origin http://PCNAME is not allowed by Access-Control-Allow-Origin.
After doing some searching, I came across the CORS Features of ServiceStack, and put the following attribute on my Web Service:
[EnableCors(allowedMethods: "GET, POST")]
However, the error still persists. This is the ajax call:
function packageSearch(tourCode) {
var searchUrl = url + 'PackageSearch';
var searchData = {
TourCode: tourCode,
};
$.ajax(searchUrl,{
data : JSON.stringify(searchData),
type: 'POST',
contentType: 'application/json, charset=utf-8',
dataType: 'json',
success: function (result) {
oTable.fnClearTable();
}});
};
Where url is http://PCNAME/.
EDIT
I have even set up the following during the Configuration stage:
public override void Configure(Funq.Container container)
{
Plugins.Add(new CorsFeature());
RequestFilters.Add((httpReq, httpRes, requestDto) =>
{
if (httpReq.HttpMethod == "OPTIONS")
httpRes.End();
});
base.SetConfig(new EndpointHostConfig
{
DefaultContentType = "application/json",
GlobalResponseHeaders = {
{ "Access-Control-Allow-Origin", "*" },
{ "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS" },
{ "Access-Control-Allow-Headers", "Content-Type, origin, accept" },
}
});
});
} // Configure
You need a ContentType specified and may have to do a preflight message (OPTIONS) to do the handshake that will allow you to proceed with a cross domain call.
I thought this code looks suspicious:
if (httpReq.HttpMethod == "OPTIONS")
httpRes.End();
I think although you are setting all header responses to send CORS headers, you may be short-circuiting the HTTP response headers - they never get sent. You could verify if you use Fiddler to check the exact http response headers sent back.
See this SO answer: ServiceStack returns 405 on OPTIONS request
They send the headers before calling httprequest.end().
You probably have to do:
httpRes.AddHeader("Access-Control-Allow-Origin", "*");
... before calling response.end()
Related
I got CORS issue when I am trying to make post request to c# web api.
Below is the error:
Access to XMLHttpRequest at 'api url in different domain' from origin
'client url' has been blocked by CORS policy: Response to preflight
request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
The same API call with GET request is working.
Both sites are deployed with windows authentication enabled.
I am using Angular io, 1.7.** with typescript. Below is how I call the api with post request in typescript.
let httpHeader = new HttpHeaders();
httpHeader = httpHeader.set('Content-Type', 'application/json');
this.http.post(this.apiUrl, this.bodyObject,
{
headers: httpHeader,
withCredentials:true
}).pipe(map(response => {
return response;
})).subscribe(result => {
console.log(result);
}, function (err) {
console.log(err);
});
I first thought that it might be api cors issue and tested with below jquery code and it is working.
<script>
$.ajax({
url:apiUrl,
type: 'post',
data: {
serviceName: "Country"
},
xhrFields: {
withCredentials: true
},
dataType: 'json',
success: function (data) {
console.info(data);
},
error: function (err) {
console.error(err);
}
});
</script>
So I think, I have something to do with my angular code. Can you guy highlight me what could be the issue.
try to add this in $.ajax
headers: {
'Access-Control-Allow-Origin': '*',
'Content-Type':'application/json'
},
crossDomain: true,
Is there a way how can I can get request origin value in the api controller when I'm calling some api endpoint with ajax call?
For example I'm making this call from www.xyz.com:
$http({
url: 'http://myazurewebsite.azurewebsites.net/api/ValueCall/CheckForExistingValuers',
method: "GET",
params: { loanID: $scope.loanIdPopup }
}).success(function (data) {
}).error(function (data) {
});
Once on the api side, how can I get the www.xyz.com value?
CORS is working properly.
What you're looking for is probably the origin-header. All modern browsers send it along if you're doing a cross domain request.
In an ApiController you fetch it like so:
if (Request.Headers.Contains("Origin"))
{
var values = Request.Headers.GetValues("Origin");
// Do stuff with the values... probably .FirstOrDefault()
}
You can grab it from the API methods via current HTTP request headers collection:
IEnumerable<string> originValues;
Request.Headers.TryGetValue("Origin", out originValues)
var originValue = Request.Headers["Origin"].FirstOrDefault();
// or
StringValues originValues;
Request.Headers.TryGetValue("Origin", out originValues);
On the user-side, I have a javascript code, that POSTs to the server via JQuery.ajax. On the server-side, I receive the request and handle it, but the user-side script does not wait for the servers response and calls error: getFail (see below) function.
EDIT: problem was due to having the server and the user-side on different domains, in my case different localhosts. See detailed answer by #Jacob below.
How did I check if the user-side waits for the server or not?
I've just run the server in debug mode, and set a breakpoint where it receives the POST-ed values. And the user-side already called the error function.
Could anybody tell me, why it is not working "properly" or what and where is the source of error?
What I suspect to be one of the sources of the "improper" behavior is the dataType parameter of the JQuery.ajax function. It is supposed to specify the expected type of the return value from the server. I am not sure what to set this, so I did not specify hoping the default will find out.
[For the dataType parameter] If none is specified, jQuery will try to infer it based on the MIME type of the response. Possible types are: xml, html, script, json, jsonp, text, multiple. (Source: http://api.jquery.com/jQuery.ajax/, JQuery docs).
Here are the codes:
User-side javascript code:
function logIn() {
try {
alert('try in logIn');
jQuery.ajax({
type: "POST",
url: "http://localhost:8080/Token",
cache: false,
data: {
"grant_type": "password",
"username": document.getElementById("username").value,
"password": document.getElementById("password").value
},
contentType: "application/x-www-form-urlencoded", // data type sent to server
// dataType: "json", // data type expected from server <- THIS may be a source of the problem
success: getSuccess,
error: getFail
});
} catch (e) {
alert(e);
}
function getSuccess(data, textStatus, jqXHR) {
alert(data.Response);
};
function getFail(jqXHR, textStatus, errorThrown) {
//jqXHR.status is 0
//textStatus is "error"
//errorThrown is empty
alert(jqXHR.status); // <-- THIS is what gets called, instantly after the post.
};
};
Server-side C# code:
public class ApplicationOAuthServerProvider
: OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(
OAuthValidateClientAuthenticationContext context)
{
await Task.FromResult(context.Validated()); // break-point was here
}
// This is called by await Task.FromResult(context.Validated())
public override async Task GrantResourceOwnerCredentials(
OAuthGrantResourceOwnerCredentialsContext context)
{
// Here manager will have correct values from the POST-ed data
var manager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var user = await manager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError(
"invalid_grant", "The user name or password is incorrect.");
context.Rejected();
return;
}
foreach (var userClaim in user.Claims)
{
identity.AddClaim(new Claim(userClaim.ClaimType, userClaim.ClaimValue));
}
context.Validated(identity);
}
}
Seeing what the actual error is that you're getting back will confirm/disprove this, but I've seen CORS issues to blame for this. Depending on how the call is being made and what the server supports, you may have a preflight request that is handled by the server as though it was the full request, and then the client can reject the response because it wasn't cross-origin authorized.
I'm not sure what your server-side framework is, but this may help if indeed this is a cross-origin issue:
CORS with WebAPI for XmlHttpRequest
I have a hard Microsoft Visual Studio 2008, I want to make cross domain query from your web service to a WCF service, but it does not work.
Ajax code on a web page:
$.ajax (
url: "http:/сите.com/ApplicationController.svc/HelloPost/"
type: "POST",
dataType: "json",
contentType: "application/json",
success: function (data) {
alert (data);
},
error: function (jqXHR, textStatus, errorThrown) {
alert (jqXHR textStatus errorThrown);<br/>
}
});
But my WCF service:
[OperationContract]
[WebInvoke (Method = "POST", UriTemplate = "HelloPost /", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
[JSONPBehavior (callback = "callback")]
String GetPostHello (Stream data);
public String GetPostHello (Stream data)
{
HttpContext.Current.Response.AddHeader ("Access-Control-Allow-Origin", "*");
if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
{
HttpContext.Current.Response.AddHeader ("Access-Control-Allow-Methods", "GET, POST");
HttpContext.Current.Response.AddHeader ("Access-Control-Allow-Headers", "Content-Type, Accept");
HttpContext.Current.Response.AddHeader ("Access-Control-Max-Age", "1728000");
HttpContext.Current.Response.End ();
return null;
}
return "Hello";
}
When a GET request with the domain it works, but try to make a POST request returns this header:
Content-Type application/json
Accept application/json, text/javascript, */*;q=0.01
Help, what could be the problem! Thank you!
For POST requests to be made cross-domain by browsers which support CORS (which is what you're using with the Access-Control headers), prior to the request the browser first sends a preflight request, which is a HTTP OPTIONS request, asking the server whether is ok to send the POST request to it. You can either add another operation which responds to the OPTIONS request, or you can implement full CORS support for WCF - it's not too simple, but I've wrote about it on http://blogs.msdn.com/b/carlosfigueira/archive/2012/05/15/implementing-cors-support-in-wcf.aspx with the steps required to make this work.
This is my jquery code to call web api
var request = {
RequestId: "123",
DeviceId: "ACU-B2-01-R1",
AccessType: "Unlock",
LoginId: "tester",
Password: "tester"
};
$.ajax({
url: 'http://localhost:55208/api/accesspanel',
type: 'POST',
data: JSON.stringify(request),
dataType: 'jsonp',
contentType: "application/json;charset=utf-8",
success: function (data) {
alert("success");
alert(JSON.stringify(data));
},
error: function (x, y, z) {
alert(x + '\n' + y + '\n' + z);
}
});
When I run this code, nothing happens. Neither the success nor error block gets fired. After checking in the debug console of chrome, this is the error record:
GET http://localhost:55208/api/accesspanel?callback=jQuery18203847100134007633_…22,%22LoginId%22:%22tester%22,%22Password%22:%22tester%22}&_=1364916423737 405 (Method Not Allowed)
send jquery.min.js:2
p.extend.ajax jquery.min.js:2
(anonymous function)
I am, however, able to call my web api method successfully using C# code, which looks like this:
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:55208/");
var request = new DeviceAccessRequest
{
RequestId = Guid.NewGuid().ToString(),
DeviceId = "ACU/B2/01/R1",
AccessType ="Unlock",
LoginId = "tester",
Password = "tester" ,
};
var response = client.PostAsJsonAsync("api/accesspanel", request).Result;
if (response.IsSuccessStatusCode)
{
var deviceAccessResponse = response.Content.ReadAsAsync<DeviceAccessResponse>().Result;
}
}
And this is my web api method:
[HttpPost]
public HttpResponseMessage PostDeviceControl(DeviceAccessRequest deviceAccessRequest)
{
var deviceAccessResponse = new DeviceAccessResponse(deviceAccessRequest.RequestId)
{
Status = "OK"
};
var response = Request.CreateResponse<DeviceAccessResponse>(HttpStatusCode.OK, deviceAccessResponse);
return response;
}
The reason you are seeing a GET request in your console is because you specified dataType: 'jsonp' which works only with GET requests (the reason for that is because jQuery translates this to a <script> tag pointing to the url you specified). If you want to be doing cross domain AJAX with other verbs than GET you cannot use JSONP. If you need to use other verbs (such as POST in your case) you have 2 options:
CORS. Take a look at the following video which illustrates how you could enable CORS on your Web API. Obviously for this to work your client side browser need tio support it
Server side bridge on your domain. The idea here is to have some server side script on the domain hosting your javascript code that will send the HTTP request to the API and return the response to the client. Then you will send a regular AJAX POST request to your own domain