Git Product home page Git Product logo

vane's Introduction

Vane

Vane is server side framework written and optimized for the Dart programming language. Vane comes bundled with a lightweight and performant middleware system and strives to provide commonly used parameters and objects in an easy to use manner such as query parameters or json body data.

Summary

  • Supports three handler types; Func, Podo and Vane
  • Class based, easy to make your own standard classes by extending any Podo or Vane class and adding your own behavior
  • Simple top level access to commonly used data such as query parameters, json body or file uploads
  • Out of the box websocket support
  • Any Vane class can run as the main controller or as a middleware
  • Middlewares can be defined to run synchronously or asynchronously, before or after the main controller
  • Built in "plug and play" support for Mongodb

Handlers

Vane supports three different types of handlers:

  1. Vane handlers - Classes that extend the Vane class
  2. Podo handlers - "Plain Old Dart Objects", normal classes that have one or more functions with the @Route annotation
  3. Func handlers - Function handlers, normal dart function with the @Route annotation

Vane handler

A vane handler is any class that extends the Vane class. When you extend the Vane class your handler functions get access to many helpers and features that are part of the Vane framework. In a vane handler you have access to a set of top level helpers to make life easier, some example of these are a ready to use parsed version of incoming json data called "json".

A Vane class can either run on it's own or in a pipeline of a set of Vane controllers. When mulitple a Vane controller is used in a pipeline to process a request those other than the main controller are called middleware controllers, but it's not a different type of controller and middleware controllers can themself also have their own middleware controllers. Inside a Vane controller you can either end the controller by returning next() or close(), if you return with next() the next middleware controller will run (if there is one, otherwise the call will be changed to a close() call). If you call close() that will end the request even if there are middleware controllers that have yet not run.

Vane classes registered to as middleware can run either before or after the main controller. Middleware controllers can run synchronously or asynchronously and you are guaranteed that they execute in the order you define. Per default middleware controllers run synchronously and the next controller only starts when the current one has finished. You can choose to run one or more middleware controllers in async and also combine both a set of synchronous and asynchronous controller to create more complex pipelines for processing.

Hello World Example:

class HelloVane extends Vane {
  @Route("/")
  Future World() { 
    return close("Hello world! (from vane handler)");
  }
}

Middleware Example:

class HelloVane extends Vane {
  var pipeline = [MyMiddleware, This]
  @Route("/")
  Future World() { 
    return close("Hello world! (from vane handler)");
  }
}

class MyMiddleware extends Vane {
  Future main() { 
    write("Hello from middleware!");
    return next();
  }
}

Podo handler

A podo handler is a "Plain Old Dart Object", basically any Dart class that have 1 or more function handlers with declared with the @Route annotation.

Hello World Example:

class HelloPodo {
  @Route("/")
  void World(HttpRequest request) {
    request.response.write("Hello World! (from podo handler)");
    request.response.close();
  }
}

Func handler

A function handler is simple a function that takes at least 1 HttpRequest parameter and optionally 1 or more parameters that can be mapped from the url.

Hello World Example:

@Route("/")
void helloFuncWorld(HttpRequest request) {
  request.response.write("Hello World! (from func handler)");
  request.response.close();
}

Vane server (server.dart)

With Vane you don't have to worry about writing a dart/web server, you focus on writing your controllers/handlers and Vane serves them for you automatically based on your @Route annotations.
All you need to do is to make sure they are in the same library and that you start the serve function.

Hello World with a Vane handler

import 'dart:async';
import 'package:vane/vane.dart';

class HelloWorld extends Vane {
  @Route("/")
  Future Hello() {
    return close("Hello world");
  }
}

void main() => serve();

Example with all three types of handlers

import 'dart:io';
import 'dart:async';
import 'package:vane/vane.dart';

class HelloVane extends Vane {
  @Route("/")
  @Route("/vane")
  Future World() {
    return close("Hello world! (from vane handler)");
  }

  @Route("/{user}")
  @Route("/vane/{user}")
  Future User(String user) {
    return close("Hello ${user}! (from vane handler)");
  }
}

class HelloPodo {
  @Route("/podo")
  void World(HttpRequest request) {
    request.response.write("Hello World! (from podo handler)");
    request.response.close();
  }

  @Route("/podo/{user}")
  void User(HttpRequest request, String user) {
    request.response.write("Hello World $user! (from podo handler)");
    request.response.close();
  }
}

@Route("/func")
void helloFuncWorld(HttpRequest request) {
  request.response.write("Hello World! (from func handler)");
  request.response.close();
}

@Route("/func/{user}")
void helloFuncUser(HttpRequest request, String user) {
  request.response.write("Hello World $user! (from func handler)");
  request.response.close();
}

void main() => serve();

Documentation, examples and roadmap

vane's People

Contributors

gubaer avatar scorpiion avatar vicb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

vane's Issues

Combining different methods in sub routes results in stack trace

The issue occurs when you combine different request methods in your sub routes (GET, POST, PUT etc.).

Working:

import 'dart:async';
import 'package:vane/vane.dart';

class TestClass extends Vane {

  @Route("/test/")
  Future main() => close("OK");

  @Route("/test/state", method: GET)
  Future state() => close("OK");

  @Route("/test/wake", method: POST)
  Future wake() => close("OK");
}

void main() => serve();

Failing:

import 'dart:async';
import 'package:vane/vane.dart';

@Route("/test")
class TestClass extends Vane {

  @Route("/")
  Future main() => close("OK");

  @Route("/state", method: GET)
  Future state() => close("OK");

  @Route("/wake", method: POST)
  Future wake() => close("OK");
}

void main() => serve();

Stack trace:

2014-07-03 15:34:46.465 WARNING: The null object does not have a method 'contains'.

NoSuchMethodError: method not found: 'contains'
Receiver: null
Arguments: ["GET"]
2014-07-03 15:34:46.465 WARNING: 

2014-07-03 15:34:46.466 WARNING: #0      Object.noSuchMethod (dart:core-patch/object_patch.dart:45)
#1      Router.matchRequest (package:vane/src/router.dart:36:42)
#2      serve.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:vane/src/serve.dart:34:36)
#3      _rootRunUnary (dart:async/zone.dart:730)
#4      _ZoneDelegate.runUnary (dart:async/zone.dart:462)
#5      _CustomizedZone.runUnary (dart:async/zone.dart:667)
#6      _BaseZone.runUnaryGuarded (dart:async/zone.dart:582)
#7      _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:333)
#8      _BufferingStreamSubscription._add (dart:async/stream_impl.dart:263)
#9      _StreamController&&_SyncStreamControllerDispatch._sendData (dart:async/stream_controller.dart:569)
#10     _StreamController._add (dart:async/stream_controller.dart:449)
#11     _StreamController.add (dart:async/stream_controller.dart:406)
#12     _HttpServer._handleRequest.<anonymous closure> (dart:io/http_impl.dart:2248)
#13     _rootRun (dart:async/zone.dart:719)
#14     _ZoneDelegate.run (dart:async/zone.dart:453)
#15     _CustomizedZone.run (dart:async/zone.dart:663)
#16     _BaseZone.runGuarded (dart:async/zone.dart:574)
#17     _BaseZone.bindCallback.<anonymous closure> (dart:async/zone.dart:599)
#18     _rootRun (dart:async/zone.dart:723)
#19     _ZoneDelegate.run (dart:async/zone.dart:453)
#20     _CustomizedZone.run (dart:async/zone.dart:663)
#21     _BaseZone.runGuarded (dart:async/zone.dart:574)
#22     _BaseZone.bindCallback.<anonymous closure> (dart:async/zone.dart:599)
#23     _createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:11)
#24     _handleTimeout (dart:io/timer_impl.dart:292)
#25     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:124)

Binding address from Env

Vane doesn't support specifying the binding address since it's hard coded to 127.0.0.1 in serve.dart#L37. I propose that we add the ADDRESS environment variable and change the default address to InternetAddress.LOOPBACK_IP_V4.

Basically something like this:

var addressEnv = Platform.environment['ADDRESS'];
var address = addressEnv == null ? InternetAddress.LOOPBACK_IP_V4 : addressEnv;

Add support to other basic types than String in handler arguments

Today this works:

  @Route("{?max}", method: GET)
  Future templates(String max)

But this does not work:

  @Route("{?max}", method: GET)
  Future templates(int max)

We should make this work so we support other types than String.

Current workaround:

  @Route("{?max}", method: GET)
  Future templates(int max) {
    var n = int.parse(max);

Add "synchronous ready to use" mongodb connection

Today when you are doing actions on a database, you have to first wait for a future before you get your connection, and by doing so you also get one more indentation block. By providing a "synchronous ready to use" connection this indentation block would not be needed, and would make for easier code to read and write for some use cases.

Alternative implementations:

  1. Change so a connection always is prepared and connected
  2. Add a new getter called just "db" (or something else) and then somehow tell Vane that some classes should prepare a connection ready to use for this getter. It could for example be a "var mongoSync" parameter that is set in the body of the class. Similar to how we do with async/sync for a middleware.
  3. Implement this as a middleware, middlewares are executed synchronously per default (if async is not set) and hence this might be a good use case for this. Implement a middleware called "MongoSync" or "SyncMongo" (or something better?), let that middleware just make the call to "mongodb.then()" then pass the connection on the tube (Vane's built in communication channel) to the handler running after it. The main handler would start with something like "Db db = tube.receive();" and then in can continue without any indentation.

I think I like alternative 3 the best, since this is not a critical issue I leave it here for now. If you have a preference please a comment with either a 1, 2 or 3 and optionally a motivation.

Issues with Platform.script.toFilePath(), from "pub run" and Windows

See:
https://github.com/DartVoid/Vane/blob/master/lib/src/generate_client_routes.dart#L15

This works:
dart bin/server.dart

This fails:
pub run bin/server.dart

Error on Linux using pub run:
Unhandled exception:
Unsupported operation: Cannot extract a file path from a http URI
#0 Uri.toFilePath (dart:core/uri.dart:1612)
#1 generateClientRoutes (package:vane/src/generate_client_routes.dart:15:43)
#2 Router.Router (package:vane/src/router.dart:22:39)
#3 serve (package:vane/src/serve.dart:27:23)
#4 main (http://localhost:53899/server.dart:8:21)
#5 _startIsolate (dart:isolate-patch/isolate_patch.dart:239)
#6 _startMainIsolate. (dart:isolate-patch/isolate_patch.dart:192)
#7 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:130)

Error on Windows (assuming DartEditor, info from email report):
Unhandled exception:
RangeError: index (-1) must be in the range [0..0)
#0 List.
#1 List.removeLast (dart:core-patch/growable_array.dart:207)
#2 generateClientRoutes (package:vane/src/generate_client_routes.dart:18:22)
#3 Router.Router (package:vane/src/router.dart:22:39)
#4 serve (package:vane/src/serve.dart:27:23)
#5 main (file:///C:/Users/jacob/Documents/Vane-Angular-Chat/server/server.dart:8:21)
#6 _startIsolate (dart:isolate-patch/isolate_patch.dart:239)
#7 _startMainIsolate. (dart:isolate-patch/isolate_patch.dart:192)
#8 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:130)

Check how to replace Platform.script.toFilePath() in a more platform agnostic way.

Temporary fix, run with production environment variable set (see https://github.com/DartVoid/Vane/blob/master/lib/src/generate_client_routes.dart#L10):
DART_PRODUCTION=true pub run bin/server.dart

Handle empty json POSTs

Right now I think we get an error if the user tries to send a empty POST request with the content type set to json. While the correct thing might still be not to set content type to json for a request without any body, it still can create hard to figure out bugs when you API changes and you acccidentlity leave the json header but remove the body.

Example:

var response = await _client.post('$_backend/orgs/$org_id/pools/$pool_id/services/mongodbs/$service_id/databases/$database_name',
                                   headers: {"Content-type": "application/json"});

Solution:
In the parsing of the json variable we could check for an empty body and if so don't try to parse the empty body as json.

Use shelf for middleware

Any plan to use shelf for middleware ?

Shelf is part of the Dart sdk.

Middleware allow re-using code across frameworks but this would not work if every framework comes with its own middleware impl.

Using mongodb with an invalid MONGODB_URI results in stack trace

The issue occurs when an application tries to use the mongodb object on a server that has been started with an invalid MONGODB_URI environment variable. E.g starting a Vane server from the DartEditor on a local machine without MongoDB installed.

Stack trace:

2014-06-29 21:46:59.593 WARNING: The null object does not have a getter 'length'.

NoSuchMethodError: method not found: 'length'
Receiver: null
Arguments: []
2014-06-29 21:46:59.593 WARNING: 

2014-06-29 21:46:59.595 WARNING: #0      Object.noSuchMethod (dart:core-patch/object_patch.dart:45)
#1      Uri.parse (dart:core/uri.dart:188)
#2      Db._parseUri (package:mongo_dart/src/database/db.dart:139:24)
#3      Db.open.<anonymous closure> (package:mongo_dart/src/database/db.dart:193:49)
#4      List.forEach (dart:core-patch/growable_array.dart:227)
#5      Db.open (package:mongo_dart/src/database/db.dart:192:21)
#6      _SessionManager.session (package:vane/src/session_manager.dart:55:30)
#7      Vane.mongodb (package:vane/src/vane.dart:507:37)
#8      getAll (file:///home/andreas/dart/vane001/bin/vane001.dart:372:5)
#9      Vane.call.<anonymous closure>.<anonymous closure> (package:vane/src/vane.dart:879:40)
#10     _rootRunUnary (dart:async/zone.dart:730)
#11     _ZoneDelegate.runUnary (dart:async/zone.dart:462)
#12     _CustomizedZone.runUnary (dart:async/zone.dart:667)
#13     _Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:488)
#14     _Future._propagateToListeners (dart:async/future_impl.dart:571)
#15     _Future._completeWithValue (dart:async/future_impl.dart:331)
#16     _Future._asyncComplete.<anonymous closure> (dart:async/future_impl.dart:393)
#17     _rootRun (dart:async/zone.dart:723)
#18     _ZoneDelegate.run (dart:async/zone.dart:453)
#19     _CustomizedZone.run (dart:async/zone.dart:663)
#20     _BaseZone.runGuarded (dart:async/zone.dart:574)
#21     _BaseZone.bindCallback.<anonymous closure> (dart:async/zone.dart:599)
#22     _asyncRunCallbackLoop (dart:async/schedule_microtask.dart:23)
#23     _asyncRunCallback (dart:async/schedule_microtask.dart:32)
#24     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:128)

Maybe the database manager could check if MONGODB_URI is null on start
and prevent applications from using the mongodb object?

Sending json data with a GET request results in stacktrace

When you try to send JSON data with a GET request which obviously isn't recommended/possible; Vane still tries to parse a request which has an empty body.

I've included a sample client and server that should reproduce the issue:

Client

import 'dart:html';
//...
HttpRequest.request("/magrathea", method: "GET",
  requestHeaders: {"Content-Type": "application/json"},
  sendData: '{"name":"Zaphod"}'
).then((response) {
  print(response.responseHeaders);
  print(response.responseText);
})
//...

Server

import 'dart:io';
import 'dart:async';
import 'package:vane/vane.dart';

class MagratheaService extends Vane {
  @Route("/magrathea")
  Future main() {
    print(json);
    return close("OK");
  }
}

We should probably throw an exception that gives developers a hint of what went wrong.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.