+
Skip to content

Event Handler Changes #784

@tmenier

Description

@tmenier

While testing 4.0 I was reminded that I've soured on how event handlers work. Currently they are assigned to the Settings object, most commonly on FlurlClient or FlurlRequest:

client.Settings.BeforeCall = call => DoSomething();

There's a few reasons that I've come to think this is less than ideal:

  1. You can only assign 1 handler per event type at each level. (Well, technically 2. There's also an async version of each, i.e. Settings.BeforeCallAsync.) Most event-based patterns allow for any number of handlers (or subscribers).
  2. The fact that they're lambda-based and not class/interface-based make using them in conjunction with DI a little harder. It's possible, but you have to write a wrapper class and Flurl doesn't really give you anything prescriptive for that.
  3. They muddy up Settings a bit, accounting for about half of the available properties. And as "actions", they don't quite feel like typical configuration settings - they feel like something different.

What's Breaking

Quite simply, all 8 event handler properties are being removed from Settings. Instead, IFlurlClient, IFlurlRequest, and the new IFlurlClientBuilder will have a new EventHandlers property, defined as an IList<(FlurlEventType, IFlurlEventHandler)>.

FlurlEventType is an enum with 4 values: BeforeCall, AfterCall, OnError, and OnRedirect. These should be familar if you've used events in 3.x, and there's no change to when they fire.

IFlurlEventHandler defines 2 methods:

void Handle(FlurlEventType eventType, FlurlCall call);
Task HandleAsync(FlurlEventType eventType, FlurlCall call);

There's also a default implementation - FlurlEventHandler - that defines both methods as virtual noops. Inheriting this should be more convenient than implementing the interface since it's extremely likely you'll want to implement either the sync or async method and not both.

What's Staying the Same

Flurl 3.x introduced fluent methods that let you assign event handlers like this:

client
    .BeforeCall(call => DoSomething())
    .OnError(call => HandleError(call.Exception))
    .AfterCallAsync(call => LogAsync(call))
    ...

These still work, with the subtle difference that they're now "additive", as opposed to overwriting any existing handler at the same level. That's probably rare, so if you're already doing event handlers this way, you should be in good shape. If you've configuring handlers on Settings directly, switching to the above approach is the easiest path to making them 4.0-compatible. Under the hood, those actions attach themselves to IFlurlEventHandler implementations, but maintain the 3.x-compatible lambda approach at the surface.

In the future I may look at adding helpers for using the class-based approach more directly and recommend DI-friendly patterns, but those enhancements can go in a minor version. I wanted to get the breaking aspects in place ahead of the official 4.0 release.

Metadata

Metadata

Assignees

No one assigned

    Projects

    Status

    Released

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载