Aqueduct HTTP 片段
Hello, World
class AppChannel extends ApplicationChannel {
@override
Controller get entryPoint {
final router = Router ();
router . route ( "/hello_world" ). linkFunction (( request ) async {
return Response . ok ( "Hello, world!" )
.. contentType = ContentType . TEXT ;
});
return router ;
}
}
路由变量
class AppChannel extends ApplicationChannel {
@override
Controller get entryPoint {
final router = Router ();
router . route ( "/variable/[:variable]" ). linkFunction (( request ) async {
return Response . ok ({
"method" : request . raw . method ,
"path" : request . path . variables [ "variable" ] ?? "not specified"
});
});
return router ;
}
}
分组路由和绑定路径变量
class AppChannel extends ApplicationChannel {
@override
Controller get entryPoint {
final router = Router ();
router
. route ( "/users/[:id]" )
. link (() => MyController ());
return router ;
}
}
class MyController extends ResourceController {
final List < String > things = [ 'thing1' , 'thing2' ];
@Operation . get ()
Future < Response > getThings () async {
return Response . ok ( things );
}
@Operation . get ( 'id' )
Future < Response > getThing ( @Bind . path ( 'id' ) int id ) async {
if ( id < 0 || id >= things . length ) {
return Response . notFound ();
}
return Response . ok ( things [ id ]);
}
}
定制中间件
class AppChannel extends ApplicationChannel {
@override
Controller get entryPoint {
final router = Router ();
router
. route ( "/rate_limit" )
. link (() => RateLimiter ())
. linkFunction (( req ) async => Response . ok ({
"requests_remaining" : req . attachments [ "remaining" ]
}));
return router ;
}
}
class RateLimiter extends RequestController {
@override
Future < RequestOrResponse > handle ( Request request ) async {
final apiKey = request . raw . headers . value ( "x-apikey" );
final requestsRemaining = await remainingRequestsForAPIKey ( apiKey );
if ( requestsRemaining <= 0 ) {
return Response ( 429 , null , null );
}
request . addResponseModifier (( r ) {
r . headers [ "x-remaining-requests" ] = requestsRemaining ;
});
return request ;
}
}
应用范围的CORS允许的来源
class AppChannel extends ApplicationChannel {
@override
Future prepare () async {
// 默认情况下,所有控制器都将使用此策略
CORSPolicy . defaultPolicy . allowedOrigins = [ "https://mywebsite.com" ];
}
@override
Controller get entryPoint {
final router = Router ();
router . route ( "/things" ). linkFunction (( request ) async {
return Response . ok ([ "Widget" , "Doodad" , "Transformer" ]);
});
return router ;
}
}
提供文件服务并设置缓存控制标头
class AppChannel extends ApplicationChannel {
@override
Controller get entryPoint {
final router = Router ();
router . route ( "/files/*" ). link (() =>
FileController ( "web" )
.. addCachePolicy ( new CachePolicy ( expirationFromNow: new Duration ( days: 365 )),
( path ) => path . endsWith ( ".js" ) || path . endsWith ( ".css" ))
);
return router ;
}
}
流式响应(带有文本/事件流的服务器端事件)
class AppChannel extends ApplicationChannel {
final StreamController < String > controller = new StreamController < String > ();
@override
Future prepare () async {
var count = 0 ;
Timer . periodic ( new Duration ( seconds: 1 ), ( _ ) {
count ++ ;
controller . add ( "This server has been up for $ count seconds \n " );
});
}
@override
Controller get entryPoint {
final router = new Router ();
router . route ( "/stream" ). linkFunction (( req ) async {
return Response . ok ( controller . stream )
.. bufferOutput = false
.. contentType = new ContentType (
"text" , "event-stream" , charset: "utf-8" );
});
return router ;
}
}
Websocket服务器
class AppChannel extends ApplicationChannel {
List < WebSocket > websockets = [];
@override
Future prepare () async {
// 当另一个隔离收到Websocket消息时,将其回显到与此隔离上连接的Websocket
messageHub . listen ( sendBytesToConnectedClients );
}
@override
Controller get entryPoint {
final router = Router ();
// 允许websocket客户端连接到 ws://host/connect
router . route ( "/connect" ). linkFunction (( request ) async {
var websocket = await WebSocketTransformer . upgrade ( request . raw );
websocket . listen ( echo , onDone: () {
websockets . remove ( websocket );
}, cancelOnError: true );
websockets . add ( websocket );
// 将请求从通道中取出
return null ;
});
return router ;
}
void sendBytesToConnectedClients ( List < int > bytes ) {
websockets . forEach (( ws ) {
ws . add ( bytes );
});
}
void echo ( List < int > bytes ) {
sendBytesToConnectedClients ( bytes );
// 发送到其他 isolates
messageHub . add ( bytes );
}
}
设置内容类型和编码响应体
class AppChannel extends ApplicationChannel {
final ContentType CSV = ContentType ( "text" , "csv" , charset: "utf-8" );
@override
Future prepare () async {
// CsvCodec extends dart:convert.Codec
CodecRegistry . defaultInstance . add ( CSV , new CsvCodec ());
}
@override
Controller get entryPoint {
final router = Router ();
router . route ( "/csv" ). linkFunction (( req ) async {
// 这些值将被CsvCodec转换为一个逗号分隔的字符串
return Response . ok ([[ 1 , 2 , 3 ], [ "a" , "b" , "c" ]])
.. contentType = CSV ;
});
return router ;
}
}
代理来自另一台服务器的文件
class AppChannel extends ApplicationChannel {
@override
Controller get entryPoint {
final router = Router ();
router . route ( "/proxy/*" ). linkFunction (( req ) async {
var fileURL = "https://otherserver/ ${ req . path . remainingPath } " ;
var fileRequest = await client . getUrl ( url );
var fileResponse = await req . close ();
if ( fileResponse . statusCode != 200 ) {
return new Response . notFound ();
}
// A dart:io.HttpResponse is a Stream<List<int>> of its body bytes.
return new Response . ok ( fileResponse )
.. contentType = fileResponse . headers . contentType
// 因为数据已经根据内容类型进行了编码,所以才让数据通过; 再次应用编码会导致问题
.. encodeBody = false ;
});
return router ;
}
}