Laravel - CNAME + Subdomain Routing

2020-06-06 05:02发布

I have my routes set up as follows:

<?php

Route::group([
    'domain' => '{username}.u.'.env('APP_DOMAIN'),
], function () {
    Route::get('/', 'FrontendController@site');
});

Route::group([
    'domain' => env('APP_DOMAIN'),
], function () {
    // Regular site routes
});

Route::group([
    'domain' => '{domain}',
], function () {
    Route::get('/', 'FrontendController@domain');
});

What I'm trying to achieve is allowing users to have their own sites, e.g. hello.u.domain.com, and for those sites to also be served through a custom domain that is CNAME'd to their subdomain. Using the routing above, the wildcard subdomain works perfectly fine. However the custom domain routing is never hit; whenever a custom domain that is CNAME'd to the subdomain is visited, the regular site routes are used.

APP_DOMAIN is not the same as the custom domain, and I have $router->pattern('domain', '[a-z0-9.]+'); in my RouteServiceProvider.php to allow {domain} as a full domain name.

3条回答
我欲成王,谁敢阻挡
2楼-- · 2020-06-06 05:34

This has been answered, at least partially, by myself here: Creating a route in Laravel using subdomain and domain wildcards

In short, you cannot make the whole domain a parameter in the routes file. Instead, you assign a middleware that will check the current domain, match it against your pre-defined list of allowed user domains and based on that - take some decision (eg. what method to hit in your controller).

Consider:

Route::group([
    'middleware' => 'domain-check',
], function () {
    Route::get('/', 'FrontendController@handle');
});

And then in your FrontendController:

public function handle(Request $request)
{
    // Information about the current user/domain is here
    $request->client;

    // An example - let's imagine that client object contains a view ID
    return response()->view($request->client->view);
}
查看更多
Rolldiameter
3楼-- · 2020-06-06 05:35

You can't have a wildcard on the whole domain, at least not with one wildcard. What you can do is the following:

Route::group([
    'domain' => '{domain}.{suffix}',
], function () {
    Route::get('/', 'FrontendController@domain');
});

Just make sure that this group is the last one in your routes file, it will act as a fallback group on domains and will serve the requests that don't match any of the previous domains.

Also I'd recommend using this package, it might be helpful for what you are trying to do.

查看更多
看我几分像从前
4楼-- · 2020-06-06 05:38

Unfortunately I did not get an answer to my comment. This answer assumes the problem is that normal default routes are used instead of routes with subdomain.

Example:

A user is visiting sub.website.com but route() returns website.com/blabla instead of sub.website.com/blabla

You can solve this by dynamically create the pattern for domain inside routes.php

// routes.php
$url_parameters = @explode(".", $_SERVER['HTTP_HOST']);

if (count($url_parameters) == 3)
{
    $pattern = '{subdomain}.{domain}.{tld}';
}
else
{
    $pattern = '{domain}.{tld}';
}

Route::group(['domain' => $pattern], function () {

    Route::get('/', [
        'as' => 'get_index',
        'uses' => 'HomeController@getIndex'
    ]);

}

By using this method you will create a problem with your route() and controller parameters.

route() problem

When calling the route() function while using this method you will get a missing argument error. The route() function expects you to give the {subdomain}.{domain}.{tld} parameters.

You can solve this by creating your own route function. I named it mdroute() (multi domain route).

function mdroute($route, $parameters = [])
{

    $data = [
        'domain' => \Request::route()->domain,
        'tld' => \Request::route()->tld
    ];


    $subdomain = \Request::route()->subdomain;

    if ($subdomain) $data['subdomain'] = $subdomain;

    // You can use mdroute('blabla', 'parameter')
    // or mdroute('blabla', ['par1' => 'parameter1', 'par2' => 'parameter2'])
    //
    if (is_array($parameters))
    {
        $data = array_merge($data, $parameters);
    }
    else
    {
        $data[] = $parameters;
    }


    return route($route, $data);

}

Controller problem

The parameters {sub}.{domain}.{tld} are always send to your controller. You can't access other parameters the way you are used to be.

Example:

// Domain = sub.webite.com


// Your route
//
Route::get('/post/{id}/{param2}', [
    'uses' => 'PostController@getIndex'
]);

// PostController
//
public function getIndex($id, $param2)
{

    // $id will be 'sub'
    // $param2 will be 'website'

}

You can solve this by accessing your parameter through the Request object.

public function getIndex(Request $request)
{

    $id = $request->id;

    $param2 = $reqeust->param2;

}
查看更多
登录 后发表回答