Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 13 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,26 +93,22 @@ each request. For example, a routing middleware might choose which handler to
call based on the request's URI or HTTP method, while a cascading middleware
might call each one in sequence until one returns a successful response.

Middleware that routes requests between handlers should be sure to update each
request's [`handlerPath`][handlerpath] and [`url`][url]. This allows inner
handlers to know where they are in the application so they can do their own
routing correctly. This can be easily accomplished using
[`Request.copyWith()`][change]:

[handlerpath]:
https://pub.dev/documentation/relic/latest/relic/Request/handlerPath.html
[url]: https://pub.dev/documentation/relic/latest/relic/Request/url.html
Relic provides built-in routing through the [`Router`][router] class and
[`routeWith`][routewith] middleware, which handles path matching and parameter
extraction automatically. Custom routing middleware can be created using the
[`Request.copyWith()`][change] method:

[router]: https://pub.dev/documentation/relic/latest/relic/Router-class.html
[routewith]: https://pub.dev/documentation/relic/latest/relic/routeWith.html
[change]: https://pub.dev/documentation/relic/latest/relic/Request/copyWith.html

```dart
// In an imaginary routing middleware...
var component = request.url.pathSegments.first;
var handler = _handlers[component];
if (handler == null) return Response.notFound();

// Create a new request just like this one but with whatever URL comes after
// [component] instead.
return handler(request.copyWith(path: component));
// Using the built-in router
final router = Router()
..get('/users/:id', userHandler)
..get('/posts/:id', postHandler);

final handler = router.asHandler;
```

## Adapters
Expand Down
5 changes: 1 addition & 4 deletions doc/site/docs/02-reference/04-requests.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ The request flows through your middleware pipeline and reaches your handler, whe
The `Request` object exposes several important properties:

- **`method`** - The HTTP method (GET, POST, PUT, DELETE, etc.) as a `Method` enum value.
- **`url`** - The relative URL from the current handler's perspective, including query parameters.
- **`requestedUri`** - The complete original URI that was requested.
- **`url`** - The complete original URI that was requested.
- **`headers`** - Type-safe access to HTTP headers.
- **`body`** - The request body wrapped in a `Body` helper. Use `await request.readAsString()` for text, or `request.read()` for the byte stream. Both are single-read.
- **`protocolVersion`** - The HTTP protocol version (typically "1.1").
Expand All @@ -46,8 +45,6 @@ The `url` property provides the relative path and query parameters from the curr

GITHUB_CODE_BLOCK lang="dart" [src](https://raw.githubusercontent.com/serverpod/relic/main/example/routing/request_response_example.dart) doctag="path-params-complete" title="Path parameters and URL"

When handling a request to `http://localhost:8080/users/123?details=true`, the `url.path` contains the path relative to the handler, while `requestedUri` contains the complete URL including the domain and all query parameters.

## Working with query parameters

Query parameters are key-value pairs appended to the URL after a question mark (`?`). They're commonly used to pass optional data, filters, or pagination information to your endpoints.
Expand Down
4 changes: 2 additions & 2 deletions doc/site/docs/02-reference/07-middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,8 @@ Middleware layers wrap each other like an onion. Each layer may:

GITHUB_CODE_BLOCK lang="dart" [src](https://raw.githubusercontent.com/serverpod/relic/main/example/middleware/auth_example.dart) doctag="middleware-auth-basic" title="Basic auth middleware"

:::warning Avoid rewriting request.path in router.use middleware
When middleware is attached with `router.use(...)`, the request has already been routed. Changing `request.url.path` at this point will not re-route the request and will not update `request.pathParameters` or related routing metadata.
:::warning Avoid rewriting `request.url` in middleware attached with `router.use`.
When middleware is attached with `router.use(...)`, the request has already been routed. Changing `request.url` at this point will not re-route the request and will not update `request.pathParameters` or related routing metadata.
:::

### CORS (Cross-Origin Resource Sharing)
Expand Down
4 changes: 2 additions & 2 deletions example/advanced/multi_isolate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ Future<void> _serve() async {
}

/// [_echoRequest] just echoes the path of the request
Response _echoRequest(final Request request) {
Response _echoRequest(final Request req) {
sleep(const Duration(seconds: 1)); // pretend to be really slow
return Response.ok(
body: Body.fromString(
'Request for "${request.url}" handled by isolate ${Isolate.current.debugName}',
'Request for "${req.url}" handled by isolate ${Isolate.current.debugName}',
),
);
}
2 changes: 1 addition & 1 deletion example/context/context_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ Hijack customProtocolHandler(final Request req) {
Future<Response> dataHandler(final Request req) async {
// Access basic HTTP information
final method = req.method; // 'GET', 'POST', etc.
final path = req.url.path; // '/api/users'
final path = req.matchedPath; // '/api/users'
final query = req.url.query; // 'limit=10&offset=0'

log('method: $method, path: $path, query: $query');
Expand Down
6 changes: 3 additions & 3 deletions example/routing/request_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ Future<void> main() async {
// doctag<requests-path-params-id>
app.get('/users/:id', (final req) {
final id = req.pathParameters[#id]!;
final url = req.url;
final fullUri = req.requestedUri;
final matchedPath = req.matchedPath;
final fullUri = req.url;

log('Relative URL: $url, id: $id');
log('Matched path: $matchedPath, id: $id');
log('Full URI: $fullUri');

return Response.ok();
Expand Down
6 changes: 3 additions & 3 deletions example/routing/request_response_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ Future<void> main() async {
// doctag<path-params-complete>
app.get('/users/:id', (final req) {
final id = req.pathParameters[#id]!;
final url = req.url;
final fullUri = req.requestedUri;
final matchedPath = req.matchedPath;
final fullUri = req.url;

log('Relative URL: $url, id: $id');
log('Matched path: $matchedPath, id: $id');
log('Full URI: $fullUri');

// Return user data as JSON
Expand Down
2 changes: 1 addition & 1 deletion lib/relic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export 'src/adapter/relic_web_socket.dart';
export 'src/body/body.dart' show Body;
export 'src/body/types/body_type.dart' show BodyType;
export 'src/body/types/mime_type.dart' show MimeType;
export 'src/context/context.dart' hide RequestInternal;
export 'src/context/result.dart' hide RequestInternal;
export 'src/handler/cascade.dart' show Cascade;
export 'src/handler/handler.dart';
export 'src/handler/pipeline.dart' show Pipeline;
Expand Down
2 changes: 1 addition & 1 deletion lib/src/adapter/adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'dart:async';

import 'package:stream_channel/stream_channel.dart';

import '../context/context.dart';
import '../context/result.dart';
import 'relic_web_socket.dart';

/// A callback function that handles a hijacked connection.
Expand Down
2 changes: 1 addition & 1 deletion lib/src/adapter/io/request.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'dart:io' as io;

import '../../body/body.dart';
import '../../body/types/mime_type.dart';
import '../../context/context.dart';
import '../../context/result.dart';
import '../../headers/headers.dart';
import '../../router/method.dart';

Expand Down
2 changes: 1 addition & 1 deletion lib/src/adapter/io/response.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'dart:io';

import '../../context/context.dart';
import '../../context/result.dart';

import 'http_response_extension.dart';

Expand Down
2 changes: 1 addition & 1 deletion lib/src/context/message.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
part of 'context.dart';
part of 'result.dart';

abstract class Message {
/// The HTTP headers associated with this message.
Expand Down
Loading
Loading