How to enable CORS in Chrome. Preflight 405 error - json

I have a wcf restful service that I need to get working in Chrome. I've read around and added all sorts of headers, etc but nothing is working. The POST works fine in IE, but when I get to Chrome, I get a 405 error on the OPTIONS request telling me that the method is not allowed. I've read where some people have added stuff to their global.asax file, but really don't feel like that should be necessary. I don't even have a global asax file and creating one just for the sole purpose of getting to work in chrome for development only seems crazy.
I'm using Aurelia's HTTP-Client library which uses just a simple XMLHttpRequest. Here's how it's configured:
this.Client = new HttpClient()
.configure(x => {
x.withBaseUrl("http://localhost/MyServices/MyService.svc/")
});
And I'm making the call like so:
this.Client.post("PostData/" + name,
{
version: 1
})
.then(resp => {
console.log(resp);
})
Here are the headers that I have added in my WCF Restful service WebConfig:
<add name="Access-Control-Allow-Origin" value="*" />
<!-- Not sure if these even do anything -->
<add name="Access-Control-Allow-Headers" value="Content-Type, Accept, X-Requested-With" />
<add name="Access-Control-Allow-Methods" value="OPTIONS, GET, POST" />
Then in my service itself:
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "/PostData/{name}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
Data myData(byte id, string name);
Like I said above, it works fine in IE, but chrome gives me a preflight error. Here's a snippet of the fiddler response:
HTTP/1.1 405 Method Not Allowed
Entity
Content-Length: 1565
Content-Type: text/html; charset=UTF-8
Security
Access-Control-Allow-Headers: Content-Type, Accept, X-Requested-With
Access-Control-Allow-Methods: OPTIONS, GET, POST
Access-Control-Allow-Origin: *
Allow: POST
I also find that if I add x.withHeader("Content-Type", "application/json; charset=utf-8") to my Http-Client configuration, Chrome doesn't even do a GET request. It throws the error.
To sum it all up, I can do GET requests but not POST requests in Chrome. How do I fix it?

Related

Cannot make POST request with JSON from react/axios to restify server

I have a restify set up like this:
var restify = require('restify');
const server = restify.createServer();
//server.use(restify.plugins.acceptParser(server.acceptable)); // [1]
server.use(restify.plugins.queryParser());
server.use(restify.plugins.bodyParser());
server.use(function(req, res, next) {
console.log(req); // <-- Never see the POST from React here
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', '*');
res.setHeader('Access-Control-Allow-Methods', '*');
next();
});
I define a bunch GET and POST routes and so far it worked perfectly fine. I called the server from an Android application, Python scripts and for testing simply using curl. No issues at all. Neat!
However, now I've implemented a Web application with React and want to make a request to the restify API using the axios package. GET requests are fine, so I exclude any typos in the URL or such things.
But a POST request like the following won't work:
var data = {"test": "hello"};
axios.post("http://.../api/v1/message/question/public/add", data)
.then(function (response) {
console.log("Test question sent!");
})
.catch(function (error) {
console.log(error);
});
When I check with the browser developer tools, I can see that the browser is trying to make an OPTIONS request (not a POST) to that URL. I assume, from what I've read, that is because the browser is making a "preflighted request". The problem is that I get an 405 Method Not Allowed error:
Request URL: http://.../api/v1/message/question/public/add
Request method: OPTIONS
Remote address: ...
Status code: 405 Method Not Allowed
Response headers (178 B)
Server: restify
Allow: POST
Content-Type: application/json
Content-Length: 62
Date: Sat, 09 Sep 2017 08:16:32 GMT
Connection: keep-alive
Request headers (485 B)
Host: ...
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linu…) Gecko/20100101 Firefox/55.0
Accept: text/html,application/xhtml+xm…plication/xml;q=0.9,*/*;q=0.8
Accept-Language: en-ZA,en-GB;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Origin: http://...
DNT: 1
Connection: keep-alive
But why? I allow all Access-Control-Allow-Methods in restify. Everything works, except from POST requests and only when they come from the browser (with the React Web app). I think it's because of the OPTIONS request, but I have no idea how to handle it.
By the way with JSON.stringify(data), the POST requests gets through, but the API expects Json and not a string. And since with all other means it works perfectly fine, I don't want to change the restify code just to accommodate this issue.
[1] If I use this line, I get the following error: AssertionError [ERR_ASSERTION]: acceptable ([string]) is required at Object.acceptParser (/home/bob/node_modules/restify/lib/plugins/accept.js:30:12)
After a couple of more hours, I finally found a solution. I've changed my restify server code as follows:
var restify = require("restify");
var corsMiddleware = require('restify-cors-middleware')
var cors = corsMiddleware({
preflightMaxAge: 5, //Optional
origins: ['*'],
allowHeaders: ['API-Token'],
exposeHeaders: ['API-Token-Expiry']
});
// WITHOUT HTTPS
const server = restify.createServer();
server.pre(cors.preflight);
server.use(cors.actual);
...
(rest is the same as above)
To be honest, I've no real idea what it actual does. But it's working and it the cost me too much energy for today already. I hope it will help others at some point. Everything seems to be rather recent changes from restify.
If your server isn’t allowing OPTIONS by default, you can add an explicit handler:
server.opts(/\.*/, function (req, res, next) {
res.send(200);
next();
});
Another possible problem is that you won’t have the effect intended the following headers:
res.setHeader('Access-Control-Allow-Headers', '*');
res.setHeader('Access-Control-Allow-Methods', '*');
The * wildcard values there are allowed by the spec, but browsers don’t yet support them. So what you must do instead is, explicitly specify in those values the methods and headers to allow:
res.setHeader('Access-Control-Allow-Headers', 'content-type');
res.setHeader('Access-Control-Allow-Methods', 'POST');
That’s because the request headers shown for the CORS preflight OPTIONS request have this:
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Origin: http://...
And what those request headers indicate is, the browser’s asking the server, Some code running at this origin wants to make a POST request to your server that adds a specific Content-Type to the request. Are you OK with receiving POST requests that add their own Content-Type?
So in response to that preflight OPTIONS request, the browser expects to receive an Access-Control-Allow-Methods response header which explicitly allows POST, along with an Access-Control-Allow-Headers response header which explicitly allows Content-Type.

WCF : Generate JSON not works

I'm using DevExpress, XAF, and XPO for my application. I need to expose my data from a webservice.
ASP.NET Web API V2 is not compatible with XPO objects... (If you found how to... I will take it!).
The DevExpress wizard can help me to generate a WCF Web Service project, where
MyContext inherits from XpoContext
MyService inherits from XpoDataServiceV3 (and the class have an attribute : [JSONPSupportBehavior])
I would get a list of my XPO Objects, for that, I wrote the next code
[WebGet]
public IQueryable<MyType> Get()
{
return new XPQuery<MyType>(new UnitOfWork());
}
I have found various properties on WebGet attribute : RequestFormat, ResponseFormat, BodyStyle, UrlTemplate. On Format properties, I have the choise between WebMessageFormat.Json and WebMessageFormat.Xml. Logically, I type WebMessageFormat.Json.
When I go on my favorite webbrowser or fiddler, I do this request:
GET http://localhost:51555/MyService.svc/Get HTTP/1.1
User-Agent: Fiddler
Host: localhost:51555
Content-Type: application/json
But this not works... The response is :
HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Length: 24250
Content-Type: application/atom+xml;type=feed;charset=utf-8
Server: Microsoft-IIS/10.0
...
And content was wrote in XML.
We are okay, I have configure my query with format properties... :
[WebGet(RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
I've found ! On your WCF service global class, wrote the following code :
HttpContext.Current.Request.Headers.Add("Accept", "application/json;");

AJAX works when extension is .json but not when it is .html

So I have an ajax request. When I use .html as below. I receive:
Failed to load resource: the server responded with a status of 406 (Not Acceptable)
When i use .json I receive the correct output. Why does it not work with .html
$("input[value = 'Add Type Targets']")
.click(
function() {
var promise = $
.ajax({
url : '/MyRoot/budget/myUrl.html',
type : 'GET',
beforeSend : function(
xhr) {
xhr
.setRequestHeader(
"Accept",
"application/json");
xhr
.setRequestHeader(
"Content-Type",
"application/json");
}
});
promise
.done(function(data) {
someCode
}
});
});
On the method I have
#RequestMapping(value = "/myUrl", method = RequestMethod.GET, produces = "application/json")
public #ResponseBody List<String> getData() {
return staticDataService.getData();
}
I have the jackson-mapper-asl-1.9.10.jar and jackson-core-asl-1.9.10.jar added.
Is it that because of the .html extension my response header is getting altered. Actually it is not even hitting my method when i use .html.
The HTTP error code 406 (Not Acceptable) means your HTTP request specified that the result must be of a certain type.
In your code, you explicitly mention that you only accept JSON results.
$.ajax({
url : '/MyRoot/budget/myUrl.html',
type : 'GET',
beforeSend : function(xhr) {
xhr.setRequestHeader( "Accept", "application/json");
...
When the server sees a file with a .json extension, it will automatically give it an application/json content-type, while a .html file, with be text/html content-type.
Since these are very different, you see the 406 error.
Since you are actually returning JSON, I would suggest you also use that for the extension. Not only will this help prevent confusion for other developers, it will also prevent you from having to 'fight the system'.
If you for some reason do want the HTML extension, you could try forcing the response content-type to be application/json. Since this extension based content-type is typically added by the server (IIS, Apache, etc.), it depends on your development stack if you can override this.
In ASP.NET you would use the following to explicitly set a header, but I can't say for sure if this will override the settings in IIS.
Response.ContentType = "application/json"
Should this not work, you can also change your AJAX call to be more permissive with it's response accepting.
xhr.setRequestHeader( "Accept", "*/*");

WebAPI: A callback parameter was not provided in the request URI

I am executing a post method in my API using fiddler I get error "A callback parameter was not provided in the request URI.". However, this works for get method.
I have seen several answers to this question, and as per the error I need to specify a callback parameter. However, I'm not sure how to do this using fiddler.
In response to one of those answers from Can I make a jQuery JSONP request without adding the '?callback=' parameter in URL? . I've tried the following in fiddler and I get the same error..
url: http://velopoint-api.localhost.dev/api/v1/tasks?callback=foo
header:
User-Agent: Fiddler
Host: velopoint-api.localhost.dev
ContentType: application/json; charset=utf-8
Authorization: basic "UNQUOTED"
Content-Length: 47
jsonp: true
jsonpCallback: jsonCallback
dataType: jsonp
request body
{ "Title":"New Task", "DueDate":"20-jul-2014" }
Startup
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
// Uncomment the following line of code to enable query support for actions with an IQueryable or IQueryable<T> return type.
// To avoid processing unexpected or malicious queries, use the validation settings on QueryableAttribute to validate incoming queries.
// For more information, visit http://go.microsoft.com/fwlink/?LinkId=279712.
// Change Formater to use CamelCasePropertyNamesContractResolver
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().FirstOrDefault();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
/* Support JsonP */
//register JSONP media type formatter
config.Formatters.Insert(0, new JsonpMediaTypeFormatter(jsonFormatter));
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
...
}
After playing around a little, I've finally figured it wasn't actually routing to the post method.
My header is now
User-Agent: Fiddler
Host: velopoint-api.localhost.dev
Authorization: basic UNQUOTED
Content-Length: 224
Content-Type: application/json
I fixed the problem by specifying Route attribute to my Post method and passing an empty string to the pattern parameter, both on the Get and the Post (as I already have the RoutePrefix attribute specified on the class.
[RoutePrefix("api/v1/tasks")]
[VeloPointAuthorise(perUser: true)]
public class TaskController : BaseApiController
{
[HttpGet]
[Route(template:"", Name = "TaskRoute")]
public HttpResponseMessage Get(int page = 0)
{
....
}
[HttpPost]
[Route(template:"")]
public HttpResponseMessage Post([FromBody] OrganiserTaskModel model)
{
....
}

HTTP OPTIONS Pre-flight request Load Cancelled with pending Status in Chrome

I am using Sencha Touch 2.1.0. I am making a HTTP GET call. It is a CORS request. Hence it is sending Pre-flight HTTP OPTIONS command as expected.
I have installed CORS filter on my server and configured it. The calls from my code were going through very well till yesterday. Suddenly today it stopped loading data. When I check the Network calls in Chrome, I see that the OPTIONS method shows up as "Load cancelled"
Method: OPTIONS
Status Text: "Load cancelled"
Type: pending
Initiator: Connection.js:319
I had a similar issue when I configured CORS filter for the first time. When I cleared browser cache, it started working. This time, I am not sure why it suddenly stopped working. It is not getting fixed even when I clear the cache and history in the browser.
If I make the same exact call from HTTPRequestor in Firefox it works very well. Here is the call. I have masked url due to confidentiality reasons.
OPTIONS http://myurl/rest/items?_dc=1358304888220&page=1&start=0&limit=25 HTTP/1.1
Access-Control-Request-Method: GET
Origin: http://localhost:8080
Access-Control-Request-Headers: accept, origin, x-requested-with
The same exact request gives me a very good response from HTTPRequestor. Here is the result:
OPTIONS http://myurl/rest/items?_dc=1358304888220&page=1&start=0&limit=25
Access-Control-Request-Headers: accept, origin, x-requested-with
Access-Control-Request-Method: GET
Origin: http://localhost:8080
-- response --
200 OK
Date: Wed, 16 Jan 2013 03:19:27 GMT
Server: Apache-Coyote/1.1
Access-Control-Allow-Origin: http://localhost:8080
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: HEAD, GET, POST, OPTIONS
Access-Control-Allow-Headers: X-Requested-With, Origin, Accept, Content-Type
Content-Length: 0
Strict-Transport-Security: max-age=15768000, includeSubDomains
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Sencha code in the Store to make this call:
proxy: {
type: 'ajax',
method: 'GET',
url: 'http://myurl/rest/items',
withCredentials: true,
useDefaultXhrHeader: false,
disableCaching: false,
headers: {
"Accept": "application/json"
},
failure: function(response) {
if (response.timedout) {
Ext.Msg.alert('Timeout', "The server timed out :(");
} else if (response.aborted) {
Ext.Msg.alert('Aborted', "Looks like you aborted the request");
} else {
Ext.Msg.alert('Bad', "Something went wrong with your request");
}
},
success: function(response){
Ext.Msg.alert(response.responseText);
}
},
autoLoad: true,
Please help me understand how I can fix this issue.
This seems to be an issue due to the last Google Chrome update. When I try with Iron browser, it works.
I was having a similar issue in beta versions of Webkit browsers. As it turns out, the Content-Type header is being set implicitly for XMLHttpRequests containing Blobs, so I had to add that to the Access-Control-Allow-Headers on my upload server.
See: https://bugs.webkit.org/show_bug.cgi?id=99983
I have just solved this problem. Server response Header should be set as:
"Access-Control-Allow-Origin": "*"
"Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept"
You have done this already. I think if GET or POST method contains X-Requested-With header, OPTIONS method will be sent first. X-Requested-With header is in the default XHR header. So we should set 'useDefaultXhrHeader' to false. Ext.data.Store didn't support this parameter. Edit sdk/src/data/Connection.js, change default value of useDefaultXhrHeader to false.