I would like to make a get request from my Ionic app to an API build with the Slim Framework.
This is the code of the API:
<?php
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json');
?>
<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
use Tuupola\Middleware\HttpBasicAuthentication;
require 'vendor/autoload.php';
$jwt_secret = '**************';
$app = new Slim\App;
$app->add(new Tuupola\Middleware\JwtAuthentication([
"path" => "/api",
"attribute" => "jwt",
"secret" => $jwt_secret, "error" => function ($response, $arguments) {
$data["status"] = "error";
$data["message"] = $arguments["message"];
return $response
->withHeader("Content-Type", "application/json")
->withHeader("Access-Control-Allow-Origin", "*")
->getBody()->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
}
]));
$app->get('/api/hello', function (Request $request, Response $response, array $args)
{
$decoded = $request->getAttribute("jwt");
$response->getBody()->write(json_encode(array("status"=> "200", "message" => "HELLO ".$decoded['uid'] ." - " . $decoded['cus'])));
return $response;
});
$app->get('/', function (Request $request, Response $response, array $args) {
$response->getBody()->write(json_encode(array("status"=> "200", "message" => "Welcome to the API")));
return $response;
});
$app->run();
?>
When I'm testing with postman the API works fine. But when I'm trying to call it with the HTTPClient in Ionic, it doesn't work. This is my Ionic Code:
import { Component } from '#angular/core';
import { HttpClient, HttpHeaders } from '#angular/common/http';
#Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {
constructor(private http: HttpClient)
{
}
sendRequest()
{
this.http.get('http://localhost/slim3',).subscribe((data)=>console.log(data));
}
}
The Error message is the following:
:8100/home:1 Access to XMLHttpRequest at 'http://localhost/slim3' from origin 'http://localhost:8100' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
core.js:6014 ERROR HttpErrorResponse {headers: HttpHeaders, status: 0, statusText: "Unknown Error", url: "http://localhost/slim3", ok: false, …}
How can I fix it? Thanks!
You must enable CORS(What is CORS?) in Slim Framework. Check http://www.slimframework.com/docs/v3/cookbook/enable-cors.html
Add this before $app->run(); (replacing http://mysite by your url, including port)
$app->options('/{routes:.+}', function ($request, $response, $args) {
return $response;
});
$app->add(function ($req, $res, $next) {
$response = $next($req, $res);
return $response
->withHeader('Access-Control-Allow-Origin', 'http://mysite')
->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
});
Related
I have a issue with my slim app, i want send json responses but with customed headers. My code is like follow:
index.php
require 'vendor/autoload.php';
require 'app/config.php';
require 'app/libs/api.cs.php';
$app = new Slim\App(
[
"settings" => $config,
"apics" => function() { return new APIHelper(); } //This is a class that contain a "helper" for api responses
]
);
require 'app/dependences.php';
require 'app/middleware.php';
require 'app/loader.php';
require 'app/routes.php';
// Run app
$app->run();
app/libs/api.cs.php (The "helper")
<?php
class APIHelper
{
public function sendResponse($response, $status='success' ,$code = 200, $message = "", $data = null)
{
$arrResponse = array();
$arrResponse['status'] = $status;
$arrResponse['code'] = $code;
$arrResponse['message'] = $message;
$arrResponse['data'] = $data;
return $response
->withHeader('Access-Control-Allow-Origin', '*')
->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization, AeroTkn')
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
->withHeader('Content-Type','application/json')
->withHeader('X-Powered-By','My API Server')
->withJson($arrResponse,$code);
}
}
my routes file (app/routes.php)
$app->group('/foo', function () {
$this->get('', function ($req, $res, $args) {
return $this->apics->sendResponse($res, 'success' ,200, "Foo API Index By Get", null);
});
$this->post('', function ($req, $res, $args) {
try{
$oBody = $req->getParsedBody();
return $this->apics->sendResponse($res, 'success' ,200, "Foo API POST Response", $oBody);
}
catch(\Exception $ex){
return $this->apics->sendResponse($res, 'error' ,500, "Process Error", array('error' => $ex->getMessage()));
}
});
});
When i trying to run my app with request body, the result is the follow:
Headers:
connection →Keep-Alive
content-type →text/html
date →Wed, 30 Aug 2017 02:22:56 GMT
keep-alive →timeout=2, max=500
server →Apache
transfer-encoding →chunked
Body (returns as simple text and not json encoded)
{"status":"success","code":200,"message":"Foo API POST Response","data":{"one":"1", "two":"2"}}
I've trying put this class as a middleware, but i'm some confused in these subject.
Can you help me telling me if these method is good or where i'm bad.
Thanks to all and i hope for your answers! Nice day
Using Middleware is the ideal answer for your problem
Just add this function in your middeleware file
$app->add(function ($req, $res, $next) {
$response = $next($req, $res);
return $response
->withHeader('Access-Control-Allow-Origin', 'http://mysite')
->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
->withHeader('Content-Type','application/json');
->withHeader('X-Powered-By','My API Server');
});
I found the "error" was a kindergarden issue hahaha, I've download all my code from the web server for test in my machine, I have the same result, but i found that all my files had strange characters at start, so i re-save the files as utf-8 and the problem is solved. Little details that can create headaches!. Thanks to Nica and Ramy. Ramy: the solution was excellent, now the code are more organizated, i take this practice. Good day to all.
I have the same problem again.
Old post here
I have a angular app and SlimFramework for api connect.
Local it works fine but when i publish to my Website come the error that my Header no set.
But the info on the API testing tool says it's allowed from * IP.
Can someone help me?
Here a valid token: Basic TyOSZcfBwMC6DR9kbAWeMnPmhF4ohZu2n9LccQEyt6uXNt8PTT
Thx
$app = new \Slim\App(["settings" => $config]);
$container = $app->getContainer();
$app->options('/{routes:.+}', function ($request, $response, $args) {
return $response;
});
$app->add(function ($req, $res, $next) {
$response = $next($req, $res);
return $response
->withHeader('Access-Control-Allow-Origin', '*')
->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
->withHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, DELETE, PUT');
});
$container['logger'] = function($c) {
$logger = new \Monolog\Logger('my_logger');
$file_handler = new \Monolog\Handler\StreamHandler("../../logs/app.log");
$logger->pushHandler($file_handler);
return $logger;
};
$app->get('/token', function ($request, $response){
$db = new DbOperation();
if (!$request->hasHeader('Authorization')) {
return $response->withJson([
"success"=> false,
"message" => "Header not set.",
"textcode"=> "MSG2"
], 401);
}
$token = $request->getHeader('Authorization');
if($db->checkToken($token[0])){
$user = $db->userInfo($token[0]);
if($db->checkActivate($user['auth_user'])){
if($db->checkExpired($user['auth_user'])){
return $response->withJson([
"success"=> false,
"message" => "The validity of the login has expired. If you have any questions, please contact the administrator..",
"textcode"=> "MSG6"
], 401);
} else {
return $response->withJson(["success"=> true], 200);
}
} else {
return $response->withJson([
"success"=> false,
"message" => "This account has not yet been activated.",
"textcode"=> "MSG8"
], 401);
}
} else {
return $response->withJson([
"success"=> false,
"message"=>'Invalid token',
"textcode"=> "MSG1"
], 403);
}
});
Your basic auth credentials do not decode into anything meaningful. PHP tends to silently ignore Authorization headers which it thinks are malformed. Try with something like Basic dGVzdDp0ZXN0 which decodes into test:test.
Workaround for this has however been added to Slim starting from version 3.5.0. Upgrading your Slim installation might also help.
From the question above, I have api/user that contains get, post, put, and delete methods. Is it possible to have passthrough on a specific method?
Example, the public method only is get and the rest needs a token to use that method?
Thank you for your answer.
$app->add(new \Slim\Middleware\JwtAuthentication([
"path" => ["/api", "/admin"],
"passthrough" => ["/api/login", "/admin/ping", "/api/user"],
"algorithm" => "HS256",
"secret" => getenv("JWT_SECRET"),
"callback" => function ($request, $response, $arguments) use ($container) {
$container["jwt"] = $arguments["decoded"];
},
"error" => function ($request, $response, $arguments) {
$data["status"] = "error";
$data["message"] = $arguments["message"];
return $response
->withHeader("Content-Type", "application/json")
->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
}]));
By default JWT Authentication middleware does not authenticate OPTIONS requests. To also allow unauthenticated GET requests you can manually add it to the RequestMethodRule. Your example code would become something like following.
require __DIR__ . "/vendor/autoload.php";
$app = new \Slim\App;
$container = $app->getContainer();
$app->add(new \Slim\Middleware\JwtAuthentication([
"path" => ["/api"],
"secret" => getenv("JWT_SECRET"),
"callback" => function ($request, $response, $arguments) use ($container) {
$container["jwt"] = $arguments["decoded"];
},
"rules" => [
new \Slim\Middleware\JwtAuthentication\RequestMethodRule([
"passthrough" => ["OPTIONS", "GET"]
])
],
"error" => function ($request, $response, $arguments) {
$data["status"] = "error";
$data["message"] = $arguments["message"];
return $response
->withHeader("Content-Type", "application/json")
->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
}
]));
$app->get("/api/user", function ($request, $response) {
print "Hello\n\n";
});
$app->post("/api/user", function ($request, $response) {
print "Hello\n\n";
});
$app->run();
This would yield.
$ curl --request GET --include http://127.0.0.1:8080/api/user
HTTP/1.1 200 OK
Host: 127.0.0.1:8080
Connection: close
X-Powered-By: PHP/7.0.12
Content-Type: text/html; charset=UTF-8
Content-Length: 7
Hello
$ curl --request POST --include http://127.0.0.1:8080/api/user
HTTP/1.1 401 Unauthorized
Host: 127.0.0.1:8080
Connection: close
X-Powered-By: PHP/7.0.12
Content-Type: application/json
Content-Length: 59
{
"status": "error",
"message": "Token not found"
}
Yes, you can use Slim Middleware and group authorized routes together and add the middleware to the group:
$validateUser = function($request,$response,$next) {
$token = $_COOKIE['token'];
$token = JWT::decode($token,$secret,['HS256']);
if ($token->user->isAdmin) {
return $next($request,$response);
}
return $response->withStatus(403)->withJson(array('message' => 'Forbidden'));
};
$app->get('/api/user',function($request,$response) {
return $response->withJson(array('message' => 'Public route'));
});
$app->group('/api/user',function() {
$this->delete('','');
$this->post('','');
$this->patch('','');
})->add($validateUser);
This code works, but how can I get the permissions to see the /api content with a get request??
<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
require 'vendor/autoload.php';
$app = new \Slim\App();
$app->add(new \Slim\Middleware\JwtAuthentication([
"path" => "/api",
"secret" => "1234"
]));
$app->get('/api', function (Request $request, Response $response) {
echo "Hi";
});
$app->get('/teste', function (Request $request, Response $response) {
echo "Hi";
});
$app->run();
1. Generate Token
Using firebase/php-jwt
$payload = [
"sub" => "user#example.com"
];
$token = JWT::encode($payload,'JWT-secret-key');
2. .htaccess Changes
If using Apache add the following to the .htaccess file. Otherwise PHP wont have access to Authorization: Bearer header
RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
3. Middleware
$app->add(new \Slim\Middleware\JwtAuthentication([
"path" => "/api",
"passthrough" => ["/teste"],
"secret" => "JWT-secret-key",
"secure" => false,
"callback" => function ($request, $response, $arguments) use ($container) {
$container["jwt"] = $arguments["decoded"];
},
"error" => function ($request, $response, $arguments) {
$data["status"] = "0";
$data["message"] = $arguments["message"];
$data["data"] = "";
return $response
->withHeader("Content-Type", "application/json")
->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
}
]));
4. Correct Request
5. Wrong Token Request
Reference Link
i used Authorization: Bearer Mykey , the key need to be encode in jwt mode
I'm having trouble setting the encoding on the response.
Tried:
$app->contentType('text/html; charset=utf-8');
header("Content-Type: application/json");
$app->response()->header('Content-Type', 'application/json;charset=utf-8');
I'm stuck... :-/
EDIT
I've downloaded the Slim/slim-skeleton via Composer.
I need to return JSON in my Route.php:
$app->get('/getStoresByBounds', function () use ($app, $stores) {
$app->contentType('application/json');
$myStores = $stores->getStores();
$app->response()->write(json_encode($myStores));
$app->stop();
});
From The Response/ Headers/ Set Header:
$newResponse = $oldResponse->withHeader('Content-type', 'application/json');
You can try this :)
// APP
$app = Slim::getInstance();
// CONTENT-TYPE
$app->contentType('application/json');
// STATUS
$app->status(200);
// RESPONSE ARRAY
$response = array();
// PRINT THE RESPONSE ARRAY
$app->response()->write(json_encode($response));
$app->stop();
Or try to access $app inside your function by:
$app = \Slim\Slim::getInstance();
Since you are using V3, you might use this:
$app = new \Slim\App();
$app->get('/getStoresByBounds', function (Request $request, Response $response) {
$myStores = $stores->getStores();
$response->getBody()->write(json_encode($myStores));
$newResponse = $response->withHeader(
'Content-type',
'application/json; charset=utf-8'
);
return $newResponse;
});