Local hosted API not accessible from Angular 4 http - php

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.

Related

Access to XMLHttpRequest has been blocked by CORS policy while making GET Request on Angular

I'm trying to make a GET method to my PHP API. In order to do that, I have this code:
export class PerfilComponent {
perfil: any;
constructor(private http: HttpClient) { }
ngOnInit() {
const token:string | null = localStorage.getItem('token')
console.log(token)
const headers = new HttpHeaders({'api_key': token!})
this.http.get("http://localhost:8000/api/usuario/mi-usuario", {headers})
.subscribe(
resultado => {
this.perfil = resultado;
}
);
console.log(this.perfil)
}
The API needs the token to be send through header.
This is the error I get everytime I try to send the GET request:
Access to XMLHttpRequest at 'http://localhost:8000/api/usuario/mi-usuario' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
I'll put the PHP code, maybe the error is in there, but I don't think so since while trying on Postman it did work well:
#[Route('/api/usuario/mi-usuario', name: 'app_mi_usuario', methods: ['GET'])]
#[OA\Tag(name: 'Usuario')]
#[Security(name: "apikey")]
#[OA\Response(response:200,description:"successful operation" ,content: new OA\JsonContent(type: "array", items: new OA\Items(ref:new Model(type: UsuarioDTO::class))))]
public function miUsuario(UsuarioRepository $usuarioRepository,
Request $request,Utilidades $utils): JsonResponse
{
if ($utils->comprobarPermisos($request,1)) {
$apikey = $request->headers->get("apikey");
$id_usuario = Token::getPayload($apikey)["user_id"];
$usuario = $usuarioRepository->findOneBy(array("id"=>$id_usuario));
return $this->json($usuario, 200, [], [
AbstractNormalizer::IGNORED_ATTRIBUTES => ['__initializer__', '__cloner__', '__isInitialized__'],
ObjectNormalizer::CIRCULAR_REFERENCE_HANDLER=>function ($obj){return $obj->getId();},
]);
} else {
return $this->json([
'message' => "No tiene permiso",
]);
}
}
Thanks!
Cross-Origin Resource Sharing (CORS) is a mechanism that supports secure requests and data transfers from outside origins (domain, scheme, or port).
For example, example.com uses a text font that’s hosted on fonts.com. When visiting example.com, the user’s browser will make a request for the font from fonts.com. Because fonts.com and example.com are two different origins, this is a cross-origin request. If fonts.com allows cross-origin resource sharing to example.com, then the browser will proceed with loading the font. Otherwise, the browser will cancel the request.
CORS is blocked in modern browsers by default (in JavaScript APIs). You have to handle cors error in your backend. for this follow this link.

Preflight Headers Not As Expected

I'm trying to send a request from an Angular 8 app to Laravel 5.8 Passport API, but without success. I mean, with only a limited success. When I set withCredientials into the Angular request to true, the Preflight headers are trying to see whether the API would return proper headers, including Access-Control-Allow-Credentials: true, but the response shows that there's no such header, even though I'm setting it into the backend.
If I don't set withCredentials, the response headers include Access-Control-Allow-Credentials: true, just as expected, but I need that response in the preflight response as well.
I have tried enveloping the preflight request case in separate block, using
if (!$_SERVER['REQUEST_METHOD']=='OPTIONS')
and setting that header response explicitly, but without success as well.
A side note is that requesting that same URL from Postman works as expected (ever since Postman doesn't mess with CORS).
The request is being fired from the following code snippet:
await this.http.post(this.logInEndPoint, credentials, {
headers: this.httpHeaders,
withCredentials: true
}).subscribe(async res => {
...
The CORS middleware looks as follows:
$res->headers->set('Content-Type', 'application/json');
$res->headers->set('Access-Control-Allow-Origin', 'http://127.0.0.1:4200');
$res->headers->set('Access-Control-Allow-Credentials', 'true');
$res->headers->set('Access-Control-Max-Age', '60');
$res->headers->set('Access-Control-Allow-Headers', 'x-requested-with, Content-Type, origin, authorization, accept, client-security-token');
$res->headers->set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
if (!$_SERVER['REQUEST_METHOD']=='OPTIONS') {
$next($res);
} else {
return $res;
}
And that's where I define the use of the CORS middleware
api.php
Route::middleware('web', 'json.response', 'cors')->group(function() {
Route::post('login', 'AuthController#login');
...
I expect "Successfully logged in" message, but instead got Access to XMLHttpRequest at 'http://127.0.0.1:8000/api/login' from origin 'http://127.0.0.1:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute. in the Developer Tools.
After several days of struggling with that problem, I finally figured it out.
If I have to summarize the majority of problems, related to Angular consuming Laravel APIs, I'd point out the header settings in the backend. For me it was somewhere within the custom CORS middleware.
If I have to be honest, I'm not sure what's exactly wrong in the configuration above (I guess it is in the Allow-Methods header), but I'd share how I fixed my issue.
First of all, I removed all the custom middlewares I made. I started using the Barry vd. Heuvel's CORS one, adding it for the API's group only. There, I changed the default configuration. I set allowOrigins to my Angular server URI and allowedMethods to the requests I'm expecting to use. After publishing the CORS configuration file, I made sure to run my Angular app on 127.0.0.1 (and not on localhost). The same I did for the Laravel server.
After running both the apps on one and the same IP address and using the new CORS middleware, everything ran smoothly and without problems.

Safari on IOS ignoring CORS / not sending CORS preflight OPTIONS request

I have a problem using Axios and VUEJS where I keep on getting an error xmlhttprequest cannot load XXX due to access control checks on Safari only. The application works find on Chrome and Brave, but not Safari.
In addition, Safari seems to make a CORS request for the expected URL, but then cancels it without waiting for the response.
My endpoint is written in PHP
$origin = 'http://192.168.1.6:8080'; // For testing
$response = $response->withAddedHeader('Access-Control-Allow-Origin', $origin);
$response = $response->withAddedHeader('Access-Control-Allow-Headers', 'Authorization,Origin,X-Requested-With,Content-Type,Range');
$response = $response->withAddedHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
The problem seems to be due Safari not having previously defined CORS data for the website. The API call in Axios was the result of a 302 response (I don't know if that is relevant) and for whatever reason it did not like that.
The solution was to add a ping request that established CORS (at least, that's what I think it's doing) when the application first loads. Like this:
this.$axios.get('api/ping').then(() => {
if (localStorage.getItem('id_token')) {
this.$router.push('/app/requests')
}
})
That ping, which just has an unauthenticated reply with CORS headers, allows subsequent API calls to execute just fine.

Http.post() AJAX no response from JSP/Spring server

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.

Access-Control-Allow-Origin angularjs to php

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

Categories