I have this fragment of code (simplified), I try to send a custom header "foo-custom" using http.post, but the request in the browser is sent as a 200 OPTIONS. What is it due?
Import
import { Http, Headers, Response, RequestOptions } from '#angular/http';
App
const myheaders = new Headers();
myheaders.append('Content-Type', 'application/json');
myheaders.append('foo-custom', 'var');
const options = new RequestOptions({headers: myheaders});
this.http.post(
this._url+'search', {}, options
).subscribe(data => {
console.log("CORRECT!");
console.log(data);
}, error => {
console.log("ERROR!");
console.log(error);
});
What is the correct way to do it?
Thanks
It looks like a preflighted request, can you check this comment
Possible duplicated thread?
Related
I have this request function that is wrappers around the fetch API to issue request to my API. But when my frontend app issues request, the headers object is always empty. What am I doing wrong ?
export function request(method, url, payload) {
const body = JSON.stringify(payload);
const headers = new Headers();
headers.append('Content-Type', 'application/json');
const parameters = {
headers: headers,
method: method,
body: body,
cache: "default"
};
return Observable.create(observer => {
fetch(url, parameters)
.then(response => {
observer.next(response);
observer.complete();
})
.catch(error => {
observer.error(error);
});
});
}
Have you checked in the network tab of the DevTools if the headers are really missing?
I have the same trouble than you describe, my Header object seems always empty from the Chrome DevTools, but if try to check a specific header like
let headers = new Headers({'X-whatever-name': 'whatever-value'});
myHeader.has('X-whatever-name'); // returns true
Also if I check the detail of the Request Headers in the DevTools (Networking tab), I can see that my custom headers are sent properly.
So only the JS api (entries, keys, ...) seems to be broken for me, but the request is correctly sent.
Try just making it a simple object:
const headers = {
"Content-Type": "application/json"
};
I am trying to send a custom HTTP Header from the front end app for it to interact with the gateway. This is my angular function:
import {Http, Headers, Response, RequestOptions } from ‘#angular/http’;
getXById(id :number){
let options = nee RequestOptions({ headers : new Headers({“X-fastgate-resource” :”resource_name}) });
return this.http.get( http://url + “/resource”, options)
I expected to see a Header with, “X-fastgate-resource” as a key, and “resource_name” as value.
What I got was this:
Request Headers:
OPTIONS http://url HTTP/1.1
host...
Access-Control-Request-Headers: x-fastgate-resource
You could try out something like below.
let headers = new HttpHeaders();
headers.append('X-fastgate-resource', 'Example');
let options = { headers: headers };
let apiUrl: string = 'http://url';
this.http.get(apiUrl, options);
Hope this helps
Try This code:
import {HttpHeaders} from '#angular/common/http';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
**With params**
const url = 'yourapi';
return this.http.post(url, {
key: value,
key1: value1
},httpOptions);
**Without Params**
const url = 'yourapi';
return this.http.post(url,httpOptions);
Try using the Angular Context.
Angular Context
This is the simplest way I know of passing data, usually to an interceptor
Define a Context Token - usually in the interceptor
export const MY_FILENAME = new HttpContextToken<string>(() => "");
Pass the data
const context = new HttpContext().set(MY_FILENAME, `${name}.pdf`)
return this.httpClient.post(url, pdf, {context: context})
Collect the data. Usually in the interceptor
const fileName = request.context.get(MY_FILENAME)
I am using a restapi and it requires that I add a token to the header before I can create a new record.
Right now I have a service to create a new record which looks like this:
service.ts
create(title, text) {
let headers: HttpHeaders = new HttpHeaders();
headers = headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
headers = headers.append('Authorization', token); // Not added yet as this is the reason for the question
return this.http.post('http://myapi/api.php/posts', {
title: 'added title',
text: 'added text'
}, { headers });
}
app.component.ts
add() {
this.service.create('my title', 'body text').subscribe(result => {
console.log(result);
});
}
The problem with this is that it won't let me add the new record because it requires a token and in order to get a token I need to run this:
getToken() {
let headers: HttpHeaders = new HttpHeaders();
headers = headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
return this.http.post('http://myapi/api.php/user', {
username: 'admin',
password: 'password'
}, { headers });
}
My question is...How do I get this two together into one call instead of two...or when is the best way to do this?
Apart from what #Pardeep Jain already mentioned, you can add an interceptor (> Angular version 4, you mentioned you're using 5) for your HttpClient that will automatically add Authorization headers for all requests.
If you need top be authenticated for only one request, it's better to keep things simple and use Pardeep's solution.
If you want to be authenticated for most of your requests, then add an interceptor.
module, let's say app.module.ts
#NgModule({
//...
providers: [
//...
{
provide: HTTP_INTERCEPTORS,
useClass: JwtInterceptor,
multi: true
},
//...
]
//...
})
and your jwt interceptor, let's say jwt.interceptor.ts
#Injectable()
export class JwtInterceptor implements HttpInterceptor {
constructor(private injector: Injector, private router: Router) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const authReq = req.clone({
headers: req.headers.set('Authorization', /* here you fetch your jwt */this.getToken())
.append('Access-Control-Allow-Origin', '*')
});
return next.handle(authReq).do((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
// do stuff with response if you want
}
}, (response: HttpErrorResponse) => { });
}
getToken() {
let headers: HttpHeaders = new HttpHeaders();
headers = headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
return this.http.post('http://myapi/api.php/user', {
username: 'admin',
password: 'password'
}, { headers });
}
}
If you want to read something, more here: https://medium.com/#ryanchenkie_40935/angular-authentication-using-the-http-client-and-http-interceptors-2f9d1540eb8
My question is...How do I get this two together into one call instead
of two...or when is the best way to do this?
You should not.
Authentication is one thing that should be performed a single time for the client or as the authentication ticket has expired.
Posting some content is another thing that you should not mix with authentication.
So authenticate the client once and store the ticket.
Then pass the ticket in the header for any request to a secured endpoints/methods. Or use a transverse way as an interceptor to set it in the send requests if you don't want to repeat the code.
The code should be like this -
create(title, text) {
let headers: HttpHeaders = new HttpHeaders();
headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
headers.append('Authorization', token);
return this.http.post('http://myapi/api.php/posts', {
title: 'added title',
text: 'added text'
}, { headers });
}
I am currently trying to call a jersey REST API that I have running locally on localhost:8080 through Angular 4 using HttpClient.
My Code:
```
ngOnInit() {
const headers = new HttpHeaders();
headers.append('Authorization', 'Basic ' + btoa('username:password'));
headers.append('Content-Type', 'application/json');
this.http.get('http://localhost:8080/mycall?myparam=myvalue', {headers: headers}).subscribe(data => {
console.log(data);
});
}
```
When I make this call I get a 401 error saying that I am Unauthorized. However, when I run this through postman it goes through just fine. What am I doing wrong here?
NOTE: this is not a CORS issue, I am currently allowing CORS through an extension on firefox
HttpHeaders are immutable, so you need to do something like that
ngOnInit() {
const headers = new HttpHeaders()
.append('Authorization', 'Basic ' + btoa('username:password'))
.append('Content-Type', 'application/json');
this.http.get('http://localhost:8080/mycall?myparam=myvalue', {headers: headers}).subscribe(data => {
console.log(data);
});
}
Actually you should not need the Content-type here for a GET request
I think the issue is that you haven't passed in your custom request headers to HttpClient, which is causing the unauthorised error.
Please try something like this.
const headers = new HttpHeaders();
headers.append('Authorization', 'Basic ' + btoa('username:password'));
headers.append('Content-Type', 'application/json');
const options = new RequestOptions({headers: headers});
this.http.get('http://localhost:8080/mycall?myparam=myvalue', options ).subscribe(data => {
console.log(data);
});
You can simply pass in your RequestOptions to http.get as it does accept headers as an optional parameter. Please check following link for more details on the usage.
https://angular.io/api/common/http/HttpClient#get
Also, remember to import RequestOptions as well.
I'm making a weather app with React.js and I want to make a CORS request for fetching data from weather underground website.
What I want is getting a city name, use autocomplete API for finding the city and fetch data for that city.
The problem is, everytime I give a city name (for example: tehran), the xhr.onerror event handler runs and I get this error:
XMLHttpRequest cannot load http://autocomplete.wunderground.com/aq?query=tehran. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access.
This is my code for fetching data:
var axios = require('axios');
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
xhr.open(method, url, true);
}
else if (typeof XDomainRequest != "undefined") {
xhr = new XDomainRequest();
xhr.open(method, url);
}
else {
xhr = null;
}
return xhr;
}
function makeCorsRequest(url) {
var autoCompleteText;
var xhr = createCORSRequest('GET', url);
if (!xhr) {
alert('CORS not supported');
return;
}
xhr.onload = function() {
var text = xhr.responseText;
autoCompleteText = text;
}
xhr.onerror = function() {
alert('Woops, there was an error making the request.');
}
xhr.send();
return autoCompleteText;
}
const WEATHER_UNDERGROUND_AUTOCOMPLETE = 'http://autocomplete.wunderground.com/aq?query=';
const WEATHER_UNDERGROUND_URL = 'http://api.wunderground.com/api/eda52d06d32d71e9/conditions/q/';
module.exports = {
getTemp: function(city) {
var encodedCity = encodeURIComponent(city);
var requestAutoComplete = `${WEATHER_UNDERGROUND_AUTOCOMPLETE}${encodedCity}`;
var autoCompleteText = makeCorsRequest(requestAutoComplete);
var foundCity = autoCompleteText.RESULTS[0].name.split(', ');
var requestUrl = `${WEATHER_UNDERGROUND_URL}${foundCity[1]}/${foundcity[0]}.json`;
return axios.get(requestUrl).then(function(res) {
return res.data.current_observation.temp_c;
}, function(err) {
throw new Error(res.data.error);
});
}
}
Screenshot of the app:
localhost:3000/weather page
Because http://autocomplete.wunderground.com/aq?query=tehran doesn’t send the Access-Control-Allow-Origin response header, you must change your frontend code to instead make the request through proxy. Do that by changing the WEATHER_UNDERGROUND_AUTOCOMPLETE value:
const WEATHER_UNDERGROUND_AUTOCOMPLETE =
'https://cors-anywhere.herokuapp.com/http://autocomplete.wunderground.com/aq?query=';
The https://cors-anywhere.herokuapp.com/http://autocomplete.wunderground.com/… URL will cause the request to go to https://cors-anywhere.herokuapp.com, a public CORS proxy which sends the request on to the http://autocomplete.wunderground.com… URL you want.
That proxy gets the response, takes it and adds the Access-Control-Allow-Origin response header to it, and then finally passes that back to your requesting frontend code as the response.
So in the end because the browser sees a response with the Access-Control-Allow-Origin response header, the browser allows your frontend JavaScript code to access the response.
Or use the code from https://github.com/Rob--W/cors-anywhere/ or such to set up your own proxy.
You need a proxy in this case because http://autocomplete.wunderground.com/… itself doesn’t send the Access-Control-Allow-Origin response header—and in that case your browser will not allow your frontend JavaScript code to access a response from that server cross-origin.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS has more details.
Incidentally, you can use curl or some other tool to verify that server isn’t sending the header:
$ curl -i -H 'Origin: http://localhost:3000' \
'http://autocomplete.wunderground.com/aq?query=tehran'
HTTP/1.1 200 OK
Content-type: application/json; charset=utf-8
Content-Length: 2232
Connection: keep-alive
{ "RESULTS": [
{
"name": "Tehran Dasht, Iran",
…
Notice there’s no Access-Control-Allow-Origin in the response headers there.
Here is a simple react component which calls the api with query params and get 's the desired result.
import React, { Component } from 'react'
import axios from 'axios';
export default class App extends Component {
componentDidMount() {
axios.get('http://autocomplete.wunderground.com/aq?query=tehran')
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
})
}
render () {
return (
<div>React simple starter</div>
)
}
}
Are you bound to using axios? if not I would highly recommend Mozilla's Fetch. To make a cors api call with fetch, do this:
var myInit = {
method: 'GET',
mode: 'cors',
credentials: 'include'
};
fetch(YOUR_URL, myInit)
.then(function(response) {
return response.json();
})
.then(function(json) {
console.log(json)
});
You can learn more here: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
If you are facing issues making CORS request, then use this simple chrome extension (Allow control Allow origin).
This will let you make CORS request without adding any extra parameter in headers/config.