I have created a middleware controller, that is responsible for authentication. The controller will be linked in front of the resource controllers. I assume this is the right way to do it?
In the handle method, I'm executing a database query, which requires the connection information. However when I try to pass the data through the constructor, it tells me that setters are not allowed.
What would be the best way to pass a Map with connection information from the channel.dart into a Middleware controller (see dbConnectionDetails)?
channel.dart
@override
Future prepare() async {
logger.onRecord.listen((rec) => print("$rec ${rec.error ?? ""} ${rec.stackTrace ?? ""}"));
dbConnectionDetails = ConnectionSettings (
user: "mydbuser",
password: "mydbpass",
host: "localhost",
port: 3306
);
}
@override
Controller get entryPoint {
final router = Router();
router
.route("/api/user/query/complex")
.link(() => AuthMiddleware())
.link(() => UserComplexQueryController(dbConnectionDetails));
}
In the example above, I can pass the dbConnectionDetails into the constructor of the UserComplexQueryController, so that I can use it for the database connection.
However if I pass the dbConnectionDetails into the constructor of AuthMiddleWare, then I get following error:
ArgumentError (Invalid argument(s): Invalid controller 'AuthMiddleware'. Controllers must not have setters and all fields must be marked as final, or it must implement 'Recyclable'.)
The AuthMiddleWare in my case is getting a token parsed in the URL and will check if the token is valid in the database. As you can see, I found no other way than to add the dbConnectionDetails directly within the AuthMiddleware isValid function. But I would prefer, if I could pass it down from channel.dart.
class AuthMiddleware extends Controller {
// This does not work, as setters not allowed
//AuthMiddleware(this.dbConnectionDetails);
//
//ConnectionSettings dbConnectionDetails
@override
Future<RequestOrResponse> handle(Request request) async {
if (await isValid(request)) {
return request;
}
return Response.unauthorized();
}
Future<bool> isValid(Request request) async {
final String token = request.raw.requestedUri.queryParameters["token"];
final int timestamp = (DateTime.now().toUtc().millisecondsSinceEpoch / 1000).round();
ConnectionSettings dbConnectionDetails = ConnectionSettings (
user: "myuser",
password: "mypass",
host: "localhost",
port: 3306
);
AuthTokenDao authTokenDao = new AuthTokenDao(dbConnectionDetails);
if(await authTokenDao.validateToken(token, timestamp)) {
return true;
}
return false;
}
}
And yes, you have seen right. I'm connecting to a MySQL database :)
Edit by CA: The solution (provided by Joe Conway):
class AuthMiddleware extends Controller {
// Works by adding "final"
final ConnectionSettings dbConnectionDetails;
// Now dbConnectionDetails can be passed within the constructor.
AuthMiddleware(this.dbConnectionDetails);
@override
Future<RequestOrResponse> handle(Request request) async {
if (await isValid(request)) {
return request;
}
return Response.unauthorized();
}
Future<bool> isValid(Request request) async {
final String token = request.raw.requestedUri.queryParameters["token"];
final int timestamp = (DateTime.now().toUtc().millisecondsSinceEpoch / 1000).round();
/* Removed this
ConnectionSettings dbConnectionDetails = ConnectionSettings (
user: "myuser",
password: "mypass",
host: "localhost",
port: 3306
);
*/
AuthTokenDao authTokenDao = new AuthTokenDao(dbConnectionDetails);
if(await authTokenDao.validateToken(token, timestamp)) {
return true;
}
return false;
}
}