Angel3 Developer Guide
  • README
  • Foreword
  • Tutorial
    • Getting Started
    • Minimal Setup
  • Command Line Interface
    • Setup
  • Templates and Views
    • Server Side Rendered Views
    • JAEL3
      • About
      • Basics
      • Custom Elements
      • Strict Resolution
      • Directive: declare
      • Directive: for-each
      • Directive: extend
      • Directive: if
      • Directive: include
      • Directive: switch
  • Authentication
    • About
    • Strategies
    • Local
  • Databases
    • Object Relational Mapping (ORM)
      • About
      • Basic Functionality
      • Relations
      • Migrations
      • PostgreSQL
    • NoSQL
  • Extensions and Plugins
    • Using Plug-ins
    • Writing a Plugin
  • Under the hood
    • Basic Routing
    • Requests & Responses
    • Request Lifecycle
    • Dependency Injection
    • Middleware
    • Controllers
    • Parsing Request Bodies
    • Serialization
    • Service Basics
    • Testing
    • Error Handling
    • Pattern Matching and Parameter
  • Angel Framework Migration
    • Angel 2.x.x to Angel3
      • Rationale - Why a new Version?
      • Framework Changelog
      • 3.0.0 Migration Guide
    • Angel 1.x.x to 2.x.x
      • Framework Changelog
      • 2.0.0 Migration Guide
  • Packages
    • Authentication
    • CORS
    • Database-Agnostic Relations
    • Configuration
    • Databases
      • ORM
      • MongoDB
      • JSON File-based
      • RethinkDB
    • Templates and Views
      • Jael template engine
      • Mustache Templates
      • compiled_mustache-based engine
      • html_builder-based engine
      • Markdown template engine
      • Using Angel with Angular
    • Hot Reloading
    • Pagination
    • Polling
    • Production Utilities
    • REST Client
    • Reverse Proxy
    • Router
    • Serialization
    • Service Seeder
    • Static Files
    • Security
    • Server-sent Events
    • shelf Integration
    • Task Engine
    • User Agents
    • Validation
    • Websockets
  • Resources
    • Dependency Injection Patterns
    • Example Projects
    • YouTube Tutorials
    • Ecosystem
Powered by GitBook
On this page
  • Middleware
  • Denying Requests via Middleware
  • Declaring Middleware
  • Global Middleware
  • Maintaining Code Readability
  • Next Up

Was this helpful?

  1. Under the hood

Middleware

PreviousDependency InjectionNextControllers

Last updated 3 years ago

Was this helpful?

Middleware

Sometimes, it becomes to recycle code to run on multiple routes. Angel3 allows for this in the form of middleware. Middleware are frequently used as authorization filters, or to serialize database data for use in subsequent routes. Middleware in Angel3 can be any route handler, whether a function or arbitrary data. You can also throw exceptions in middleware.

Denying Requests via Middleware

A middleware should return either true or false. If false is returned, no further routes will be executed. If true is returned, route evaluation will continue. (more on request handler return values ).

In practice, you will only need to write a return statement when you are returning true.

As you can imagine, this is perfect for authorization filters.

Declaring Middleware

You can call a router's chain method, or assign middleware in the middleware parameter of a route method.

// All ways ultimately accomplish the same thing.
// Keep it readable!

// Cleanest. Use when it doesn't create visual clutter of its own.
app.chain([cors()]).get('/', 'world!');

// Another readable use of the `.chain()` method.
app.chain([cors()]).get('/something', (req, res) {
  // Do something here...
});

// Use when more than one middleware is involved, or when
// using an anonymous function as a handler (or middleware that spans
// multiple lines)
app.get('/', chain([
  someMiddleware,
  (req, res) => ...,
  (req, res) {
    return 'world!';
  },
]));

// The `middleware: ` parameter is used internally by `package:angel_route`.
// Avoid using it when you can.
app.get('/', 'world!', middleware: [someListOfMiddleware]);

Though this might at first seem redundant, there are actually reasons for all three existing.

By convention, though, follow these readability rules when building Angel3 servers:

  • Routes with no middleware should not use chain, app.chain, or `middleware. Self-explanatory.

  • Routes with one middleware and one handler should use app.chain([...]) when:

    • The construction of all the middleware does not take more than one line.

  • In all other cases, use the chain meta-handler.

  • Avoid using middleware: ... directly, as it is used internally package:route.

Global Middleware

app.fallback((req, res) async => res.close());

For more complicated middleware, you can also create a class.

Canonically, when using a class as a request handler, it should provide a handleRequest(RequestContext, ResponseContext) method. This pattern is seen throughout many Angel3 plugins, such as VirtualDirectory or Proxy.

class MyCanonicalHandler {
 Future<bool> handleRequest(RequestContext req, ResponseContext res) async {
  // Do something cool...
 }
}

app.use(MyCanonicalHandler().handleRequest);

Maintaining Code Readability

Take the following example. At first glance, it might not be very easy to read.

app.get('/the-route', chain([
  banIp('127.0.0.1'),
  'auth',
  ensureUserHasAccess(),
  (req, res) async => true,
  takeOutTheTrash()
  (req, res) {
   // Your route handler here...
  }
]));

In general, consider it a code smell to stack multiple handlers onto a route like this; it hampers readability, and in general just doesn't look good.

Instead, when you have multiple handlers, you can split them into multiple chain calls, assigned to variables, which have the added benefit of communicating what each set of middleware does:

var authorizationMiddleware = chain([
 banIp('127.0.0.1'),
 requireAuthentication(),
 ensureUserHasAccess(),
]);

var someOtherMiddleware = chain([
 (req, res) async => true,
 takeOutTheTrash(),
]);

var theActualRouteHandler = (req, res) async {
 // Handle the request...
};

app.get('/the-route', chain([
 authorizationMiddleware,
 someOtherMiddleware,
 theActualRouteHandler,
]);

Tip: Prefer using named functions as handlers, rather than anonymous functions, or concrete objects.

Next Up

To add a handler that handles every request, call app.fallback. This is merely shorthand for calling app.all('*', <handler>). (more info on request lifecycle ).

The reason for this is that a name like handleRequest makes it very clear to anyone reading the code what it is supposed to do. This is the same rationale behind providing a configureServer method.

Take a good look at in Angel3!

here
controllers
controllers
Middleware
Denying Requests via Middleware
Declaring Middleware
Named Middleware
Global Middleware
chain([...])
**Maintaining Code Readability
Next Up...
here