I read the document.
but I think I must have misunderstood it.
$http.defaults.headers.jsonp = { 'Accept' : 'application/json'};
$http.jsonp(url).success(function(data, status, headers, config) {
I also have tried
$httpProvider.defaults.headers.jsonp = { 'Accept' : 'application/json'};
$http.jsonp(url).success(function(data, status, headers, config) {
I wanted to change the Accept to application/json
Neither work.
There is no way to control headers sent by a browser while using JSONP. JSONP is a smart trick (or a hack, depending on how you see it...) that consist of inserting a <script> tag pointing to a server endpoint. Ultimately it is a browser who will decide which headers to sent while requesting scripts via <script> tag and you can't influence it.
More info here: Modify HTTP Headers for a JSONP request
Related
I am using navigator for communicating with the server , but problem is that we need to pass some header information as there is filter which recognise the request is from the valid source.
Can anybody help on this?
Thanks.
See the Navigator.sendBeacon MDN documentation for further information.
Create a blob to provide headers. Here is an example:
window.onunload = () => {
const body = {
id,
email,
};
const headers = {
type: 'application/json',
};
const blob = new Blob([JSON.stringify(body)], headers);
navigator.sendBeacon('url', blob);
};
navigator.sendBeacon will send a POST request with the Content-Type request header set to whatever is in headers.type. This seems to be the only header you can set in a beacon though, per W3C:
The sendBeacon method does not provide ability to customize the request method, provide custom request headers, or change other processing properties of the request and response. Applications that require non-default settings for such requests should use the [FETCH] API with keepalive flag set to true.
I was able to observe some of how this worked through this Chromium bug report.
As written in the Processing Model of sendBeacon :
Extract object's byte stream (transmittedData) and content type (contentType).
How extraction is performed is described here
What I've gathered is that the content type of the transmitted data is extracted, and it is set as the Content-Type of the HTTP request.
1) If a Blob object is sent, the Content-Type becomes the Blob's type.
2) If a FormData object is sent, the Content-Type becomes multipart/form-data
3) If a URLSearchParams object is sent, the Content-Type becomes application/x-www-form-urlencoded
4) If a normal string is sent, the Content-Type becomes text/plain
Javascript code to implement different objects can be found here
If you're using Chrome and you're trying to set the content-type header, you'll probably have some issues due to security restrictions:
Uncaught DOMException: Failed to execute 'sendBeacon' on 'Navigator': sendBeacon() with a Blob whose type is not any of the CORS-safelisted values for the Content-Type request header is disabled temporarily. See http://crbug.com/490015 for details.
See sendBeacon API not working temporarily due to security issue, any workaround?
I want to call an api when someone close the tab, so I tried to use navigator.sendBeacon() but the problem is we need to pass the Authorization token into it and sendBeacon does not provide that, so I found other solution that is more effective and very easy to implement.
The solution is a native fetch API with a keepalive flag in pagehide event.
Code
window.addEventListener('pagehide', () => {
fetch(`<URL>`, {
keepalive: true,
method: '<METHOD>',
headers: {
'content-type': 'application/json',
// any header you can pass here
},
body: JSON.stringify({ data: 'any data' }),
});
});
FAQs / TL;DR Version
Why should we need to use the keepalive flag?
The keepalive option can be used to allow the request to outlive the page. Fetch with the keepalive flag is a replacement for the Navigator.sendBeacon() API.
Learn more about it, please visit https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters
What is PageLifecycle API
Learn more about it, please visit https://developer.chrome.com/blog/page-lifecycle-api/
From the Page Lifecycle image, shouldn't unload be considered as the best choice?
unload is the best event for this case but unload is not firing in some cases on mobile and it also does not support the bfcache functionality.
I also notice that when I am using unload then I am not getting proper output in the server log. why? IDK, if you know about it then comments are welcome.
Nowadays, It's also not recommended by the developers.
Learn more about why unload is not recommended: https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event#usage_notes
Learn more about pagehide: https://developer.mozilla.org/en-US/docs/Web/API/Window/pagehide_event
Because the method sendBeacon(..) does not allow headers manipulation, I added them into the form as normal fields:
const formData = new FormData();
formData.append('authorization', myAuthService.getCachedToken());
navigator.sendBeacon(myURL, formData);
Then on the host side I added a simple Middleware class (.Net) which catches POST requests without headers and copies them from the body:
public class AuthMiddleware
{
...
...
public async Task Invoke(HttpContext context)
{
string authHeader = context.Request.Headers["Authorization"];
if (authHeader == null && context.Request.Method=="POST")
{
context.Request.Headers["Authorization"] = string.Format("Bearer {0}",
context.Request.Form["authorization"].ToString());
}
await _next.Invoke(context);
}
}
Posting as an answer as I'm not allowed to post a comment under the answer:
For Chrome, issue with navigator.sendBeacon sending Blob for with non CORS-safelisted types was fixed in Chrome version 81 so this should be safe to use now.
https://bugs.chromium.org/p/chromium/issues/detail?id=724929
For IE, an alternative in unload event is to use synchronous ajax request, as IE doesn't support sendBeacon but supports synchronous ajax call in my case.
You can't send data with JSON after Chrome 39, has been disabled due to a security concern.
You can try to send data with plain text. But don't forget the parseing text from the backend.
After searching for an answer for this question I found out that for passing header with navigator we need to pass a blob object.
For example
var headers = {type: 'application/json'};
var blob = new Blob(request, headers);
navigator.sendBeacon('url/to/send', blob);
I'm trying to do an AJAX request to https://developers.zomato.com/api/v2.1/search referring to Zomato API
The server has headers:
"access-control-allow-methods": "GET, POST, DELETE, PUT, PATCH, OPTIONS",
"access-control-allow-origin": "*"
The problem is that the API requires additional headers set for user-key. But whenever I set custom headers then chrome would do a pre-flight request by sending an OPTIONS request to the above URL which is failing, and thus the AJAX request is failing as well.
If I don't set the headers, then I don't get a CORS error, but rather a forbidden error from server since I'm not setting user-key header.
Any way to go about this catch-22 situation?
Both Jquery and JavaScript way are failing:
$(document).ready(function () {
$.ajax({
url: 'https://developers.zomato.com/api/v2.1/search',
headers: {
'Accept': 'application/json',
'user_key': 'XXXXX'
},
success: function (data) {
console.log(data);
}
});
});
var xhr = new XMLHttpRequest();
var url = 'https://developers.zomato.com/api/v2.1/search';
xhr.open('GET', url, false);
xhr.setRequestHeader('Accept', 'application/json');
xhr.setRequestHeader('user_key', 'XXXXXX');
xhr.send(null);
if (xhr.status == 200) {
console.log(xhr.responseText);
}
Error I'm getting:
OPTIONS https://developers.zomato.com/api/v2.1/search
XMLHttpRequest cannot load https://developers.zomato.com/api/v2.1/search. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access. The response had HTTP status code 501.
If somebody wants to reproduce you can get a free user-key here:
https://developers.zomato.com/api
There does not appear to be a work-around for this issue from a browser. The CORS specification requires a browser to preflight the request with the OPTIONS request if any custom headers are required. And, when it does the OPTIONS preflight, it does not include your custom headers because part of what the OPTIONS request is for is to find out what custom headers are allowed to be sent on the request. So, the server is not supposed to require custom headers on the OPTIONS request if it wants this to work from a browser.
So, if the server is requiring the custom headers to be on the OPTIONS request, then the server is just expecting something that will not happen from a browser.
See related answers that describe more about this here:
jQuery CORS Content-type OPTIONS
Cross Domain AJAX preflighting failing Origin check
How do you send a custom header in a cross-domain (CORS) XMLHttpRequest?
Using CORS for Cross-Domain Ajax Requests
And, another user with the same issue here:
Zomato api with angular
It appears the Zomato is not browser friendly, but requires access from a server where you don't have CORS restrictions.
FYI, the error coming back from Zomato is 501 which means NOT IMPLEMENTED for the OPTIONS command. So, it looks like it's not only that the key is not being sent with the OPTIONS command, but that Zomato does not support the OPTIONS command, but that is required for the use of custom headers on a cross-origin request from a browser.
You can't bypass Access-Control-Allow-Headers in preflight response.
However as mentioned by #Jaromanda X in comments, Zomato sends:
Access-Control-Allow-Headers:X-Zomato-API-Key
...meaning you can only send this non-standard header from browser. Also don't go too low-level in request definition when jQuery has pretty and prepared shorthands ...
TL;DR Working example:
$.ajax({
type: "GET", //it's a GET request API
headers: {
'X-Zomato-API-Key': 'YOUR_API_KEY' //only allowed non-standard header
},
url: 'https://developers.zomato.com/api/v2.1/dailymenu', //what do you want
dataType: 'json', //wanted response data type - let jQuery handle the rest...
data: {
//could be directly in URL, but this is more pretty, clear and easier to edit
res_id: 'YOUR_RESTAURANT_OR_PLACE_ID',
},
processData: true, //data is an object => tells jQuery to construct URL params from it
success: function(data) {
console.log(data); //what to do with response data on success
}
});
I already searched within SO for some threads about this, but could only find some which explained what this header is for or how to get the authorization header in c# but I don't want to read it from server side but from client side.
Is there any way to get the Base64 encoded header "Authorization" from the browser?
I want to implement a tool where you can log in and if you click on a spezific button your username will be saved.
My problem is that the browser does the authorization automatically, and with jQuery and JavaScript methods you can only set the requestheaders and get the responseheaders. I couldn't find a method to get the requestheaders.
The library gethttp could get some headers, but not the authorization header.
My guess is that this header is hidden.
I'm doing a login via SVN and the browser does the authorization the moment you enter the website.
Only the username is enough.
I'm searching for solutions where the user doesn't have to input their username.
I'm assuming you're trying to use the Basic Realm authorisation mechanism
This had already been replied on Stackoverflow and involves the $.ajax() jquery object.
How to use Basic Auth with jQuery and AJAX?
So please don't upvote me on this
$.ajaxSetup({
headers: {
'Authorization': "Basic XXXXX"
},
data: '{ "comment" }',
success: function (){
alert('Thanks for your comment!');
}
});
where XXXXX is your username:password base64 encoded
You can use native fetch API:
fetch("http://localhost:8888/validate",{
method:"GET",
headers: {"Authorization": "Bearer xxxxx"}
})
.then(res => res.json())
.then(
(result) => {
// do something
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(error) => {
// handle error
}
)
It's not possible to get the headers for the request of the CURRENT page. This has been asked several times on SO.
However, you can make a new request and retrieve the headers of that request. That way you are able to get the Basic Auth headers, base64 decode that string and then you have the username (and also the password).
Decoding base64 in javascript can be done using the following function as suggested by #michael in the comments.
window.atob("base64encodedString");
Following is the request i used so far
$http.get(url)
.success(function (data){})
.error(function (data){})
works without any CORS issues. My server side Allows all origins, methods, all headers
when i add http header like
$http.get(url, { headers: { "USERID": user, "SESSIONID": sessionId}})
the request changes into OPTIONS method when i see in chrome dev tools network tab
What is the reason for this? if it is expected then how to add custom http headers.
I have gone thru this link angularjs-performs-an-options-http-request-for-a-cross-origin-resource but it didnt help
Here i am expecting that server should allow different origins . But it is allowing headers, only if i were in a same server. But not sure about this is by angular or by server side.
after headers
$http.get(url,{ headers: { "USERID": user, "SESSIONID": sessionId } })
in chrome dev tools i am seeing like
Request Method:OPTIONS
Status Code:404 Not Found
but without headers
Request Method:GET
Status Code:200 OK
When i do this in REST Client, i can send headers to the backend.
$http({method: 'GET', url: '/someUrl', headers: { "USERID": user, "SESSIONID": sessionId}}).
success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
will work.
$http.get is a shortcut method.
Check the config in the docs
This is a known bug, see for instance https://github.com/angular/angular.js/issues/1585 .
A workaround is to use a jQuery request.
I had the same massive issue when trying to pass header in my get, where it changes get to options and wouldn't work. In order to make it work I added the following in my php api
<?php if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']) && $_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'] == 'GET') {
header("Access-Control-Allow-Headers: Authorization, X-Auth-Token");
}
exit;
} ?>
You can allow for any headers that you wish to pass.
Hope this helps
For my particular problem with my C# Web API solution I had to have something handle the Options request. Angular was sending a preflight request method OPTIONS which I did allow in my web.config with
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS, PATCH" />
But that wasn't enough I also included a method to handle the Options Request and I returned nothing
[ResponseType( typeof( void ) )]
public IHttpActionResult OptionsPost() {
return StatusCode( HttpStatusCode.NoContent );
}
I'm trying to read the headers of the coming response upon Ext.ajax.request.
Here it is the code:
Ext.Ajax.request({ url: 'http://localhost:3000/v0.1/login' ,
method: 'POST',
scope:this,
jsonData: {"_username":username,"_userpwd":password},
success: function(responseObject){
var headers = responseObject.getAllResponseHeaders();
console.info(headers );
Ext.destroy(Ext.ComponentQuery.query('#loginWindow'));
this.application.getController('SiteViewController').showView();
},
failure: function(responseObject){
alert(responseObject.status);
}
});
But the only header that it is printed out in console is:
Object {content-type: "application/json; charset=utf-8"}
All the other headers are missing, but they are present in the chrome inspector!!!
What am I missing? Thanks
Because you're probably doing a cross-domain request, you will only have headers explicitly exposed by the server. Same domain requests expose all the headers.
On the server side you have to add the header "Access-Control-Expose-Headers" with the exhaustive list of headers you want to expose, separated by a coma. In php it would look like this:
header("Access-Control-Expose-Headers: Content-length, X-My-Own-Header");
The headers will indeed be available through responseObject.getAllResponseHeaders() or something like responseObject.getResponseHeader('content-type').
More information about cross-domain requests and headers: http://www.html5rocks.com/en/tutorials/cors/
PS: Ace.Yin had the right answer, but I don't have enough reputation to simply comment.
i ran into the same issue and finally i found the solution here: http://www.html5rocks.com/en/tutorials/cors/
here is the part about the headers:
Access-Control-Expose-Headers (optional) -
The XMLHttpRequest 2 object has a getResponseHeader() method that returns the value of
a particular response header. During a CORS request, the getResponseHeader() method
can only access simple response headers.
Simple response headers are defined as follows:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
If you want clients to be able to access other headers, you have to use the
Access-Control-Expose-Headers header. The value of this header is a comma-delimited
list of response headers you want to expose to the client.
i have not verify it yet, but it seems on the right track :)