So I have an api-version middleware that I use in laravel 5 that I am trying to use in lumen. However it uses getAction() and setAction() in the middleware which isn't available to lumen ("yet"). If I do $request->route()[1] I can get the current route but updating that with the new route does no good. I have thought about cloning the request and modifying it but I can't tell what part of the request object I would need to "update".
Here are my routes:
$app->group(['middleware' => ['api-version']], function() use ($app) {
$app->get('users', '{api-namespace}\UsersController#index');
$app->get('vips/{per_page?}', '{api-namespace}\VipsController#index');
$app->get('vip/{id}/profile', '{api-namespace}\VipsController#showProfile');
$app->get('vip/{id}', '{api-namespace}\VipsController#show');
});
Can anyone tell me how i can update the request with my simple route update?
Found this in my search for a solution. Here's what I came up with using middleware in Lumen 5.4.
First, I created an ExtractApiVersionMiddleware that extracts the version from the Accept header. I used the accept header because not all headers passed are trusted and it is becoming "Best Practice": to pass API versions in this manner:
<?php
namespace App\Http\Middleware;
use Closure;
class ExtractApiVersionMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
*
* #return mixed
*/
public function handle($request, Closure $next)
{
$app = app();
$version = "0";
foreach (explode(';', $request->header('accept')) as $frag) {
if (stristr($frag, "version=")) {
$version = str_replace("version=", "", $frag);
break;
}
}
if ($version != 0) {
$app->group(['namespace' => "App\Http\Controllers\V{$version}\Reductions"],
function () use ($app, $version) {
require base_path() . "/routes/web/v{$version}.php";
});
} else {
$app->group(['namespace' => 'App\Http\Controllers\V0'], function () use ($app, $version) {
require base_path() . "/routes/web/v0.php";
});
}
return $next($request);
}
}
Second, I namespaced my code according to versions (the legacy codebase hasn't died yet).
Third, I pass the version that the call uses in the Accept header
Accept: application/json;version=1
Fourth, I have separate route files for each supported version. So, instead of having a web.php for all my routes, I created a web folder under routes and I placed my version files in there v0.php & v1.php
So, when I make a request, the Middleware extracts the API version and based on the version number enables the appropriate route group. This keeps the versions clean and separate but still enables me to get 'creative' with my Models.
Hope this helps. I had to apply this solution because I wasn't sure about using Dingo API in pre-release form
Related
I am building an application, where there would be main application users (say support users) and separate client users (the application can have many different clients and each clients can have many users), every client has its own separate database, but the codebase for the entire application would be the same for every client.
What I wanted to achieve is, before calling auth in the main application, I wanted to call a middleware, which would detect a parameter (say db_slug) from request URL and according to that param it will change the DB respectively. And then login them to the client user to their respective DB.
Note: The client users will not be a part of the main DB. Their record would be only in their Client's DB.
But I am failing to do so, as my auth middleware is called first, before my custom middleware, and on accessing auth routes, it is saying unauthenticated, since that specific client user is not a part of my main application.
Note: I am using sanctum auth.
What I tried is, created a middleware called ClientDBMiddleware
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use DB;
use Config;
class ClientDBMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle(Request $request, Closure $next)
{
if($request->has('db_slug')){
$dbSlug = $request->db_slug;
DB::purge('mysql');
Config::set('database.connections.mysql.database', "db_$dbSlug");
}
return $next($request);
}
}
and applied it my api.php auth routes before auth middleware
Route::group(['middleware' => ['clientDB', 'auth:sanctum']], function () {
Route::get('/me', [UserController::class, 'me']);
});
Important Note :
If there is a better approach to achieve my required thing, then that would be highly appreciated, please help me with it.
I want to prevent access to some of my app routes from other domain except listed. It success using below code:
$loginRoutes = function() {
Route::get('/', 'HomeController#index')->name('home');
};
Route::domain('domain1.com')->group($loginRoutes);
Route::domain('domain2.com')->group($loginRoutes);
Route::domain('localhost')->group($loginRoutes);
But the problem is when I call {{route('home')}}, the URL always becomes the domain at the last line of the routes.php(at above case is http://localhost ). How to make it to current domain?
My current solution:
if (isset($_SERVER["HTTP_HOST"]) && $_SERVER["HTTP_HOST"] == "domain1.com") {
Route::domain('domain1.com')->group($loginRoutes);
}elseif (isset($_SERVER["HTTP_HOST"]) && $_SERVER["HTTP_HOST"] == "domain2.com") {
Route::domain('domain2.com')->group($loginRoutes);
}
It's work but I think it's dirty. I have a lot of domains/subdomain and also the routes too.
I need solution on route directly, because I have a lot of routes, if I update each controller it's will take a long time. Maybe edit route provider or laravel vendor code is also no problem.
I am also using PHP 7.3 and Laravel 5.7
I actually use this routing for my domains.
Maybe this is not exactly what you asked, but you can try something like this
// To get the routes from other domains
// Always add the new domains here
$loginRoutes = function() {
Route::get('/', 'HomeController#index')->name('home');
};
Route::group(array('domain' => 'domain1.com'), $loginRoutes);
Route::group(array('domain' => 'domain2.com'), $loginRoutes);
Route::group(array('domain' => 'domain3.com'), $loginRoutes);
If you want to handle something at the domain level. In your controller (HomeController#index), you can get the current domain and do whatever you want. To get exact domain I have used like this:
class HomeController extends Controller
{
public function index()
{
$domain = parse_url(request()->root())['host'];
if ($domain == 'domain1.com'){
// do something
}
...
}
...
}
That way I can handle different things for each domain.
Just to make it more complete, we can take the domains from a table/query and dynamically create the routes.
$domains = Cache::get('partners')->where('status', '=', 'A')->where('domain', '<>', '')->all();
$loginRoutes = function() {
Route::get('/', 'HomeController# index')->name('home');
};
foreach ($domains as $domain) {
Route::group(array('domain' => $domain->dominio_externo), $loginRoutes);
}
It has been working for me. I hope to help you.
You can maybe try something like this :
Route::pattern('domainPattern', '(domain1.com|domain2.com|localhost)');
$loginRoutes = function() {
Route::get('/', 'HomeController#index')->name('home');
};
Route::group(['domain' => '{domainPattern}'], $loginRoutes);
If I understand your issue, you just want to filter domains. Using regex, you can do it. You could try the following code:
Route::domain('{domain}')->group($loginRoutes)->where('domain', '\b(domain1\.com|domain2\.com|localhost)\b');
Details:
\b: we get exactly the string.
\.: in regex, the character . means any character. So, we have to escape . using backslash.
Note:
You might get an error, because I can not check the results. Let me know any errors you encounter.
I want to prevent access to some of my app routes from other domain
except listed. It success using below code:
I think you are right with your thoughts about a better, more laravel-core based solution for this problem.
Every route handling method you define in a controller file recieves a request. In standard laravel this is an object of type Illuminate\Http\Request.
You can extend this class with a custom class - let's say "AdminRequest". This extended class than offers authorization methods which will check if the Auth:user has the correct role, session values or whatever you want in your app.
I guess this is more flexible and clean - in your controller you only have to change the definition of the request you recieve in that controller method. Validation messages and everything else can be wrapped in the custom request class.
See this also:
How to Use custom request (make:request)? (laravel) Method App\Http\Requests\Custom::doesExistI does not exist
Extend Request class in Laravel 5
for preventing access to a certain route, its a bad design to inject a Route into these structure:
Route::domain('domain1.com')->group($loginRoutes);
Route::domain('domain2.com')->group($loginRoutes);
Route::domain('localhost')->group($loginRoutes);
since it defines route multiple time, and only the last will be override the others.
you can check this by php artisan route:list .
the laravel way to handle this situation (access management ) is to use middleware
class DomainValid
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$domain = $request->server->get('SERVER_NAME');
if (in_array($domain , ['domain1.com','domain2.com','localhost'] )) {
return $next($request);
}
abort(403);
}
}
and use it like this:
use App\Http\Middleware\DomainValid;
Route::get('/', 'HomeController#index')->name('home')->middleware(DomainValid::class);
so it will be only ONE home route.
I have a Laravel 5.2 app and inside the public_html folder I have some assets stored in a subfolder called metronic as I am using a theme called metronic and wanted to keep it all bundled in one place to make updating it easier.
I have linked to these assets using the URL::asset() method like so:
<script src="{!! URL::asset('metronic/global/plugins/fullCalendar/fullcalendar/fullcalendar.js') !!}"></script>
This generates the following URL:
http://www.mywebsite.com/metronic/global/plugins/fullCalendar/fullcalendar/fullcalendar.js
For some reason this is working fine locally but when I run it on my production server the request is going through Laravel. I have some middleware that checks whether or not to show the coming soon page and this is being run and so rather than the script being fetched the holding page is just being run. This is the content of the middleware:
<?php namespace App\Http\Middleware;
use Closure;
use Request;
use Route;
use Session;
class HoldingPageCheck {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if(env('HOLDING_PAGE')==true && (!$request->is('coming-soon') && !$request->is('member/subscribe') && !$request->is('migrate') && !$request->is('contact') && $request->segment(1)!='admin')) {
Session::reflash();
return redirect(route('holding_page'));
}
return $next($request);
}
}
It doesn't make any sense to me why Laravel would be interfering with an asset request it should just bypass Laravel entirely shouldn't it when a request for a JS file is made?
Found the problem, it was because I was using camel case in the path to the script. Locally with xampp it didn't mind but obviously my live environment was case sensitive.
I'm using 2 languages in my Laravel 5.2 app. There is a simple password-reminder page I'm implementing currently, and for reasons unknown to me I have problems in sending the new-password email in the correct language.
Let's say I see the page in German. In the view of the page, I echo 2 values, using Facades:
echo App::getLocale();
echo Session::get('locale');
The page is served in German, so both values echo de.
Now, I enter an email address into the form and submit it. The input gets to a controller method and calls a library to send a new password to the user:
public function resetPassword() {
// Validate the input, retrieve the user...
Mailer::sendNewPasswordEmail($user); // Call to the library sending emails
}
Finally, in the library, I var_dump the same 2 values, like this:
public static function sendNewPasswordEmail($user) {
var_dump(App::getLocale());
var_dump(Session::get('locale'));
die;
}
In this case, Session::get('locale') still equals de, but App::getLocale() shows en.
Why, why, why?
In my email template, I'm using the Blade's #lang() directive. As far as I know, the directive checks the application locale to determine which translation to serve. In my case, the email is being sent always in English and I have no clue why App::getLocale() returns a different value in the view and during the next POST request I'm making.
This is not the first time this happens, btw. At times is seems that views "know" more about the actual application locale, than the controllers, models or libraries. Confusing.
Ideas?
Laravel 5.2 App_Locale is not persistent. the only way I've found to make locales work properlly is creating a middleware that calls App::setLocale() like this:
<?php namespace App\Http\Middleware;
use Closure;
use Session;
use App;
use Config;
class Locale {
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
App::setLocale(Session::get('locale'));
return $next($request);
}
}
Register your middleware on Kernel.php
protected $middleware = [
.
.
.
'App\Http\Middleware\Locale'
];
I'm building a multi languages site using Laravel 5.
I knew that in Laravel, I can define prefix for route like:
http://domain/en/users/1
http://domain/en/shop/1
And in Middleware, I can get the segment of url path to detect the language and set locale of current Request.
But I can't find anyway to add lang parameter in default for route like folowings:
http://domain/users/1?lang=en
http://domain/shop/1?lang=en
Or are there anyways to hook into route function of Framework to append default parameter ?lang=jainto all route when I call ?
(ja is current locale of application which was set in middleware before )
Thanks !
You can create a middleware that sets up a default 'lang' query parameter if the request doesn't have one. It will work for all the requests to your app, so you can get the lang parameter in every route handler.
Create a middleware LangFilter in the console (while in the project directory) :
php artisan make:middleware LangFilter
Then open up ./app/Http/Kernel.php and add :
\App\Http\Middleware\LangFilter::class
to the $middleware array. Now open up the middleware you created, i.e ./app/Http/Middleware/LangFilter.php and add the checking and setting code :
<?php
namespace App\Http\Middleware;
use Closure;
class LangFilter
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
// Checks for lang in the parameters.
if($request->input('lang') == null) {
// Adds the default one since it doesn't have one.
$request->merge(array('lang' => 'en'));
}
return $next($request);
}
}
If you want to have this kind of filtering for just a subset of all the routes you have, you need to register the middleware differently in Kernel.php.
UPDATE
For making a helper that generates routes with current locale :
Create a folder app/Support.
Create the helpers file app/Support/helpers.php
Open up helpers.php, and add this code to add the helper :
<?php
function locale_route($name, $parameters = [], $absolute = true) {
return route($name, array_merge($parameters, [
'lang' => App::getLocale()
]), $absolute);
}
?>
Add the helpers file to composer autoload in composer.json:
"autoload" : {
"files" : [
"app/Support/helpers.php"
]
}
Now run in the console :
composer dumpautoload
Now you can call locale_route with the same parameters you give to route to create urls that has the current locale added in query params.
I hope this is what you are looking for. Generating a route with a query string parameter