/java-express

🧪 HTTP Framework based on expressjs, no dependencies, simple usage. Can be used to quickly spin up an API or serve local files.

Primary LanguageJavaMIT LicenseMIT

Logo

Simple and fast HTTP-Framework with the touch of expressjs

License MIT Support me

State of this project

I created this years ago and I'm no longer actively working with java. If anyone is interested maintaining this (and has experience with java), feel free to leave a comment here. It merely was a proof-of-concept of how we could do things more the "funcional" oriented way and less like big java-frameworks and library use to be (as seen on simple but extremely popular libraries such as expressjs).

Getting Started

Express app = new Express();

app.get("/", (req, res) -> {
   res.send("Hello World");
}).listen(); // Will listen on port 80 which is set as default

Installation

Add the following dependency coordinate from JCenter on your favorite build system:

Download

io.vacco.java-express:java-express:<VERSION>

Routing

DynExpress

Express allows the attaching of request-handler to instance methods via the DynExpress annotation:

// Your main class
import express.Express;
public class Main {
    public static void main(String[] args) {
        Express app = new Express();
        app.bind(new Bindings()); // See class below
        app.listen();
    }
}

// Your class with request handlers
import express.DynExpress;
import express.http.RequestMethod;
import express.http.request.Request;
import express.http.response.Response;
public class Bindings {

    @DynExpress() // Default is context="/" and method=RequestMethod.GET
    public void getIndex(Request req, Response res) {
        res.send("Hello World!");
    }

    @DynExpress(context = "/about") // Only context is defined, method=RequestMethod.GET is used as method
    public void getAbout(Request req, Response res) {
        res.send("About page");
    }

    @DynExpress(context = "/impressum", method = RequestMethod.PATCH) // Both defined
    public void getImpressum(Request req, Response res) {
        res.send("Impressum page was patched");
    }

    @DynExpress(method = RequestMethod.POST) // Only the method is defined, "/" is used as context
    public void postIndex(Request req, Response res) {
        res.send("POST to index");
    }
}

Direct

You can add routes (And middlewares) directly to the Express object to handle requests:

Express app = new Express();

// Sample for home routes
app.get("/", (req, res) -> res.send("Hello index!"));
app.get("/home", (req, res) -> res.send("Homepage"));
app.get("/about", (req, res) -> res.send("About"));

// Sample for user
app.get("/user/login", (req, res) -> res.send("Please login!"));
app.get("/user/register", (req, res) -> res.send("Join now!"));

app.listen();

Directly it also supports methods like POST PATCH DELETE and PUT, others need to be created manually:

Express app = new Express();

// Basic methods
app.get("/user", (req, res) -> res.send("Get an user!"));
app.patch("/user", (req, res) -> res.send("Modify an user!"));
app.delete("/user", (req, res) -> res.send("Delete an user!"));
app.put("/user", (req, res) -> res.send("Add an user!"));

// Example fot the CONNECT method
app.on("/user", "CONNECT", (req, res) -> res.send("Connect!"));

app.listen();

With Router

But it's better to split your code, right? With the ExpressRouter you can create routes and add it later to the Express object:

Express app = new Express() {{

  // Define root greeting
  get("/", (req, res) -> res.send("Hello World!"));

  // Define home routes
  use("/home", new ExpressRouter(){{
    get("/about", (req, res) -> res.send("About page"));
    get("/impressum", (req, res) -> res.send("Impressum page"));
    get("/sponsors", (req, res) -> res.send("Sponsors page"));
  }});

  // Define root routes
  use("/", new ExpressRouter(){{
    get("/login", (req, res) -> res.send("Login page"));
    get("/register", (req, res) -> res.send("Register page"));
    get("/contact", (req, res) -> res.send("Contact page"));
  }});

  // Start server
  listen();
}};

URL Basics

Over the express object you can create handler for all request-methods and contexts. Some examples:

app.get("/home", (req, res) -> {
	// Will match every request which uses the 'GET' method and matches the '/home' path
});

app.post("/login", (req, res) -> {
	// Will match every request which uses the 'POST' method and matches the /login' path
});

URL Parameter

Sometimes you want to create dynamic URL where some parts of the URL's are not static. With the : operator you can create variables in the URL which will be saved later in a HashMap.

Example request: GET /posts/john/all:

app.get("/posts/:user/:description", (req, res) -> {
   String user = req.getParam("user"); // Contains 'john'
   String description = req.getParam("description"); // Contains 'all'
   res.send("User: " + user + ", description: " + description); // Send: "User: john, description: all"
});

URL Parameter Listener

You can also add an event listener when the user called an route which contains an certain parameter.

app.get("/posts/:user/:id", (req, res) -> {
  // Code
});

For example, if we want to check every id before the associated get post etc. handler will be fired, we can use the app.onParam([PARAM]) function:

app.onParam("id", (req, res) -> {
  // Do something with the id parameter, eg. check if it's valid.
});

Now, this function will be called every time when an context is requested which contains the id parameter placeholder.

URL Querys

If you make an request which contains querys, you can access the querys over req.getQuery(NAME).

Example request: GET /posts?page=12&from=john:

app.get("/posts", (req, res) -> {
   String page = req.getQuery("page"); // Contains '12'
   String from = req.getQuery("from"); // Contains 'John'
   res.send("Page: " + page + ", from: " + from); // Send: "Page: 12, from: John"
});

Cookies

With req.getCookie(NAME) you can get an cookie by his name, and with res.setCookie(NAME, VALUE) you can easily set an cookie.

Example request: GET /setcookie:

app.get("/setcookie", (req, res) -> {
   Cookie cookie = new Cookie("username", "john");
   res.setCookie(cookie);
   res.send("Cookie has been set!");
});

Example request: GET /showcookie:

app.get("/showcookie", (req, res) -> {
   Cookie cookie = req.getCookie("username");
   String username = cookie.getValue();
   res.send("The username is: " + username); // Prints "The username is: john"
});

Form data

Over req.getFormQuery(NAME) you receive the values from the input elements of an HTML-Form. Example HTML-Form:

<form action="http://localhost/register" method="post">
   <input description="text" name="email" placeholder="Your E-Mail">
   <input description="text" name="username" placeholder="Your username">
   <input description="submit">
</form>

Attention: Currently, File-inputs don't work, if there is an File-input the data won't get parsed! Now description, for the example below, john in username and john@gmail.com in the email field. Java code to handle the post request and access the form elements:

app.post("/register", (req, res) -> {
  String email = req.getFormQuery("email");
  String username = req.getFormQuery("username");
  // Process data

  // Prints "E-Mail: john@gmail.com, Username: john"
  res.send("E-Mail: " + email + ", Username: " + username);
});

HTTP Relevant classes

Express

This class represents the entire HTTP-Server, the available methods are:

app.get(String context, HttpRequest handler);                   // Add an GET request handler
app.post(String context, HttpRequest handler);                  // Add an POST request handler
app.patch(String context, HttpRequest handler);                 // Add an PATCH request handler
app.put(String context, HttpRequest handler);                   // Add an PUT request handler
app.delete(String context, HttpRequest handler);                // Add an DELETE request handler
app.all(HttpRequest handler);                                   // Add an handler for all methods and contexts
app.all(String context, HttpRequest handler);                   // Add an handler for all methods but for an specific context
app.all(String context, String method, HttpRequest handler);    // Add an handler for an specific method and context
app.use(String context, String method, HttpRequest handler);    // Add an middleware for an specific method and context
app.use(HttpRequest handler);                                   // Add an middleware for all methods but for an specific context
app.use(String context, HttpRequest handler);                   // Add an middleware for all methods and contexts
app.use(String context, ExpressRouter router);                  // Add an router for an specific root context
app.use(ExpressRouter router);                                  // Add an router for the root context (/)
app.onParam(String name, HttpRequest handler);                  // Add an listener for an specific url parameter
app.getParameterListener();                                     // Returns all parameterlistener
app.get(String key);                                            // Get an environment variable
app.set(String key, String val);                                // Set an environment variable
app.isSecure();                                                 // Check if the server uses HTTPS
app.setExecutor(Executor executor);                             // Set an executor service for the request
app.listen();                                                   // Start the async server on port 80
app.listen(ExpressListener onstart);                            // Start the async server on port 80, call the listener after starting
app.listen(int port);                                           // Start the async server on an specific port
app.listen(ExpressListener onstart, int port);                  // Start the async server on an specific port call the listener after starting
app.stop();                                                     // Stop the server and all middleware worker

Response Object

Over the response object, you have serveral possibility like setting cookies, send an file and more. Below is an short explanation what methods exists: (We assume that res is the Response object)

res.getContentType();                  // Returns the current content type
res.setContentType(MediaType type);    // Set the content type with enum help
res.setContentType(String type);       // Set the content type
res.isClosed();                        // Check if the response is already closed
res.getHeader(String key);             // Get the value from an header field via key
res.setHeader(String key, String val); // Add an specific response header
res.sendAttachment(Path file)          // Sends a file as attachment
res.send(String str);                  // Send a string as response
res.send(Path path);                   // Send a file as response
res.send(byte[] bytes)                 // Send bytes as response
res.send();                            // Send empty response
res.redirect(String location);         // Redirect the request to another url
res.setCookie(Cookie cookie);          // Add an cookie to the response
res.sendStatus(Status status);         // Set the response status and send an empty response
res.getStatus();                       // Returns the current status
res.setStatus(Status status);          // Set the repose status
res.streamFrom(long contentLength, InputStream is, MediaType mediaType) // Send a inputstream with known length and type

The response object calls are comments because you can only call the .send(xy) once each request!

Request Object

Over the Request Object you have access to serveral request stuff (We assume that req is the Request object):

req.getAddress();                 // Returns the INET-Adress from the client
req.getMethod();                  // Returns the request method
req.getPath();                    // Returns the request path
req.getContext();                 // Returns the corresponding context
req.getQuery(String name);        // Returns the query value by name
req.getHost();                    // Returns the request host
req.getContentLength();           // Returns the content length
req.getContentType();             // Returns the content type
req.getMiddlewareContent(String name); // Returns the content from an middleware by name
req.getFormQuerys();              // Returns all form querys
req.getParams();                  // Returns all params
req.getQuerys();                  // Returns all querys
req.getFormQuery(String name);    // Returns the form value by name
req.getHeader(String key);        // Returns the value from an header field by name
req.getParam(String key);         // Returns the url parameter by name
req.getApp();                     // Returns the related express app
req.getCookie(String name);       // Returns an cookie by his name
req.getCookies();                 // Returns all cookies
req.getIp();                      // Returns the client IP-Address
req.getUserAgent();               // Returns the client user agent
req.getURI();                     // Returns the request URI
req.isFresh();                    // Returns true if the connection is fresh, false otherwise (see code inline-doc)
req.isStale();                    // Returns the opposite of req.fresh;
req.isSecure();                   // Returns true when the connection is over HTTPS, false otherwise
req.isXHR();                      // Returns true if the 'X-Requested-With' header field is 'XMLHttpRequest'
req.getProtocol();                // Returns the connection protocol
req.getAuthorization();           // Returns the request authorization
req.hasAuthorization();           // Check if the request has an authorization
req.pipe(OutputStream stream, int buffersize); // Pipe the request body to an outputstream
req.pipe(Path path, int buffersize);           // Pipe the request body to an file
req.getBody();                    // Returns the request inputstream

Middleware

Middleware are one of the most important features of JavaExpress, with middleware you can handle a request before it reaches any other request handler. To create an own middleware you have serveral interfaces:

  • HttpRequest - Is required to handle an request.
  • ExpressFilter - Is required to put data on the request listener.
  • ExpressFilterTask - Can be used for middleware which needs an background thread.

Middlewares work, for you, exact same as request handler. For example an middleware for all request-methods and contexts:

// Global context, matches every request.
app.use((req, res) -> {
  // Handle data
});

You can also filter by request-methods and contexts:

// Global context, you can also pass an context if you want
app.use("/home", "POST", (req, res) -> {
  // Handle request by context '/home' and method 'POST'
});

In addition to that yo can use * which stands for every context or request-method:

// Global context, you can also pass an context if you want
app.use("/home", "*", (req, res) -> {
  // Handle request which matches the context '/home' and all methods.
});

Create own middleware

Now we take a look how we can create own middlewares. Here we create an simple PortParser which parse / extract the port-number for us. We only used HttpRequest and ExpressFilter because we don't need any background thread.

public class PortMiddleware implements HttpRequest, ExpressFilter {

   /**
    * From interface HttpRequest, to handle the request.
    */
   @Override
   public void handle(Request req, Response res) {
      
      // Get the port
      int port = req.getURI().getPort();
      
      // Add the port to the request middleware map
      req.addMiddlewareContent(this, port);

      /**
       * After that you can use this middleware by call:
       *   app.use(new PortMiddleware());
       *   
       * Than you can get the port with:
       *   int port = (Integer) app.getMiddlewareContent("PortParser");
       */
   }

   /**
    * Defines the middleware.
    *
    * @return The middleware name.
    */
   @Override
   public String getName() {
      return "PortParser";
   }
}

Now we can, as we learned above, include it with:

// Global context, you can also pass an context if you want
app.use(new PortMiddleware());

And use it:

app.get("/port-test", (req, res) -> {
  
  // Get the content from the PortParser which we create above
  int port = (Integer) req.getMiddlewareContent("PortParser");
   
  // Return it to the client:
  res.send("Port: " + port);
});

Existing Middlewares

There are already some basic middlewares included, you can access these via static methods provided from Middleware.

CORS

To realize a cors api yu can use the cors middleware.

app.use(Middleware.cors());

You can use CorsOptions to specify origin, methods and more:

CorsOptions corsOptions = new CorsOptions();
corsOptions.setOrigin("https://mypage.com");
corsOptions.setAllowCredentials(true);
corsOptions.setHeaders(new String[]{"GET", "POST"});
corsOptions.setFilter(req -> // Custom validation if cors should be applied);
        
app.use(Middleware.cors());

Provide static Files

If you want to allocate some files, like librarys, css, images etc. you can use the static middleware. But you can also provide other files like mp4 etc.

Example:

 app.use(Middleware.statics("examplepath\\myfiles"));

Now you can access every files in the test_statics over the root adress \. I'ts also possible to set an configuration for the FileProvider:

FileProviderOptionsoptions = new FileProviderOptions();
options.setExtensions("html", "css", "js"); // By default, all are allowed.

/*
 * Activate the fallbacksearch.
 * E.g. if an request to <code>/js/code.js</code> was made but the
 * requested ressource cannot be found. It will be looked for an file called <code>code</code>
 * and return it.
 *
 *  Default is false
 */
options.setFallBackSearching(true);
options.setHandler((req, res) -> {...});    // Can be used to handle the request before the file will be returned.
options.setLastModified(true);              // Send the Last-Modified header, by default true.
options.setMaxAge(10000);                   // Send the Cache-Control header, by default 0.
options.setDotFiles(DotFiles.DENY);         // Deny access to dot-files. Default is IGNORE.
app.use(Middleware.statics("examplepath\\myfiles", new FileProviderOptions())); // Using with StaticOptions

Cookie Session

There is also an simple cookie-session implementation:

// You should use an meaningless cookie name for serveral security reasons, here f3v4.
// Also you can specify the maximum age of the cookie from the creation date and the file types wich are actually allowed.
app.use(Middleware.cookieSession("f3v4", 9000));

To use a session cookie we need to get the data from the middleware which is actually an SessionCookie:

 // Cookie session example
app.get("/session", (req, res) -> {

   /**
   * CookieSession named his data "Session Cookie" which is
   * an SessionCookie so we can Cast it.
   */
   SessionCookie sessionCookie = (SessionCookie) req.getMiddlewareContent("sessioncookie");
   int count;
   
Check if the data is null, we want to implement an simple counter
   if (sessionCookie.getData() == null) {
   
      // Set the default data to 1 (first request with this session cookie)
      count = (Integer) sessionCookie.setData(1);
   
   } else {
      // Now we know that the cookie has an integer as data property, increase it
      count = (Integer) sessionCookie.setData((Integer) sessionCookie.getData() + 1);
   }

Send an info message
   res.send("You take use of your session cookie " + count + " times.");
});

Global Variables

Java-express also supports to save and read global variables over the Express instance.

app.set("my-data", "Hello World");
app.get("my-data"); // Returns "Hello World"

Examples

Very simple static-website

// Create instance
new Express() {{
  
  // Define middleware-route for static site
  use("/", Middleware.statics("my-website-folder/"));
}};

File download

// Your file
Path downloadFile = Paths.get("my-big-file");

// Create instance
new Express() {{

  // Create get-route where the file can be downloaded
  get("/download-me", (req, res) -> res.sendAttachment(downloadFile));
}};

Send cookies

new Express() {{

  // Define route
  get("/give-me-cookies", (req, res) -> {
  
    // Set an cookie (you can call setCookie how often you want)
    res.setCookie(new Cookie("my-cookie", "Hello World!"));
    
    // Send text
    res.send("Your cookie has been set!");
  });
}};

License

This project is licensed under the MIT License - see the LICENSE file for details.