axios put Not working - http

my axios code:
const instance = axios.create({
baseURL: process.env.BASE_API,
timeout: 5000,
withCredentials: true,
headers: {
'content-type': 'application/x-www-form-urlencoded;charset=UTF-8'
}
})
function get (url, getData) {
return instance.get(url, {
params: getData
})
}
function post (url, postData) {
return instance.post(url, qs.stringify(postData))
}
function put (url, putData) {
return instance.put(url, qs.stringify(putData))
}
export default {
get: get,
post: post,
put: put
}
Post request with content-type': 'application/x-www-form-urlencoded; charset=UTF-8 is useful
However, when using PUT, the request header does not have a content-type': 'application/x-www-form-urlencoded; charset=UTF-8
Causes the put request to become an options

It's not so clear from your question what exactly you are trying to ask. I'll assume you want your PUT request to actually send a PUT request instead of just an OPTIONS request. I'm also assuming that you are making requests to an API that you control.
I had the same problem (i.e. I was only seeing OPTIONS requests when I tried to make PUT calls) and I discovered that in my API I did not have the PUT options enabled in my CORS settings. I'm using rails so all I had to do is add :put to my cors middleware:
config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'
resource '*', :headers => :any, :methods => [:get, :post, :put, :options]
end
end
I figured this out based on this answer

Related

Firebase HTTP Function triggered twice when POST request sent with headers

I deployed a firebase HTTP cloud function and am experiencing this (unexpected) behavior:
when I call the function (using POST) from a browser environment with fetch(), the function gets triggered twice, one time without any data sent in the body, and another time as I would expect it. In the frontend (chrome network tab) I can only see 1 request, the successfull one.
this does only happen with POST requests
this does only happen when the request is sending headers
Is this normal behavior that I dont understand or a potential bug?
my minimal cloud function
exports.run = functions.https.onRequest(async (req, res) => {
// ALLOW CORS FOR POST REQUEST:
// => https://stackoverflow.com/a/38259193
res.set('Access-Control-Allow-Origin', '*');
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
return res.status(200).send({
status: "ok",
body: req.body,
query: req.query,
}).end();
});
calling from frontend
// example data (not a real one)
const url = "https://us-central1-myproject.cloudfunctions.net/test";
const postData = { x: 1, y: 2 };
// GET request => ✅ works as expected
fetch(url);
// POST request without headers => ✅ works as expected
fetch(url, {
method: 'POST',
body: JSON.stringify(postData),
});
// POST request with headers => ❌ 2 requests get triggered
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(postData),
});
This behavior is happening because of the CORS preflight request:
A CORS preflight request is a CORS request that checks to see if the CORS protocol is understood and a server is aware using specific methods and headers.
...
A preflight request is automatically issued by a browser, and in normal cases, front-end developers don't need to craft such requests themselves. It appears when a request is qualified as "to be preflighted" and omitted for simple requests.
As pointed in this other question:
As long as you’re adding a Content-Type': 'application/json' header to the request, the browser is going to automatically on its own do a CORS preflight OPTIONS request before trying the request from your code.
Therefore, this is a normal behavior and is not a problem of Cloud Functions for Firebase.
In order to not have the two requests, you can change the header request as suggested by this answer:
// example data (not a real one)
const url = "https://us-central1-myproject.cloudfunctions.net/test";
const postData = { x: 1, y: 2 };
// POST request with different header => ✅ only one request is triggered
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: JSON.stringify(postData),
}).then(data => console.log(data));

Angular5 Response Header (Content-Disposition) Reading

How Can I read Response Header (Content-Disposition)? Please share resolution.
When I check at either Postman or Google Chrome Network tab, I can see 'Content-Disposition' at the response headers section for the HTTP call, but NOT able to read the header parameter at Angular Code.
// Node - Server JS
app.get('/download', function (req, res) {
var file = __dirname + '/db.json';
res.set({
'Content-Type': 'text/plain',
'Content-Disposition': 'attachment; filename=' + req.body.filename
})
res.download(file); // Set disposition and send it.
});
// Angular5 Code
saveFile() {
const headers = new Headers();
headers.append('Accept', 'text/plain');
this.http.get('http://localhost:8090/download', { headers: headers })
.subscribe(
(response => this.saveToFileSystem(response))
);
}
private saveToFileSystem(response) {
const contentDispositionHeader: string = response.headers.get('Content-Disposition'); // <== Getting error here, Not able to read Response Headers
const parts: string[] = contentDispositionHeader.split(';');
const filename = parts[1].split('=')[1];
const blob = new Blob([response._body], { type: 'text/plain' });
saveAs(blob, filename);
}
I have found the solution to this issue. As per Access-Control-Expose-Headers, only default headers would be exposed.
In order to expose 'Content-Disposition', we need to set 'Access-Control-Expose-Headers' header property to either '*' (allow all) or 'Content-Disposition'.
// Node - Server JS
app.get('/download', function (req, res) {
var file = __dirname + '/db.json';
res.set({
'Content-Type': 'text/plain',
'Content-Disposition': 'attachment; filename=' + req.body.filename,
'Access-Control-Expose-Headers': 'Content-Disposition' // <== ** Solution **
})
res.download(file); // Set disposition and send it.
});
It is not the problem with Angular, is the problem with CORS.
If the server does not explicitly allow your code to read the headers, the browser don't allow to read them.
In the server you must add Access-Control-Expose-Headers in the response.
In the response it will be like Access-Control-Expose-Headers:<header_name>,
In asp.net core it can be added while setting up CORS in ConfigureServices method in startup.cs
this solution help me to get the Content-Disposition from response header.
(data)=>{ //the 'data' is response of file data with responseType: ResponseContentType.Blob.
let contentDisposition = data.headers.get('content-disposition');
}
Firstly you need to allow your server to expose these headers. Note that it will show in you browser network tab, regardless if you have these settings. This makes it 'available'.
With C# it would look something like this:
services.AddCors(options => {
options.AddPolicy(AllowSpecificOrigins,
builder => {
builder
.WithOrigins("http://localhost:4200")
.AllowAnyHeader()
.AllowAnyMethod()
.WithExposedHeaders("Content-Disposition", "downloadFileName");
});
});
When you send your API request to the server ensure that you include the "observe" in you return. See below:
getFile(path: string): Observable<any> {
// Create headers
let headers = new HttpHeaders();
// Create and return request
return this.http.get<Blob>(
`${environment.api_url}${path}`,
{ headers, observe: 'response', responseType: 'blob' as 'json' }
).pipe();
}
Then in your response of your angular on your subscribe you can access your filename like this (the subscribe method is not complete it attaches to a pipe function)
.....
.subscribe((response: HttpResponse<Blob>) => {
const fileName = response.headers.get('content-disposition')
.split(';')[1]
.split('filename')[1]
.split('=')[1]
.trim();
});

How to correctly set Http Request Header in Angular 2

I have an Ionic 2 application using Angular 2, which is sending an Http PUT to a ASP.NET Core API server. Here's the method I'm using to send the request:
public update(student: Student): Promise<Student>
{
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('authentication', `${student.token}`);
const url = `${this.studentsUrl}`;
return this.http
.put(url, JSON.stringify(student), { headers: headers })
.toPromise()
.then(() => student)
.catch(this.handleError);
}
I'm setting an authentication key/value on the headers object.
But when I receive this request on the server, I cannot find the authentication key on the header:
As you can see in the picture, there are many keys on the header, but not the content and authentication keys that I manually added to the header in the client application.
What am I doing wrong?
Your parameter for the request options in http.put() should actually be of type RequestOptions. Try something like this:
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('authentication', `${student.token}`);
let options = new RequestOptions({ headers: headers });
return this.http
.put(url, JSON.stringify(student), options)
Angular 4 >
You can either choose to set the headers manually, or make an HTTP interceptor that automatically sets header(s) every time a request is being made.
Manually
Setting a header:
http
.post('/api/items/add', body, {
headers: new HttpHeaders().set('Authorization', 'my-auth-token'),
})
.subscribe();
Setting headers:
this.http
.post('api/items/add', body, {
headers: new HttpHeaders({
'Authorization': 'my-auth-token',
'x-header': 'x-value'
})
}).subscribe()
Local variable (immutable instantiate again)
let headers = new HttpHeaders().set('header-name', 'header-value');
headers = headers.set('header-name-2', 'header-value-2');
this.http
.post('api/items/add', body, { headers: headers })
.subscribe()
The HttpHeaders class is immutable, so every set() returns a new instance and applies the changes.
From the Angular docs.
HTTP interceptor
A major feature of #angular/common/http is interception, the ability to declare interceptors which sit in between your application and the backend. When your application makes a request, interceptors transform it before sending it to the server, and the interceptors can transform the response on its way back before your application sees it. This is useful for everything from authentication to logging.
From the Angular docs.
Make sure you use #angular/common/http throughout your application. That way your requests will be catched by the interceptor.
Step 1, create the service:
import * as lskeys from './../localstorage.items';
import { Observable } from 'rxjs/Observable';
import { Injectable } from '#angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpHeaders } from '#angular/common/http';
#Injectable()
export class HeaderInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (true) { // e.g. if token exists, otherwise use incomming request.
return next.handle(req.clone({
setHeaders: {
'AuthenticationToken': localStorage.getItem('TOKEN'),
'Tenant': localStorage.getItem('TENANT')
}
}));
}
else {
return next.handle(req);
}
}
}
Step 2, add it to your module:
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: HeaderInterceptor,
multi: true // Add this line when using multiple interceptors.
},
// ...
]
Useful links:
Interceptor not working properly.
APP_INITIALIZER not working in combination with interceptor
For us we used a solution like this:
this.http.get(this.urls.order + '&list', {
headers: {
'Cache-Control': 'no-cache',
}
}).subscribe((response) => { ...
Reference here
We can do it nicely using Interceptors. You dont have to set
options in all your services neither manage all your error responses,
just define 2 interceptors (one to do something before sending the
request to server and one to do something before sending the server's
response to your service)
Define an AuthInterceptor class to do something before sending the request to the server. You can set the api token (retrieve it from localStorage, see step 4) and other options in this class.
Define an responseInterceptor class to do something before sending the server response to your service (httpClient). You can manage your server response, the most comon use is to check if the user's token is valid (if not clear token from localStorage and redirect to login).
In your app.module import HTTP_INTERCEPTORS from '#angular/common/http'. Then add to your providers the interceptors (AuthInterceptor and responseInterceptor). Doing this your app will consider the interceptors in all our httpClient calls.
At login http response (use http service), save the token at
localStorage.
Then use httpClient for all your apirest services.
You can check some good practices on my github proyect here
This should be easily resolved by importing headers from Angular:
import { Http, Headers } from "#angular/http";
You have a typo.
Change: headers.append('authentication', ${student.token});
To: headers.append('Authentication', student.token);
NOTE the Authentication is capitalized
The simpler and current approach for adding header to a single request is:
// Step 1
const yourHeader: HttpHeaders = new HttpHeaders({
Authorization: 'Bearer JWT-token'
});
// POST request
this.http.post(url, body, { headers: yourHeader });
// GET request
this.http.get(url, { headers: yourHeader });

Http REST call problems No 'Access-Control-Allow-Origin' on POST

I have not been able to get Angular $http to communicate with a remote REST service. I have tried Restangular and $resource too. The problem seems to be with the underlying $http service and CORS limitations. I think I just need to get my headers right. Do I also need to tweak my server config?
I am getting the following error:
XMLHttpRequest cannot load http://www.EXAMPLE-DOMAIN.com/api/v2/users/sign_in. No 'Access-Control- Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8100' is therefore not allowed access.
I have researched this a lot. Currently I have tried setting the $httpProvider headings when configuring my app module and played with $http headers. Below is some of my current code.
My Service
app.service('Auth', function($http) {
var headers = {
'Access-Control-Allow-Origin' : '*',
'Access-Control-Allow-Methods' : 'POST, GET, OPTIONS, PUT',
'Content-Type': 'application/json',
'Accept': 'application/json'
};
return $http({
method: "POST",
headers: headers,
url: 'http://www.EXAMPLE-DOMAINcom/api/v2/users/sign_in',
data: {"email":"my#email.com","password":"secret"}
}).success(function(result) {
console.log("Auth.signin.success!")
console.log(result);
}).error(function(data, status, headers, config) {
console.log("Auth.signin.error!")
console.log(data);
console.log(status);
console.log(headers);
console.log(config);
});
});
App Config
var app = angular.module('starter', ['ionic', 'app.controllers', $httpProvider])
.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
$httpProvider.defaults.headers.common = 'Content-Type: application/json';
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}
])
If you are in control of the server, you might need to set the required headers there. Depending on which server, this might help: http://enable-cors.org/server.html

ASP .NET 4 Wev Api controller - modify response type

Let’s say I’ve got a simple Web API controller. I want to return a basic .NET type as a result. For example:
public class LoginController : ApiController
{
[HttpPost]
public bool Authenticate(LoginUserViewModel loginUserViewModel)
{
return true;
}
}
I’m getting different results in different browsers even if the request is exactly the same for all of them.
In Chrome and IE7 I get Content-Type in Response Headers as application/json; charset=utf-8, and response value equal to "true".
Firefox recognizes response Content-Type as application/xml; charset=utf-8 and sets response value to:
"<boolean xmlns="http://schemas.microsoft.com/2003/10/Serialization/">true</boolean>"
Is there any way to set response type on the server-side so it is always the same?
Thanks.
UPDATE: Here is JavaScript that I use to call my controller.
Ext.Ajax.request({
async: false,
url: 'Login/Authenticate',
defaultHeaders: { 'Accept': 'application/json' },
jsonData: user,
success: function (response, options) {
if (response.responseText !== 'true') {
Ext.Msg.alert('Error', 'Login failed, please try again');
} else {
document.location = 'Main.aspx';
}
},
failure: function (response, options) {
Ext.MessageBox.hide();
Ext.Msg.alert('Error', 'Server error. Cannot authenticate user.');
}
});
This is because browsers send different Accept headers. Web API uses the accept header to determine the content-type of the response. By default Web API loads up a few different formatters into it's configuration.Formatters collection.
One way to force the response of to be a specific media-type is to remove all the existing formatters and add only the one you want.
configuration.Formatters.Clear();
configuration.Formatters.Add(new JsonMediaTypeFormatter());

Resources