Implement EndpointSelector and MatcherPolicy
This change makes the EndpointSelector API more concrete, and is the
beggining of removing EndpointConstraint by making it obsolete.
To that end, I'm introducing MatcherPolicy, which is a
feature-collection API for registering policies that interact with the
DfaMatcher. The two policies that we'll need to start are:
- ability to order endpoints
- ability to append 'policy' nodes to the graph
These two concepts together replace EndpointConstraint. Extending our
graph representation is a really efficient way to processing most common
scenarios.
---
In general this helps with common cases where 4 or so endpoints match
the URL, but with different HTTP methods supported on each. Today we
have to process route values and call into some 'policy' to make a
decision about which one is the winner. This change pushes this
knowledge down into the graph so that it's roughly as cheap as a
dictionary lookup, and can be done allocation-free.
The big savings here is ability to remove more candidates *before*
collecting route data.
---
Along with this change, I also built 'rejection' into the DFA node
model, you can see an example with the HTTP Method handling that I
implemented. I implemented a policy that can treat failure to resolve an
HTTP method as a 405 response by returning a failure endpoint. This is
at the heart of much of the feedback we've gotten in this area around
versioning and http method handling. We also have a version of this
feature in MVC for [Consumes].
For preview one the branding is:
new thing = UseGlobalRouting/UseEndpoint
old thing = UseRouter
We're going to drop the name Dispatcher everywhere and make sure that we
position our new work as 'new and improved routing' instead of
introducing a new product/concept name.
We're not totally sure of the term Global yet, but it's what we're doing
for preview 1. Suggestions welcome for dicussion after we do the first
preview :)
Add benchmarks that include some HTTP method matching.
Clean up names and name like-kinded benchmarks alphabetically.
Matcher*Benchmark -> E2E including HTTP method selection
MatcherSelectCandidates*Benchmark -> Focused on just URL path processing
* Introduce RoutePattern
Introduces RoutePattern - a new parser and representation for routing
templates, defaults, and constraints.
This is a new representation for all of the 'inputs' to routing that is
immutable and captures 'out of line' information for defaults and
constraints.
This will allow us to unify the handling of constraints and values from
attribute style routes and conventional style routes.
* Add benchmarks for RVD
There are the scenarios that are critical for URL matching performance.
* Reimplement RouteValueDictionary
Improves the scenarios with benchmarks by about 30%
* Fix benchmark
* PR feedback
* More feedback and tests
Here's a code dump of the parts of the Dispatcher prototype codebase
that are needed to get us off the ground.
This first cut attempts to use part of routing where possible, and not
all of those changes will be long-lasting.
I'll leave comments through thoughout the PR for education.
This change adds the Template as a top level abstraction. URL templating
is now a two-stage process.
First you use a 'key' to look up a Template, then you use the Template
to create the URL.
This change also has some cleanup of the way RoutePatternBinder gets
instantiated. I added a factory service so that most of the complex
things can be made internal to Dispatcher. Now it's much easier to
constuct and use. These impacts some pubternal APIs that we already
broke, but makes them actually nice :)
Also cleaned up some tests and fixed one that was broken and not
running.
-Renamed RouteTemplate -> RoutePattern
-Made immutable
-Added Builder
-Lots of fixes to parser to support new design
There are a few small issues logged for follow-up but this is mostly in
the place I want it design-wise.
This is a very small micro-optimization: When LowercaseUrls and/or AppendTrailingSlash
options are enabled, on every call to RouteCollection.NormalizeVirtualPath a new
char[] { '?', '#' } is being allocated.
This makes endpoints and addresses easier to work with by dropping the
'metadata first' approach for the the things that are really at the core
of the dispatcher.