I'm building a web app with Django. I have a bunch of API calls in Javascript via Ajax (jQuery v1.8.3).
Most of them work, but a particular one results in a return object with status 0 and this message as the statusText:
[Exception... "'JavaScript component does not have a method named: "available"' when calling method: [nsIInputStream::available]" nsresult: "0x80570030 (NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED)" location: "JS frame :: http://127.0.0.1:8000/media/js/jquery.js :: .send :: line 8434" data: no]
The corresponding line in jQuery is xhr.send( ( s.hasContent && s.data ) || null );
However, this occurs only in Firefox. Chrome works fine. Again, other requests do work. The only thing which sets this one apart is the DELETE http method.
The request is as follow (HTTP network data shown in Chrome – Firebug doesn't show anything in Firefox):
Request URL: http://127.0.0.1:8000/api/reservation/13/
Request Method: DELETE
Status Code: 400 BAD REQUEST (This is expected)
Request Headers
Accept: application/json, text/javascript, */*; q=0.01
Content-Length: 15
Content-Type: application/json
Origin: http://127.0.0.1:8000
Referer: http://127.0.0.1:8000/reservation/
X-Requested-With: XMLHttpRequest
Request Payload
[object Object]
Response Headers
Cache-Control: no-cache
Content-Type: text/html; charset=utf-8
Date: Tue, 02 Apr 2013 19:18:35 GMT
Server: WSGIServer/0.1 Python/2.7.2
On the server, I don't receive any request.
The JS code is (taken directly from Firebug Watch at breakpoint):
options = {
contentType: "application/json",
data: Object {},
dataType: "json",
processData: false,
type: "DELETE",
url: "/api/reservation/13/",
error: function(),
success: function()
};
$.ajax(options);
I also did try to disable all extensions in FF. I run v20.0.
The problem was a combination of Firefox with jQuery/XMLHttpRequest and sending an object via HTTP DELETE. Once JSON'ifying the object via JSON.stringify() everything worked.
Still, a strange exception for Firefox to throw.
Thanks to freddyb for that idea.
The problem was with the property called processData within the $.ajax function. When this property is supplied as "false" (don't know why) Firefox doesn't like it, and as consequence, the browser doesn't digest the JSON request/response package. Chrome and Safari works just fine.
This happens (as of 2014 with FireFox 32) with any non-GET AJAX request when the request data object is an empty object, like {}. I am using Mithril.js and it may be related to the fact that Mithril always sets a Content-Type for non-GET requests. This was absolutely repeatable once I knew the trigger.
(Note that the "non-GET" part may not be entirely accurate -- Mithril ignores the data object if it's a GET so sending an empty object with GET using the underlying AJAX object may also fail in the same way.)
Counter-intuitively, setting data to an empty string, "", does not fail in this way, so that was my work-around. I actually don't set data at all when there is none, and if it's unset by the time I send the request (in my AJAX wrapper) I default it to "".
It sounds like you have a buggy Firefox extension installed which is trying to examine the XMLHttpRequest data and failing....
I suggest you try http://support.mozilla.org/en-US/kb/troubleshoot-firefox-issues-using-safe-mode or just disabling whatever Firefox extensions are involved.
Related
I am working on an internal web application at work. In IE10 the requests work fine, but in Chrome all the AJAX requests (which there are many) are sent using OPTIONS instead of whatever defined method I give it. Technically my requests are "cross domain." The site is served on localhost:6120 and the service I'm making AJAX requests to is on 57124. This closed jquery bug defines the issue, but not a real fix.
What can I do to use the proper http method in ajax requests?
Edit:
This is in the document load of every page:
jQuery.support.cors = true;
And every AJAX is built similarly:
var url = 'http://localhost:57124/My/Rest/Call';
$.ajax({
url: url,
dataType: "json",
data: json,
async: true,
cache: false,
timeout: 30000,
headers: { "x-li-format": "json", "X-UserName": userName },
success: function (data) {
// my success stuff
},
error: function (request, status, error) {
// my error stuff
},
type: "POST"
});
Chrome is preflighting the request to look for CORS headers. If the request is acceptable, it will then send the real request. If you're doing this cross-domain, you will simply have to deal with it or else find a way to make the request non-cross-domain. This is why the jQuery bug was closed as won't-fix. This is by design.
Unlike simple requests (discussed above), "preflighted" requests first
send an HTTP request by the OPTIONS method to the resource on the
other domain, in order to determine whether the actual request is safe
to send. Cross-site requests are preflighted like this since they may
have implications to user data. In particular, a request is
preflighted if:
It uses methods other than GET, HEAD or POST. Also, if POST is used to send request data with a Content-Type other than
application/x-www-form-urlencoded, multipart/form-data, or text/plain,
e.g. if the POST request sends an XML payload to the server using
application/xml or text/xml, then the request is preflighted.
It sets custom headers in the request (e.g. the request uses a header such as X-PINGOTHER)
Based on the fact that the request isn't sent on the default port 80/443 this Ajax call is automatically considered a cross-origin resource (CORS) request, which in other words means that the request automatically issues an OPTIONS request which checks for CORS headers on the server's/servlet's side.
This happens even if you set
crossOrigin: false;
or even if you ommit it.
The reason is simply that localhost != localhost:57124. Try sending it only to localhost without the port - it will fail, because the requested target won't be reachable, however notice that if the domain names are equal the request is sent without the OPTIONS request before POST.
I agree with Kevin B, the bug report says it all. It sounds like you are trying to make cross-domain ajax calls. If you're not familiar with the same origin policy you can start here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Same_origin_policy_for_JavaScript.
If this is not intended to be a cross-domain ajax call, try making your target url relative and see if the problem goes away. If you're really desperate look into the JSONP, but beware, mayhem lurks. There really isn't much more we can do to help you.
If it is possible pass the params through regular GET/POST with a different name and let your server side code handles it.
I had a similar issue with my own proxy to bypass CORS and I got the same error of POST->OPTION in Chrome. It was the Authorization header in my case ("x-li-format" and "X-UserName" here in your case.) I ended up passing it in a dummy format (e.g. AuthorizatinJack in GET) and I changed the code for my proxy to turn that into a header when making the call to the destination. Here it is in PHP:
if (isset($_GET['AuthorizationJack'])) {
$request_headers[] = "Authorization: Basic ".$_GET['AuthorizationJack'];
}
In my case I'm calling an API hosted by AWS (API Gateway). The error happened when I tried to call the API from a domain other than the API own domain. Since I'm the API owner I enabled CORS for the test environment, as described in the Amazon Documentation.
In production this error will not happen, since the request and the api will be in the same domain.
I hope it helps!
As answered by #Dark Falcon, I simply dealt with it.
In my case, I am using node.js server, and creating a session if it does not exist. Since the OPTIONS method does not have the session details in it, it ended up creating a new session for every POST method request.
So in my app routine to create-session-if-not-exist, I just added a check to see if method is OPTIONS, and if so, just skip session creating part:
app.use(function(req, res, next) {
if (req.method !== "OPTIONS") {
if (req.session && req.session.id) {
// Session exists
next();
}else{
// Create session
next();
}
} else {
// If request method is OPTIONS, just skip this part and move to the next method.
next();
}
}
"preflighted" requests first send an HTTP request by the OPTIONS method to the resource on the other domain, in order to determine whether the actual request is safe to send. Cross-site requests
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
Consider using axios
axios.get( url,
{ headers: {"Content-Type": "application/json"} } ).then( res => {
if(res.data.error) {
} else {
doAnything( res.data )
}
}).catch(function (error) {
doAnythingError(error)
});
I had this issue using fetch and axios worked perfectly.
I've encountered a very similar issue. I spent almost half a day to understand why everything works correctly in Firefox and fails in Chrome. In my case it was because of duplicated (or maybe mistyped) fields in my request header.
Use fetch instead of XHR,then the request will not be prelighted even it's cross-domained.
$.ajax({
url: '###',
contentType: 'text/plain; charset=utf-8',
async: false,
xhrFields: {
withCredentials: true,
crossDomain: true,
Authorization: "Bearer ...."
},
method: 'POST',
data: JSON.stringify( request ),
success: function (data) {
console.log(data);
}
});
the contentType: 'text/plain; charset=utf-8', or just contentType: 'text/plain', works for me!
regards!!
I've got an http 501 error bubbling up in prod from this call:
return $.ajax({
url: finalUrl,
success: function (result) {
console.log('success call result');
console.log(result);
finalResult = result;
},
data: JSON.stringify(data),
type: 'PATCH',
contentType: 'application/json'
});
How can I return a mock simulating the error so I can test a fix outside of production? I looked at the response tab in chrome and I see an HTML message:
<HTML><HEAD>
<TITLE>Unsupported Request</TITLE>
</HEAD><BODY>
<H1>Unsupported Request</H1>
PATCH to http://demo.site.com/serviceName/v1/requests/9305e338-666a-e611-8516-000c291891bb not supported.<P>
Reference #8.f0fd717.1472154919.9959c96
</BODY></HTML>
We suspect that the API is not being hit at all, blocked by a firewall. I don't know if I should expect a string or object in this case? If object, then what are the members of that object?
Fiddler says:
Server: AkamaiGHost
Mime-Version: 1.0
Content-Type: text/html
Content-Length: 350
Expires: Thu, 25 Aug 2016 20:21:49 GMT
Date: Thu, 25 Aug 2016 20:21:49 GMT
Connection: close
We suspect that the API is not being hit at all, blocked by a firewall.
From the error, it looks like you're hitting a back-end that isn't configured for that request-type.
See these questions for a potential solution, assuming you control the back-end too.
Does tomcat support http PATCH method?
How do I stop Apache httpd from rejecting HTTP PATCH requests?
If you do control the back-end, but the above doesn't help, make sure that your controller function supports that request method. In Spring, for example, you have to declare that explicitly:
#Controller
MyController {
#Requestmapping(value = "/api/patchTheThing", method=RequestMethod.PATCH)
#ResponseBody String patchTheThing(....) {
...
}
}
Just set up an endpoint on a server of your choice and use your web server configure or coding language of your choice to issue a 501 response. But the problem seems pretty clear, the server is not expecting a PATCH request. If you had some networking problem you would not be getting a response at all.
you should strongly consider making sure your JavaScript code handles both happy path / success outcome as well as other types off sever response outcomes so your application can better handle how it wants to recover from such errors.
Trying to make a call and retrieve a very simple, one line, JSON file.
$(document).ready(function() {
jQuery.ajax({
type: 'GET',
url: 'http://wncrunners.com/admin/colors.json' ,
dataType: 'jsonp',
success: function(data) {
alert('success');
}
});
});//end document.ready
Here's the RAW Request:
GET http://wncrunners.com/admin/colors.json?callback=jQuery16406345664265099913_1319854793396&_=1319854793399 HTTP/1.1
Host: wncrunners.com
Connection: keep-alive
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.106 Safari/535.2
Accept: */*
Referer: http://localhost:8888/jquery/Test.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Here's the RAW Response:
HTTP/1.1 200 OK
Date: Sat, 29 Oct 2011 02:21:24 GMT
Server: Apache/1.3.33 (Unix) mod_ssl/2.8.22 OpenSSL/0.9.7d SE/0.5.3
Last-Modified: Fri, 28 Oct 2011 17:48:47 GMT
ETag: "166a2402-10-4eaaeaff"
Accept-Ranges: bytes
Content-Length: 16
Content-Type: text/plain
Connection: close
{"red" : "#f00"}
The JSON is coming back in the response (red : #f00), but Chrome reports Uncaught SyntaxError: Unexpected token : colors.json:1
If I navigate directly to url itself, the JSON is returned and is displayed in the browser.
If I paste the contents of colors.json into JSLINT, the json validates.
Any ideas why I can't get this error and I never make it to the success callback?
EDIT - the jQuery.ajax() call above runs perfect at jsfiddle.net, and returns the alert 'success' as expected.
EDIT 2 - this URL works fine 'http://api.wunderground.com/api/8ac447ee36aa2505/geolookup/conditions/q/IA/Cedar_Rapids.json' I noticed that it returned as TYPE: text/javascript and Chrome did not throw the Unexpected Token. I've tested several other url's and the ONLY one that does not throw the Unexptected Token is the wunderground that is returned as TYPE: text/javascript.
Streams returned as text/plain and application/json are not being parsed correctly.
You've told jQuery to expect a JSONP response, which is why jQuery has added the callback=jQuery16406345664265099913_1319854793396&_=1319854793399 part to the URL (you can see this in your dump of the request).
What you're returning is JSON, not JSONP. Your response looks like
{"red" : "#f00"}
and jQuery is expecting something like this:
jQuery16406345664265099913_1319854793396({"red" : "#f00"})
If you actually need to use JSONP to get around the same origin policy, then the server serving colors.json needs to be able to actually return a JSONP response.
If the same origin policy isn't an issue for your application, then you just need to fix the dataType in your jQuery.ajax call to be json instead of jsonp.
I have spent the last few days trying to figure this out myself. Using the old json dataType gives you cross origin problems, while setting the dataType to jsonp makes the data "unreadable" as explained above. So there are apparently two ways out, the first hasn't worked for me but seems like a potential solution and that I might be doing something wrong. This is explained here [ https://learn.jquery.com/ajax/working-with-jsonp/ ].
The one that worked for me is as follows:
1- download the ajax cross origin plug in [ http://www.ajax-cross-origin.com/ ].
2- add a script link to it just below the normal jQuery link.
3- add the line "crossOrigin: true," to your ajax function.
Good to go! here is my working code for this:
$.ajax({
crossOrigin: true,
url : "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=-33.86,151.195&radius=5000&type=ATM&keyword=ATM&key=MyKey",
type : "GET",
success:function(data){
console.log(data);
}
})
I had the same problem and the solution was to encapsulate the json inside this function
jsonp(
.... your json ...
)
That hex might need to be wrapped in quotes and made into a string. Javascript might not like the # character
I am new to Backbone and got the GET working with a test endpoint e.g.,
var Attributes = Backbone.Collection.extend({
url: '//127.0.0.1:8080/blah'
});
var AttributeListView = Backbone.View.extend({
el: '.page',
render: function () {
var that = this;
var attributes = new Attributes();
attributes.fetch({
success: function (attributes) {
var template = _.template($('#attribute-list-template').html(), {attributes: attributes.models});
that.$el.html(template);
}
})
}
})
However, the real endpoint requires a POST with JSON payload and I can't get the syntax to work. I tried something like this
var AttributeListView = Backbone.View.extend({
el: '.page',
render: function () {
var that = this;
var attributes = new Attributes();
attributes.fetch({
type: "POST",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: '{ "searchtext": "abc" }',
success: function (attributes) {
var template = _.template($('#attribute-list-template').html(), {attributes: attributes.models});
that.$el.html(template);
}
})
}
})
#Rusty, the URL works fine with or without http, browsers nowadays handle it properly. After digging a bit more, it seems like it is a CORS issue. I know that the endpoint has set Access-Control-Allow-Origin:* but only for POST request and I don't think the request is being set properly, here's what I got from Chrome debug
Request URL:http://127.0.0.1:8080/blah
Request Headers CAUTION: Provisional headers are shown.
Accept:application/json, text/javascript, */*; q=0.01
Cache-Control:max-age=0
Content-Type:application/x-www-form-urlencoded; charset=UTF-8
Origin:http://localhost:8000
Referer:http://localhost:8000/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.102 Safari/537.36
Form Dataview sourceview URL encoded
{ "searchtext": "abc" }:
From the console log
XMLHttpRequest cannot load http://127.0.0.1:8080/blah. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access.
As you said this is a CORS issue.
Specifically on POST PUT and DELETE requests the browser actually performs 2 requests.
What happens under the hood is that, before the real request, the browser sends a preflight OPTION request which is like asking the server for permission to make the actual request.
(Please checks on the server log which type of request comes from the browser)
To allow CORS request the server must correctly handle this scenario and respond to the OPTION request with a set of CORS Headers like those:
Access-Control-Allow-Origin: http://some.another.domain
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type
Content-Type: text/html; charset=utf-8
In the CORS Headers (all begin with Access-Control-Allow-*) the server specifies the following permissions:
Which domain is allowed to perform the request, it can be * to allow any external domain.
Which HTTP Methods are accepted from those domains.
Which Request Headers are accepted, you can add all the headers you need.
For example to handle HTTP Authentication between different domains, which is a common scenario using external API you'll need to add the Authorization header.
If the server responds correctly to the OPTION request the browser will performs the the actual request.
This is a guide to correctly handle CORS Requests for Rails but the it easily applies to all the server side languages/frameworks: http://leopard.in.ua/2012/07/08/using-cors-with-rails/
Rustytoms in his comment is correct. You don't have to set the entire url, you could just write /blah but I think the problem you are having is different.
By default, when you call fetch on a new model Backbone will generate a GET request to get the information following the REST principles. To check whether the model is new Backbone uses the model.isNew() method, which basically just checks if the model has an id. If you call the save() method on a Model then Backbone will generate a POST request to '/users', and if the model is not new it will generate a SYNC request to '/users/:id'.
This methods delegate all the functionality to the Backbone.sync() method. So what you can do if you back end API does not follow the REST principle is to replace the fetch() method in your model with an AJAX call, or create a new one. On the 'success' of the AJAX request you can usemodel.set() to save your attributes to the model.
I suggest you read the Backbone documentation to get to know this methods since everything is very clear.
We are developing a mobile site using html5, jQuery(1.8.2) and jQuery mobile while making jQuery ajax calls (get and post).
After we changed our domain name, we are getting "access denied" for ajax calls on ie9.
We tried to include jquery.iecors.js. But still we are getting the same error.Is there any resolution for this?
Sample Code:
$.support.cors = true;
$.ajax({
cache: false,
async: true,
crossDomain: true,
timeout: 600000,
url: baseUrl + '/SmartTouch/restServices/PrefferedHotels',
type: 'GET',
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", "Basic " + myencoded);
},
contentType: "application/x-www.form-urlencoded; (http://www.form-urlencoded;) (http://www.form-urlencoded;) charset=UTF-8",
success: function (data) {
alert("success");
},
error: function (jqXHR, textStatus, errorThrown) {
alert("error!!::" + JSON.stringify(jqXHR));
alert('response: ' + jqXHR.responseText);
alert('code: ' + jqXHR.getResponseHeader('X-Subscriber-Status'));
alert("textStatus " + textStatus);
alert("errorThrown " + errorThrown);
}
});
Edited:
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", "Basic " + myencoded);
xhr.setRequestHeader("Access-Control-Allow-Origin", "*");
xhr.setRequestHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS");
},
contentType: "application/x-www.form-urlencoded; (http://www.form-urlencoded;) (http://www.form-urlencoded;) charset=UTF-8",
success: function (data) {
alert("success");
},
error: function (jqXHR, textStatus, errorThrown) {
alert("error!!::" + JSON.stringify(jqXHR));
Request and Response headers in IE9:
Request:
Key Value
Request GET url HTTP/1.1
Accept text/html, application/xhtml+xml, */*
Accept-Language en-US
User-Agent Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Accept-Encoding gzip, deflate
Proxy-Connection Keep-Alive
Host ("url")
Pragma no-cache
Cookie GUEST_LANGUAGE_ID=en_US; COOKIE_SUPPORT=true; __utmc=24444716; __utma=24444716.47018335.1379597653.1380274476.1380276859.17; __utmz=24444716.1379597653.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utmb=24444716.6.10.1380276859
Response:
Key Value
Response HTTP/1.1 200 OK
Server Apache-Coyote/1.1
X-Powered-By Servlet 2.5; JBoss-5.0/JBossWeb-2.1
Accept-Ranges bytes
ETag W/"64578-1380266616000"
Last-Modified Fri, 27 Sep 2013 07:23:36 GMT
Content-Type text/html
Date Fri, 27 Sep 2013 10:17:01 GMT
Content-Length 64578
Age 0
Via 1.1 localhost.localdomain
This kind of Content-Type looks strange:
application/x-www.form-urlencoded; (http://www.form-urlencoded;) (http://www.form-urlencoded;) charset=UTF-8"
I can imagine the IE has as problem with it.
Try the proper one:
application/x-www-form-urlencoded; charset=UTF-8
^-- notice: no dot!
It's also possible for the IE to have problems with the authorization.
Maybe myencoded is out of the scope or not filled correctly. Debug this variable and have a look at this question: Authorization through setRequestHeader
If you want the Ajax url to be hit from any domain, the server must send a response header Access-Control-Allow-Origin : * or Access-Control-Allow-Origin : your-domain if restricted only to your domain.Can you see these headers in response?
See this Microsoft article on CORS implementation on IE8 and IE9: http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx
Specifically they say two things:
No custom headers may be added to the request
and
No authentication or cookies will be sent with the request
And you mentioned in your comment:
Our problem is we are doing basic authorization and including following header: xhr.setRequestHeader("Authorization", "Basic " + myencoded); in our ajax call.But in ie9 we are getting access denied.Is there any way to include this header?
Unfortunately, the answer I'll have to give you is no, there is no way to include this header in IE8 or IE9. Microsoft designed it that way.
To get it to work with CORS on IE9 you'll have to convince the site you're connecting to to allow you to send authorization information some other way - maybe query params or post data.
If the site is not cooperative there's always the request proxy work-around where you request to a page on your server and you server forwards the request with the correct header etc.
You seem confident that the issue has nothing to do with the suggested jQuery bug (especially since you're using jquery.iecors.js) so I'll move right on.
What is the significance of the "edited" bit? Access-Control-Allow-Origin:* should be set on the response (i.e. server-side, as part of Apache/IIS/F5 configuration), not on the request. Edit: there is more information available on MDN; you could also use something like burp's tampering proxy to play with the headers if you don't have immediate access to config changes (pretty common in an enterprise environment)
Even if not an issue, #DanFromGermany is absolutely right - content-type does look strange. You shouldn't even have to set it manually, jQuery.ajax() has it correct by default.
You also seem concerned with setting the basic authentication header. Remember that myencoded value is just encoded (not encrypted), so you might as well skip the header and pass credentials in the URL: http(s)://username:password#www.example.com/
Moar edit:
Looking through those MDN docos above, this seems relevant:
By default, in cross-site XMLHttpRequest invocations, browsers will
not send credentials. A specific flag has to be set on the
XMLHttpRequest object when it is invoked.
Perhaps try adding xhr.withCredentials = true; to your beforeSend?
Important note: when responding to a credentialed request, server must specify a domain, and cannot use wild carding. The above example
would fail if the header was wildcarded as:
Access-Control-Allow-Origin: *. Since the Access-Control-Allow-Origin
explicitly mentions http://foo.example, the credential-cognizant
content is returned to the invoking web content.
This would invalidate previous advice of using an asterisk in the header (i.e. explicit domain is required)
If you were using windows based hosting?
Please check old configs for the IIS, if available,
there are security provisions which will allow content by its type,
add this response header <% Response.AddHeader("Access-Control-Allow-Origin","*") %> in your page also.
or refer the source link to MSDN for more details
I think you have updated this in edits but there are many things as its AJAX involved, and your IE9 may also one of the reason if you have changed security options and not default.
I think that should do, if not please reply
You can try this
<meta http-equiv="X-UA-Compatible" content="IE=Edge" >
It forces the browser the render at whatever the most recent version's standards are. For reference http://msdn.microsoft.com/en-us/library/ie/ms533876%28v=vs.85%29.aspx