I'd like to use pion 5.0.6 as a small webserver in a VS2017 c++ project. For static routes I can use
add_resource("/my/static/route", <handler>)
I would need dynamic routes as well - like "/data/:id/info
How do I do this?
For those who may need it: I found a solution to add dynamic routing to the pion webserver. It requires smart router code I found at hxoht on github, and works the way that
all routes - static and dynamic - are set with httpd->add_resource(<url>, <handler);
a 404-handler has to be set with httpd->set_not_found_handler(<handler>); and is responsible for dispatching the dynamic routes to the handlers added above.
your webserver class must derive from pion::http::server in order to find the handler by name with httpd->find_request_handler(<url>, <handler>);
in your 404-handler, you use the Match::test(<dynamic-route>) method to detect a dynamic route - like in the following code fragment:
void handle_404(http::request_ptr& req, tcp::connection_ptr& con)
{
Route target;
Match dynamic = target.set(req->get_resource());
for (auto& route : dynamic_routes) // Our list of dynamic routes
{
if (dynamic.test(route)) // Does the url match the dynamic route pattern?
{
request_handler_t h;
if (find_request_handler(route, h))
{
auto name = get_param_name(route); // e.g. /a/:b -> "b"
value = dynamic.get(name); // Save value in string or map<name, value>
h(req, con); // Call original handler with value set properly
return;
}
}
}
// If no match then return a 404.
http::response_writer_ptr w(http::response_writer::create(con, *req,
boost::bind(&tcp::connection::finish, con)));
http::response& res = w->get_response();
res.set_status_code(http::types::RESPONSE_CODE_NOT_FOUND);
res.set_status_message(http::types::RESPONSE_MESSAGE_NOT_FOUND);
w->send();
}
For using the pion webserver in a multi-threaded way, I would store the parsed value inside the request object, which would be derived from pion::http::request.
This would work for Windows and Linux :)
Related
I have a ServiceStack self-hosted webservice, using the AppSelfHostBase.
WHen the Configure method is executed, I have this:
public override void Configure(Container container)
{
Config.RouteNamingConventions = new List<RouteNamingConventionDelegate> {
RouteNamingConvention.WithRequestDtoName,
RouteNamingConvention.WithMatchingAttributes,
RouteNamingConvention.WithMatchingPropertyNames,
};
Routes.AddFromAssembly(typeof(ServiceStackHost).Assembly);
and I expected the following service to be executed under /StartBankIdAuthentication path, but it resides under /json/reply/StartBankIdAuthentication instead.
public class StartBankIdAuthentication : IReturn<StartBankIdAuthenticationResponse>
{
public string IdNbr { get; set; }
}
Also, is there an automatic way to make the properties in the DTO to be under "sub-paths", like /StartBankIdAuthentication/1234 instead of the /StartBankIdAuthentication?IdNbr=1234?
I know I can manually add the Route attribute, but it seems cumbersome and also messy in many ways (not Typed, error-prone etc).
I expected the following service to be executed under /StartBankIdAuthentication path, but it resides under /json/reply/StartBankIdAuthentication instead.
The /json/reply/StartBankIdAuthentication is a pre-defined route that's always available by default, they have no relation to Auto Generated Routes.
The default Route generation strategies you've listed are already registered by default and are what's applied when you use Routes.AddFromAssembly(). You should only override with route strategies you want in addition to the defaults, and you should use SetConfig() for any configuration in ServiceStack, e.g:
SetConfig(new HostConfig {
RouteNamingConventions = { MyCustomRouteStrategy }
});
The implementation for the different Route Strategies available in ServiceStack are in RouteNamingConvention.cs, you'll need to register your own strategy for anything additional Route strategies you want.
By default additional routes are generated for any Id or IDs property, the routing docs shows examples of how they can be customized:
The existing rules can be further customized by modifying the related static properties, e.g:
RouteNamingConvention.PropertyNamesToMatch.Add("UniqueId");
RouteNamingConvention.AttributeNamesToMatch.Add("DefaultIdAttribute");
Which will make these request DTOs:
class MyRequest1
{
public UniqueId { get; set;}
}
class MyRequest2
{
[DefaultId]
public CustomId { get; set;}
}
Generate the following routes:
/myrequest1
/myrequest1/{UniqueId}
/myrequest2
/myrequest2/{CustomId}
I know I can manually add the Route attribute, but it seems cumbersome and also messy in many ways (not Typed, error-prone etc).
If you really want you can use nameof() for Typed Routes:
[Route("/" + nameof(StartBankAuthentication) +"/"+ nameof(StartBankAuthentication.IdNbr))]
I'm not sure if Mythz will maybe come up with a different of better solution, but I managed to achieve what I wanted by overriding the GetRouteAttributes, and by using reflection, I could create what I wanted. It looks like this:
public override RouteAttribute[] GetRouteAttributes(Type requestType)
{
string fullname = requestType.FullName.Replace("AlfaOnlineServiceModel.Api.", "");
string path = "/" + fullname.ToLower().Replace(".", "/");
RouteAttribute[] routes = base.GetRouteAttributes(requestType);
if (routes.Length == 0)
{
routes = new RouteAttribute[1];
PropertyInfo[] pInfos = requestType.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly);
foreach(PropertyInfo pi in pInfos)
{
path += "/{" + pi.Name + "}";
}
routes[0] = new RouteAttribute(path);
}
return routes;
}
Which will give for example:
MyMethodResult
The following routes are available for this service:
All Verbs /myCoolPath/mySubPath/myMethod/{MyProperty}
I am adding Coldbox to our legacy application and I ran into a problem where we can't access certain variables from within the views when using Coldbox. In the existing legacy code inside of Application.cfc in the onRequestStart method we set several variables like so:
VARIABLES.screenID = 0;
VARIABLES.DSN = 'datasourcemain';
VARIABLES.DSNRO = 'datasourcereadonly';
VARIABLES.DSNADMIN = 'datasourceadmin';
VARIABLES.pagetitle = "Default Page Title for web application";
This is just a small snippet of the variables set. The problem is that in the legacy code these were used all over the place like in the header and footer. When browsing to a legacy page these are still accessible but when sending the request through coldbox the variables become inaccessible. My question is, is there a recommended way that I can have Coldbox know about these variables and pass them along to the views so I don't have to modify hundreds of files?
It depends, there are a few places to define such variables. From the limited information given, I would suggest you add the datasource information to the Coldbox.cfc > datasources struct (#1) and add the default pageTitle to the global request handler (#2). As for the screenID, who knows -- good luck!
config/Coldbox.cfc has both a settings and datasources struct that can be injected via wirebox into the handlers/controllers.
// Dependency Injection using WireBox
property name='settings' inject='coldbox:settings';
Use a global request handler and add all the global vars into the prc (private request context) which is visible to the controller and views.
//config/Coldbox.cfc
...
coldbox = {
requestStartHandler: 'Main.onRequestStart'
};
...
// handlers/Main.cfc
component extends='coldbox.system.EventHandler' {
function onRequestStart( event, rc, prc) {
prc.screenID = 0;
prc.DSN = 'datasourcemain';
prc.DSNRO = 'datasourcereadonly';
prc.DSNADMIN = 'datasourceadmin';
prc.pagetitle = "Default Page Title for web application";
}
}
Use a request interceptor and add data to the prc.
//config/Coldbox.cfc
...
interceptors = [
{ class="interceptors.Globals" }
];
...
//interceptor/Globals.cfc
component {
property name='legacyGlobals' inject='LegacyGlobals';
function preProcess(event, interceptData) {
event.setPrivateValue('someLegacyGlobalVar', legacyGlobals.getSomeLegacyGlobalVar() );
}
}
Is there a way for setting configuration for remote actor selection simliar to the remote actor creation as specified in Akka docs:
akka {
actor {
deployment {
/sampleActor {
remote = "akka.tcp://sampleActorSystem#127.0.0.1:2553"
}
}
}
}
I prefer not to define custom variable for that.
system.actorSelection("sampleActor")
There are only two forms of the actor selection method, from the docs:
def actorSelection(path: ActorPath): ActorSelection
Construct an akka.actor.ActorSelection from the given path, which is
parsed for wildcards (these are replaced by regular expressions
internally). No attempt is made to verify the existence of any part of
the supplied path, it is recommended to send a message and gather the
replies in order to resolve the matching set of actors.
def actorSelection(path: String): ActorSelection
Construct
an akka.actor.ActorSelection from the given path, which is parsed for
wildcards (these are replaced by regular expressions internally). No
attempt is made to verify the existence of any part of the supplied
path, it is recommended to send a message and gather the replies in
order to resolve the matching set of actors.
And an ActorPath is just created from a string anyway:
def
fromString(s: String): ActorPath
Parse string as actor path; throws java.net.MalformedURLException if unable to do so.
So there isn't a direct way to do actor selection just by setting a particular value in config. However it is quite easy to pull a value from config and use it for actor selection. Given the config:
akka {
actor {
selections: {
sampleActor: {
path: "akka.tcp://sampleActorSystem#127.0.0.1:2553/user/sampleActor"
}
}
}
}
You could use:
val sampleActorSelection =
system.actorSelection(
system.settings.config.getString("akka.actor.selections.sampleActor.path"))
If this was a method you found yourself using frequently, you could use an implicit class to add a helper method to system:
implicit class ActorSystemExtension(system: ActorSystem) {
def actorSelectionFromConfig(actorName: String): ActorSelection {
system.actorSelection(
system.settings.config.getString(s"akka.actor.selections.${actorName}.path"))
}
}
So, I'm using the Play Framework in conjunction with MongoDB. I'm attempting to pass a List to my view to loop through and display the rows.
Controller:
public static Result find() {
List<DBObject> results = MongoController.find(MongoController.getLocalConnection("test"), "jobs");
if (results == null) {
redirect("/");
}
return ok(find_job.render(results));
}
The MongoController called above uses the following function (functioning - tried and tested):
public static List<DBObject> find(DB database, String collectionName) {
DBCursor cursor = database.getCollection(collectionName).find();
List<DBObject> items = new ArrayList<DBObject>();
while (cursor.hasNext()) {
items.add(cursor.next());
}
return items;
}
View:
#(results : List[DBObject])
The error I'm receiving is:
Compilation error:
Not found: type DBObject
Does anybody have any pointers please? I apologise - I'm new to this framework.
Twirl templates don't work quite the same way when importing things into the local namespace. By default only the models package, and a few other Play packages are imported into the view namespace. Imports made afterward are not used for the template parameters.
You need to either use the fully-qualified package name:
#(results: List[com.mongodb.DBObject])
Or you can add to the templateImports key in build.sbt. i.e., you'd drop this line into your build.sbt file:
TwirlKeys.templateImports += "com.mongodb.DBObject"
This will add the previous import to all compiled templates. Note that it's for Play 2.3.x. If using 2.2.x, then you'd use the following instead:
templatesImport += "com.mongodb.DBObject"
I need to add a route for the following syntax:
http://www.testsite.com/select?term=query1
In my routes file, I tried using the following
GET /select/{term}
However, the above does not catch the URL - instead it goes to another handler in the config (placed beneath the handler for select/{term}:
GET /{auth}
Any thoughts on fixing or troubleshooting this would be most welcome. thanks
?term= means that term is a parameter - not part of the route you are trying to match
so you'd write
GET /select YourControllerClass.yourMethod
....
YourControllerClass extends Controller {
public static void yourMethod(String term){
Logger.debug("term=" + term);
}
}
If your URL was http://www.testsite.com/select/query1 then the route definition you provided above should work