I've been using slim/twig-view, documented here: https://notes.enovision.net/slim/composer-package-documentation/slim_twig-view. The recommended way to handle a route, render a view is:
$app->get('/hello/{name}', function ($request, $response, $args) {
$view = Twig::fromRequest($request);
return $view->render($response, 'profile.html', [
'name' => $args['name']
]);
})
Problem is, if you try to add any route based middleware to run afterwards, it fails
$app->get('/hello/{name}', function ($request, $response, $args) {
$view = Twig::fromRequest($request);
return $view->render($response, 'profile.html', [
'name' => $args['name']
]);
})->add(function(Request $request, RequestHandler $handler){
$response = $handler->handle($request);
return $response;
});
With an error like this:
Type: TypeError
Code: 0
Message: Return value of Slim\Handlers\Strategies\RequestResponse::__invoke() must implement interface Psr\Http\Message\ResponseInterface, int returned
File: /var/www/html/vendor/slim/slim/Slim/Handlers/Strategies/RequestResponse.php
Line: 43
I see this is because I'm not returning a response from the route handler, so I rewrite the route handler to return the $response:
$view = Twig::fromRequest($request);
$view->render($response, 'profile.html', [
'name' => $args['name']
]);
return $response;
Still the same error.
I can work around this, but it's a greenfield project and it would be nice to have access to route middleware. Any ideas?
Related
Given the route:
Route::get('verify/{id}/{hash}', 'Auth\VerificationController#verify');
It uses the Laravel's default verify method from
auth-backend/VerifiesEmails.php
The default verify method looks like bellow:
public function verify(Request $request)
{
if (! hash_equals((string) $request->route('id'), (string) $request->user()->getKey())) {
throw new AuthorizationException;
}
if (! hash_equals((string) $request->route('hash'), sha1($request->user()->getEmailForVerification()))) {
throw new AuthorizationException;
}
if ($request->user()->hasVerifiedEmail()) {
return $request->wantsJson()
? new Response('', 204)
: redirect($this->redirectPath());
}
if ($request->user()->markEmailAsVerified()) {
event(new Verified($request->user()));
}
if ($response = $this->verified($request)) {
return $response;
}
return $request->wantsJson()
? new Response('', 204)
: redirect($this->redirectPath())->with('verified', true);
}
I would like to change only the last block of the code in the verify method from
return $request->wantsJson()
? new Response('', 204)
: redirect($this->redirectPath())->with('verified', true);
to
return $request->wantsJson()
? new Response('', 204)
: redirect($this->redirectPath())->with([
'verified' => true,
'userNotification' => [
'message' => 'Wellcome to my website',
'title' => 'Hello World',
],
]);
I know I can override the whole verify method in the VerificationController, which is not ideal to copy and paste the whole block of code for a small change.
My question is How can override only the last block of code as mentioned above?
Right before the final return there is this block:
if ($response = $this->verified($request)) {
return $response;
}
So in your VerificationController you can override just the verified method which is meant for that.
If you look into its source you will see it:
source
So in your local VerificationController add:
protected function verified(Request $request)
{
return $request->wantsJson()
? new Response('', 204)
: redirect($this->redirectPath())->with([
'verified' => true,
'userNotification' => [
'message' => 'Wellcome to my website',
'title' => 'Hello World',
],
]);
}
I am creating API with Default api-authentication
I am using laravel 6.x
Its run when i generate on user register and pass generated token with request.
But
when i pass a wrong token, Then it shows a Login page HTML, i want to show some custom JSON response instead of HTML
Also is there any way to check that passed token is same with passed user id or not. Because user can pass different user id with token.
My api route file as below
Route::middleware('auth:api')->post('/listUser', 'ApiController#listUser');
I have manage my points as below
For Point 1
when i pass a wrong token, Then it shows a Login page HTML, i want to show some custom JSON response instead of HTML
I made change in App/Exceptions/handler.php
Modify render function as below
public function render($request, Exception $exception)
{
if ($exception instanceof NotFoundHttpException) {
if ($request->is('api/*')) {
return response()->json(['error' => 'Not Found'], 404);
}
//return response()->view('404', [], 404);
}
return parent::render($request, $exception);
}
It workrs well because i have an api based routes
My api route look likes
// Request with Authentication v1
Route::group(['prefix' => 'v1', 'namespace' => 'Api\v1', 'middleware' => ['api','auth:api'] ], function () {
Route::post('/myProfile', 'ApiController#myProfile');
});
// Request without Authentication v1
Route::group(['prefix' => 'v1', 'namespace' => 'Api\v1', 'middleware' => 'api'], function () {
Route::post('/register', 'ApiController#register');
});
For Point 2
Also is there any way to check that passed token is same with passed user id or not. Because user can pass different user id with token.
For that i have created a function checkValidations in ApiController and check user id is associated with particular token or not as below:
In that function i check in way that
Check for all validation passed from called method
Match token associated with user id then return success
else return invalid token response
Function Code
public function checkValidations($required = [], $request = [])
{
$validator = Validator::make($request->all(), $required);
if ($validator->fails()) {
$this->response[] = array(
'status' => 'false',
'response_msg' => implode(",",$validator->messages()->all()),
);
return array('response' => $this->response);
} else if(isset($request['api_token']) && auth('api')->user()->id ==
$request['id']) {
return 'success';
} else {
$this->response[] = array(
'status' => 'false',
'response_msg' => 'Invalid token',
);
return array('response' => $this->response);
}
}
And call that checkValidations from any function and can reuse it as
public function myProfile(Request $request)
{
$validation = [
'id' => 'bail|required|exists:users',
'api_token' => 'bail|required|min:60|max:60'
];
if( $this->checkValidations($validation, $request) == 'success'){
$this->response[] = array(
'status' => 'true',
'response_msg' => 'Success',
'data' => auth('api')->user()
);
}
return array('response' => $this->response);
}
May be there is many other best way to manage that points, but i didn't found, so i manage in above ways.
You can configure a custom response in the Authenticate middleware. e.g.
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest()) {
if ($guard === 'api') {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('login');
}
}
return $next($request);
}
You can do this by extending the TokenGuard, with your custom logic. Or you can create a new Middleware, which asserts that user authenticated by API matches the passed user ID.
I just verified the kind of exception if is related with authentication and then the URL( as API guard use '/api' just verify it) and fire the response.
if($exception instanceof \Illuminate\Auth\AuthenticationException){
if($request->is('api/*')){
return response()->json([
'success' => false,
'message' => 'User not logged'
]);
}
}
I made the below change in app/Exceptions/Handler.php.
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['error' => 'Not Authorized'], 404);
}
return redirect()->guest(route('login'));
}
Add use Illuminate\Auth\AuthenticationException in the document. Also, do not forget to add X-Requested-With:XMLHttpRequest to your request header. (Or Headers in postman)
return redirect()->guest(route('login')); is to redirect you to login page when you are not using the APIs.
I am using Laravel 5.4 with JWTAuth & Dingo and for some reason I am now no longer able to make POST requests with Postman. This was working when I first set it up, but not I get the response 405 Method Not Allowed
This seems to have been raised a few times on here, but I can't seem to find a solution. I've cleared the route cache, and when i do api:routes the correct routes are in there.
Below is the routes file, and the controllers it should be sending too. I am only having the issue with the LeadController routes.
api.php
use Dingo\Api\Routing\Router;
/** #var Router $api */
$api = app(Router::class);
$api->version('v1', function (Router $api) {
$api->group(['prefix' => 'auth'], function(Router $api) {
//$api->post('signup', 'App\\Api\\V1\\Controllers\\SignUpController#signUp');
$api->post('login', 'App\\Api\\V1\\Controllers\\LoginController#login');
$api->post('recovery', 'App\\Api\\V1\\Controllers\\ForgotPasswordController#sendResetEmail');
$api->post('reset', 'App\\Api\\V1\\Controllers\\ResetPasswordController#resetPassword');
});
$api->group(['middleware' => 'jwt.auth'], function(Router $api) {
$api->get('protected', function() {
return response()->json([
'message' => 'Access to protected resources granted! You are seeing this text as you provided the token correctly.'
]);
});
$api->get('refresh', [
'middleware' => 'jwt.refresh',
function() {
return response()->json([
'message' => 'By accessing this endpoint, you can refresh your access token at each request. Check out this response headers!'
]);
}
]);
$api->post('lead/store', 'App\\Api\\V1\\Controllers\\LeadController#store');
$api->get('lead', 'App\\Api\\V1\\Controllers\\LeadController#index');
});
});
LeadController.php
namespace App\Api\V1\Controllers;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Tymon\JWTAuth\JWTAuth;
use App\Http\Controllers\Controller;
use Dingo\Api\Routing\Helpers;
use Illuminate\Http\Request;
use Carbon\Carbon;
use App\Lead;
use App\User;
class LeadController extends Controller
{
use Helpers;
public function index(Lead $leads)
{
$leads = $leads->all();
$count = 0;
foreach($leads as $key => $lead){
$user = User::where('id', $lead->user_id)->first();
$leads[$count]['name'] = $user->name;
array_pull($leads[$count], 'user_id');
$count++;
}
return $leads;
}
public function store(Request $request)
{
$today = new Carbon();
$this->validate(request(), [
'owner' => 'required',
'bname' => 'required|min:3|max:255',
'tname' => 'max:255',
'created' => 'required|date|before_or_equal:today',
'update' => 'date'
]);
if(!$user = User::where('zoho_id', $request->get('owner'))->first())
return $this->response->error('invalid_owner', 500);
$lead = new Lead;
$lead->user_id = $user->id;
$lead->bname = $request->get('bname');
$lead->tname = $request->get('tname');
$lead->created_at = $request->get('created');
$lead->updated_at = $request->get('updated');
if($lead->save())
return $this->response->created();
else
return $this->response->error('could_not_create_lead', 500);
}
}
I found the answer, for this whilst I almost finished writing the question by stumbling upon the answer here:
https://laracasts.com/discuss/channels/laravel/dingo-api-and-postman-not-matching-post-request
Remove the trailing / from the request URL. So will leave this answer here, in the hopes it may prove useful to someone.
ie. http://api.someurl.app/api/lead/store?token=....
Perhaps, someone can suggest a way of allowing trailing / ??
When I am making a API call to the http://localhost/lvl53/public/api/getSongs using Postman I get the following error.
FatalErrorException
Doctrine\Common\Proxy\AbstractProxyFactory::getProxyDefinition(): Failed opening required.
https://i.stack.imgur.com/TNEhO.png
Here are my routes web.php
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Route::group(['middleware' => ['web','auth']], function () {
Route::get('/song', 'SongController#index')->name('song');
Route::get('/addSong', 'SongController#addSong')->name('addNewSong');
Route::post('/registerSong', 'SongController#create')->name('registerSong');
});
Route::get('/home', 'HomeController#index')->name('home');
Here is api.php
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
Route::group(['middleware' => ['web']], function () {
Route::get('/getSongs', 'SongController#getSongs')->name('getSongs');
});
But if I add the API route getSongs to the Route::Group in web.php and after logging in make the call, then I am able to retrieve data.
Here's my SongController's getSongs method
public function getSongs(EntityManagerInterface $em)
{
$songs = $em->getRepository(SongEntity::class)->findAll();
$songList = [];
\Log::info(["Songs",$songs]);
foreach ($songs as $song){
$currentSong["artist"] = $song->getArtist();
$currentSong["title"] = $song->getTitle();
$currentSong["url"] = $song->getUrl();
array_push($songList, $currentSong);
}
return $songList;
/*return view('song.songs', ['songs' => Song::all()]);*/
}
In here when making the api call \Log doesn't create a log. How to retrieve data using the API?
PS: When I looked into error msg they say I have do advance configurations on doctrine, it doesn't make sense. Any help is highly appreciated.
PPS: Do you think it has do something with attaching a authenticated user to SongEntity when creating the SongEntity?
Here's what happens when creating a SongEntity.
protected function create(Request $request, EntityManagerInterface $em)
{
$user = Auth::user();
//\Log::info($user);
$data = $request->input();
$this->validate($request, [
'title' => 'required|string|max:255',
'artist' => 'required|string|max:255',
'url' => 'required|string|url|unique:App\Entities\SongEntity',
]);
$song = new SongEntity(
$data['title'],
$data['artist'],
$data['url'],
$user
);
$em->persist($song);
$em->flush();
/* Song::create([
'title' => $data['title'],
'artist' => $data['artist'],
'url'=> $data['url']
]);*/
return redirect('song');
}
I'm making a REST API that should validate data entry from the user, to achieve that, I made a Request class that has the rules function that it should do the validations.
Request class
class StoreUpdateQuestionRequest extends Request {
public function authorize() {
return true;
}
public function rules() {
$method = $this->method();
$rules = [
'question' => 'required|min:10|max:140',
'active' => 'boolean',
];
return $method !== 'GET' || $method !== 'DELETE' ? $rules : [];
}
}
So, in the controller, when I try to run an endpoint which it fires the validations, it does work, it fails when it's has to, but it doesn't show me the errors as I expect, even though I defined the error messages in the messages function contained in the Request class, instead of showing me that errors, it redirects me to a location. Which sometimes is the result of a request I made before or it runs the / route, weird.
Controller function
public function store(StoreUpdateQuestionRequest $request) {
$question = new Question;
$question->question = $request->question;
$question->active = $request->active;
if($question->save()) {
$result = [
'message' => 'A question has been added!',
'question' => $question,
];
return response()->json($result, 201);
}
}
Any ideas? Thanks in advance!
In order to make this work, you have to add an extra header to your request:
Accept: application/json
That did the trick.
You can use controller based validation as described in documentation https://laravel.com/docs/5.2/validation#manually-creating-validators
public function store(Request $request) {
$validator = Validator::make($request->all(), [
'question' => 'required|min:10|max:140',
'active' => 'boolean',
]);
if ($validator->fails()) {
return response()->json($validator->errors());
}
//other
}