In one of my posts about Akka.NET I’ve covered actor selections and paths. Sadly I’ve skipped one rather important part regarding allowed characters for actor paths and what to do if our name cannot be used for creating an actor. And recently I’ve received a question about why I’m creating actors using encoded string. This is the cause.
I’ve encountered this problem for the first time when I’ve tried to create actors for my content with URL as a name (and by extension path). Of course, it was impossible, even logically speaking it shouldn’t be possible to do so because URLs contains slashes which are being used to separate segments in actor paths.
If actor path will contain invalid character exception will be thrown during its creation. It will contain a very precise message about what we can and what we can’t use:
Illegal actor name [http://google.com]. Actor paths MUST: not start with
$
, include only ASCII letters and can only contain these special characters: $”-_.*$+:@&=,!~’;”().
My first thought was encoding actor name in Base64 which would work in some cases. Sadly Base64 can contain two special characters ‘=’ and ‘/’. The second one is of course unacceptable.
I’ve gone with MD5 later and it was exactly what I’ve needed. Whenever my actor name is provided by user in some kind of input I’ll need to secure it by making sure it doesn’t contain anything that’ll throw an exception. Every message in my app is being handled by manager actor and I’m using “create if doesn’t exist” patter. during that so changing it was really easy. In this case, I’m removing schema from URI, changing all characters to lower case and then I’m converting entire string to MD5.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public ContentManagerActor() : base() { Receive<IHaveContentUri>(msg => CreateContentActorIfNotExists(msg.Uri).Forward(msg)); } private IActorRef CreateContentActorIfNotExists(Uri uri) { var actorPathSegment = uri.ToSchemalessUriAsMD5(); if (!Context.Child(actorPathSegment).IsNobody()) return Context.Child(actorPathSegment); else return Context.ActorOf(ContentActor.Props(uri), actorPathSegment); } |
It’s a simple solution and MD5 doesn’t need to be stored anywhere because it doesn’t need to be used anywhere else.