In my Ionic 2 app, I'm fetching data from example site www.my-website.com/member/loginPersist.do using this block of code :
This /member/loginPersist is a Spring MVC controller
login-service.ts
private GetUser(user : User): Promise<any> {
let options = this.makeHeader("post");
let x = this.http.post("http://my-website.com/member/loginPersist.do", this.serialize(user) ,options).toPromise().catch(this.handleError);
return x;
}
private makeHeader(method : string){
let headers = new Headers({
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Accept': 'application/json, text/javascript, */*; q=0.01',
'X-Requested-With': 'XMLHttpRequest'
});
let options = new RequestOptions({ headers: headers });
return options;
}
But I get no response/ Blank(no json results) and status code is 200 OK. But the response is empty.
But If I change the url to www.site.com/test.php I get a response from a PHP. So I know that my http request code is not the problem.
Is this problem related to the JSP/Spring server? and How would I get the JSON response
My guess based on the information you provided is that you are having cross origin request issues. Please make sure in your controller you have allowed cross origin requests for the method. Just anotate the method with
#CrossOrigin
This will basically indicate to your server that should respond to the browser that it accepts requests from any domain. You can also restrict the domains you want to allow. More here http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cors.html
If you don't enable this on the server the browser will not complete the request due to security policies.
Related
I am having trouble when I'm trying to initialise a Channel.
I've followed some tutorials provided (https://getstream.io/blog/chat-messaging-with-laravel/, https://getstream.io/blog/tutorial-build-customer-support-chat-with-laravel-vue-and-stream/) that have a stack as mine (Laravel + Vue)
I am already getting the token on the backend, initializing the Client, setting the User and the token on the client.
But when I try to do this.channel.watch(); or even a simple channels query like
const filter = { type: 'messages', id: '1000056864'};
const sort = { last_message_at: -1 };
const channels = await this.client.queryChannels(filter, sort, {
watch: true,
state: true,
});
It will return to me the error as follows:
Access to XMLHttpRequest at 'https://chat-us-east-1.stream-io-api.com/channels/messages/1000056864/query?user_id=62&api_key=2e******e2&connection_id=5983f850-3d50-4ac3-9c06-d9e0fdaf7212' from origin 'http://local.site.test' has been blocked by CORS policy: Request header field x-csrf-token is not allowed by Access-Control-Allow-Headers in preflight response.
Everything is working on the backend, even the equivalent calls.
Based on the error you are receiving, it looks like you are including your CSRF token to all your AJAX requests. Stream API servers have a whitelist of headers that you can pass, this is to safe developers from sending sensitive data by accident. In this specific case it is arguable that csrf-token could be in such whitelist for the sake of ease of use.
Perhaps you are using something like this on your frontend?
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
If that's the case my suggestion is to opt for a more fine grained solution such as:
$.ajaxSetup({
url: "/laravel/",
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
Or make sure that only your Laravel backend receives the CSRF token by extracting JS code doing Ajax calls.
CSRF tokens are not as valuable as session IDs but they exist to make your application more secure and are not meant to be shared with 3rd parties.
For an Angular 5 app, I have an auth service that does a HTTP POST which returns the session cookie (CORS) as shown below in the code below:
signIn(signInRequest: SignInRequest): Observable<SignInResponse> {
let headers: Headers = new Headers();
headers.append('Content-Type','application/json');
return this.http
.post("/login", {email: signInRequest._email,password:signInRequest._password}, { headers: headers, withCredentials: true })
.map(this.extractData)
.catch(this.handleErrorObservable);}
The response of the header contains the set-cookie as shown below:
and the request header is the following:
I know that the browser should be setting the cookie response. Why is it not doing it?
Your frontend is hosted on localhost:4200 and your backend is hosted on api.safra.me. By default, your browser won't send the cookies along the request unless you use the withCredentials in the login request as you already did, and all of the subsequent requests.
I've got a very strange issue.
local hosted PHP Slim App using XAMPP (localhost:4040)
local hosted Angular 4 App using CLI (localhost:4200)
Making API Requests using "Postman" and browser is no problem, everything works fine.
Now I'm integrating the requests into my Angular app using import { Headers, Http } from '#angular/http'; and observables.
const requestUrl = 'http://localhost:4040/register';
const headers = new Headers({
'content-type': 'application/x-www-form-urlencoded'
});
this.http
.get(requestUrl, {headers: headers})
.map(response => response.json())
.subscribe(result => {
console.log(result);
}, error => {
console.log(error);
});
The request always fails with:
Failed to load http://localhost:4040/register: 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:4200' is therefore not allowed access.
But: I am definitely sending these headers!
public static function createJsonResponseWithHeaders($response, $requestedData)
{
// Add origin header
$response = $response->withHeader('Access-Control-Allow-Origin', '*');
$response = $response->withHeader('Access-Control-Allow-Methods', 'GET');
// Add json response and gzip compression header to response and compress content
$response = $response->withHeader('Content-type', 'application/json; charset=utf-8');
$response = $response->withHeader('Content-Encoding', 'gzip');
$requestedData = json_encode($requestedData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT);
$response->getBody()->write(gzencode($requestedData), 9);
if (!$requestedData || (count($requestedData) === 0)) {
return $response->withStatus(404)->write('Requested data not found or empty! ErrorCode: 011017');
}
return $response;
}
What I already tried for solving:
Run Slim App inside a Docker Container to get a different origin than localhost - same behaviour
Add allow-origin-header right on top of the index.php
header('Access-Control-Allow-Origin: *'); - same behaviour
Your requests are blocked because of CORS not being set up properly. There are other questions that address this, e.g. How to make CORS enabled requests in Angular 2
What you should ideally look at using is a proxy that forwards your requests to the API, the latest Angular CLI comes with support for a dev proxy (see https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/proxy.md) out of the box. You set it up with a proxy.conf.json that could look like this:
{
"/api": {
"target": "http://localhost:4040",
"secure": false,
"pathRewrite": {"^/api" : ""}
}
}
What this piece of code does is any requests from Angular to a URI matching /api will be forwarded to localhost:4040.
Note that you will also need to figure out how your app will talk to the API server in a non-dev environment. I have been happy with using Nginx to serve Angular files, and act as proxy for the API.
Sorry, my bad. The solution is simple:
The "Cache-control" header in the request seems to be not allowed, although it worked fine when testing the api with Postman.
I removed the header from the request and everything worked well.
I have a webservice (Actually generated with the drupal 7 plugin Service) accepting requests on 'http://mywebsite:8080/api_name/user' and checking possible connections against an 'api-key' variable in the requests, containing sort of a password.
Trying to connect to the service with my ionic2/cordova app result in a fail for the payload of the request results empty on the server side ($_REQUEST is an empty array and does not contain the api-key required, so that the request fails).
Debugging the request with https://requestb.in/ shows that the variable is in the body of the request, as expected.
The webservice is working well, on its side, for a post request sent by Google Postman go on successfully (thought I noticed a difference sending the postman request to requestb.in: 'api-key' parameter is not in the body, but in a part called "Form/Post Parameters" and the Content type is 'multipart/form-data' instead of application/json)
I am a bit confused about how my code (that seems correct) is not working...
Here is the code of the function trying to connect to the webservice:
register(username,password,email){
var headers = new Headers();
headers.append('Accept', 'application/json');
let options = new RequestOptions({ headers: headers });
let postParams = {
"api-key" : "someSecret",
"name" : "something",
"mail" : "somethingelse"
}
this.http.post("http://mywebsite:8080/api_name/user", postParams, options).subscribe(data => {
console.log("ok");
}, error => {
console.log("error:"+error);// Error getting the data
});
}
EDIT: i'm reading that "$_REQUEST is used to collect data after submitting an HTML form" so the problem lays actually in sending the request as a 'multipart/form-data' but how I can do that? Changing the 'Content-type' in the header seems not to be enough. I tried adding this (with no fortune)
headers.append('Content-type', 'multipart/form-data');
After hours of attempts, here it is the correct code:
register(username,password,email){
var headers = new Headers();
headers.append('enctype', 'multipart/form-data');
let options = new RequestOptions({ headers: headers });
let postParams:FormData = new FormData();
postParams.append("api-key", "something secret");
this.http.post("http://mywebsite:8080/api_name/user", postParams, options).subscribe(data => {
console.log("ok");
}, error => {
console.log("error:"+error); // Error getting the data
});
}
my scenario is composed by two webserver one local and one remote.
Local webserver (Apache) process a web app in which I want make an ajax request to remote webserver (Lighttpd).
Ajax request use angularjs $http.
var req = {
method: 'POST',
url: 'http://url/myphp.php',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/x-www-form-urlencoded'
},
xhrFields: {
withCredentials: true
},
crossDomain: true,
data: xmlString
}
$http(req).then(function () {
console.log("OK!");
});
Remote php script is:
<?php
echo "You have CORS!";
?>
Unfortunately I got a
401 Unhauthorized
XMLHttpRequest cannot load http://url/myphp.php. 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:8888' is therefore not allowed access. The response had HTTP status code 401.
Remote web server has .htpasswd authentication mode enable and CORS request configured.
Follow a piece of lighttpd.conf
setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )
For add-response-header to work in lighttpd you must enable mod_setenv in your server.modules. However, you have to enable this mod_setenv before mod_status.
server.modules = (
# ...
"mod_fastcgi",
"mod_rewrite",
"mod_redirect",
"mod_setenv", ## before mod_status
"mod_status",
# ...
)
Alternatively you could use PHP to output the cors header
<?php
header("Access-Control-Allow-Origin: *");
?>
I also want to add that if you are sending http basic/digest auth data you cannot use wildcards for the origin. You have to use the actual source domain
setenv.add-response-header = ( "Access-Control-Allow-Origin" => "example.com" )
setenv.add-response-header = ( "Access-Control-Allow-Credentials" => "true" )
Because you are doing a cross domain POST, Angular is making a pre-flight OPTIONS request to check the Access Origin headers before making the POST.
The NET tab in your browser will confirm this.
Your server isn't responding well to the OPTIONS request and therefore Angular refuses to make the POST.
If you POST to your server with POSTMAN is everything OK?
I believe it is possible to configure Angular to not make the pre-flight request.
Alternatively, configure your server to respond correctly to OPTIONS requests, in particular returning the correct Access Origin headers in response to the OPTIONS request. (OPTIONS is just trying to find out if your server has these headers set, if it hasn't then why bother making the POST?)
Hopefully this information will point you in the right direction.
* can not be used in the case of credentials.
Server is disregarding your
setenv.add-response-header statement.
See the answer here:
CORS: Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true