Skip to content

Commit

Permalink
restructure response handler
Browse files Browse the repository at this point in the history
  • Loading branch information
necessarylion committed Dec 16, 2023
1 parent 9145610 commit 487ec36
Show file tree
Hide file tree
Showing 26 changed files with 274 additions and 121 deletions.
2 changes: 1 addition & 1 deletion packages/dox-core/lib/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class Dox implements IDox {
DoxServer().setResponseHandler(config.responseHandler);
await DoxServer().listen(config.serverPort, isolateId: 1);

DoxLogger.info(sprintf(
Logger.info(sprintf(
'Server started at http://127.0.0.1:%s with $isolatesToSpawn isolate',
<dynamic>[Dox().config.serverPort],
));
Expand Down
5 changes: 1 addition & 4 deletions packages/dox-core/lib/dox_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ export 'constants/constants.dart';
export 'env/env.dart';

/// Exceptions
export 'exception/internal_error_exception.dart';
export 'exception/not_found_exception.dart';
export 'exception/query_exception.dart';
Expand All @@ -29,15 +28,13 @@ export 'http/response/serializer.dart';

/// interfaces
export 'interfaces/app_config.dart';

/// auth
export 'interfaces/dox_middleware.dart';
export 'interfaces/dox_service.dart';
export 'interfaces/response_handler_interface.dart';
export 'interfaces/router.dart';

/// Tools
export 'ioc/ioc_container.dart';
export 'middleware/log_middleware.dart';

/// Router
export 'router/route.dart';
Expand Down
9 changes: 5 additions & 4 deletions packages/dox-core/lib/env/env.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ class Env {
/// Evn.get('APP_KEY', 'with_default_value_if_null');
/// Evn.get<int>('PORT', 3000); (This will return int type)
/// Evn.get<num>('PORT', 3000); (This will return num type)
/// Evn.get<String>('APP_KEY'); (This will return type String)
/// Current this function support String, int and num types.
/// Evn.get<String>('APP_KEY'); (This will return String type)
/// Currently this function support String, int and num types.
/// ```
static T get<T>(String key, [dynamic defaultValue]) {
String value = Env().env[key].toString();
String val =
value.isEmpty || value.toLowerCase() == 'null' ? defaultValue : value;
String val = value.isEmpty || value.toLowerCase() == 'null'
? defaultValue.toString()
: value;
if (T.toString() == 'int') {
return int.parse(val) as T;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/dox-core/lib/http/http_controller_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Future<dynamic> middlewareAndControllerHandler(DoxRequest doxReq) async {

/// DoxMiddleware class
/// with handle function
if (fn is DoxMiddleware) {
if (fn is IDoxMiddleware) {
result = await fn.handle(doxReq);
}

Expand Down Expand Up @@ -68,7 +68,7 @@ Future<dynamic> _handleController(
FormRequest? formReq = Dox().ioc.getByName(requestName);
if (formReq != null && _isFormRequestTypeMatched(fn, formReq)) {
/// mapping request inputs field
doxRequest.mapInputs(formReq.mapInputs());
doxRequest.processInputMapper(formReq.mapInputs());

/// setting dox request to custom form request
formReq.setRequest(doxRequest);
Expand Down
28 changes: 15 additions & 13 deletions packages/dox-core/lib/http/http_cors_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,22 @@ import 'dart:io';

import 'package:dox_core/dox_core.dart';

void httpCorsHandler(HttpRequest req) {
void httpCorsHandler(bool? enabled, HttpRequest req) {
CORSConfig cors = Dox().config.cors;

Map<String, dynamic> headers = <String, dynamic>{
HttpHeaders.accessControlAllowOriginHeader: cors.allowOrigin,
HttpHeaders.accessControlAllowMethodsHeader: cors.allowMethods,
HttpHeaders.accessControlAllowHeadersHeader: cors.allowHeaders,
HttpHeaders.accessControlExposeHeadersHeader: cors.exposeHeaders,
HttpHeaders.accessControlAllowCredentialsHeader: cors.allowCredentials,
};

headers.forEach((String key, dynamic value) {
_setCorsValue(req.response, key, value);
});
if (enabled ?? cors.enabled) {
Map<String, dynamic> headers = <String, dynamic>{
HttpHeaders.accessControlAllowOriginHeader: cors.origin,
HttpHeaders.accessControlAllowMethodsHeader: cors.methods,
HttpHeaders.accessControlAllowHeadersHeader: cors.headers,
HttpHeaders.accessControlExposeHeadersHeader: cors.exposeHeaders,
HttpHeaders.accessControlAllowCredentialsHeader: cors.credentials,
HttpHeaders.accessControlMaxAgeHeader: cors.maxAge,
};

headers.forEach((String key, dynamic value) {
_setCorsValue(req.response, key, value);
});
}
}

// set cors in header
Expand Down
3 changes: 3 additions & 0 deletions packages/dox-core/lib/http/http_request_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:io';

import 'package:dox_core/dox_core.dart';
import 'package:dox_core/http/http_controller_handler.dart';
import 'package:dox_core/http/http_cors_handler.dart';
import 'package:dox_core/http/http_error_handler.dart';
import 'package:dox_core/http/http_response_handler.dart';
import 'package:dox_core/http/http_route_handler.dart';
Expand All @@ -17,6 +18,8 @@ void httpRequestHandler(HttpRequest req) {
RouteData? route = httpRouteHandler(req);
if (route == null) return;

httpCorsHandler(route.corsEnabled, req);

if (WebSocketTransformer.isUpgradeRequest(req)) {
httpWebSocketHandler(req, route);
return;
Expand Down
38 changes: 22 additions & 16 deletions packages/dox-core/lib/http/http_response_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,35 @@ dynamic httpResponseHandler(
dynamic payload,
HttpRequest request,
) {
/// websocket handler will return websocket payload
/// Websocket handler will return websocket payload
/// and in this case we need to remain the connection open for
/// websocket communication. so there is no `res.close()`;
/// websocket communication. So there is no `res.close()` required.
if (payload is WebSocket) {
return;
}

/// if payload is DoxResponse, DoxResponse have process function
/// If there is responseHandler set, handle responseHandler
/// before DoxResponse process. The responseHandler will return
/// DoxResponse to process
ResponseHandlerInterface? customResponseHandler = DoxServer().responseHandler;
if (customResponseHandler != null) {
DoxResponse doxResponse = customResponseHandler
.handle(payload is DoxResponse ? payload : DoxResponse(payload));
payload = doxResponse;
}

/// If payload is DoxResponse, DoxResponse have process function
/// which will set header and status code in the response
/// and will pass payload/content back to this function `httpResponseHandler`
/// where the payload is not DoxResponse anymore
/// so it will continue belows process
/// and will pass payload/content to `responseDataHandler` function.
if (payload is DoxResponse) {
return payload.process(request);
payload.process(request);
return;
}
}

/// Handle response for different types of data
/// such as string, stream, Model, List, encodable.
void responseDataHandler(dynamic payload, HttpRequest request) {
HttpResponse res = request.response;

if (payload == null) {
Expand All @@ -38,6 +51,8 @@ dynamic httpResponseHandler(
return;
}

/// if payload is downloadable file,
/// we response contentDisposition header with stream data.
if (payload is DownloadableFile) {
res.headers.contentType = payload.contentType;
res.headers.add(FILE_DOWNLOAD_HEADER, payload.contentDisposition);
Expand All @@ -51,15 +66,6 @@ dynamic httpResponseHandler(
return;
}

/// if there is responseHandler we handle responseHandler and
/// get new payload and override existing payload
if (DoxServer().responseHandler != null) {
dynamic result = DoxServer().responseHandler?.handle(DoxResponse(payload));
if (result != null) {
payload = result;
}
}

/// if payload handle base Http Exception
/// we set http status from exception and return as map
/// which will parse into json and response as json
Expand Down
4 changes: 2 additions & 2 deletions packages/dox-core/lib/http/http_route_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ RouteData? httpRouteHandler(HttpRequest req) {
if (route == null) {
/// return 200 status on preflight OPTIONS Method
if (req.method == HttpRequestMethod.OPTIONS.name) {
httpResponseHandler(null, req);
responseDataHandler(null, req);
} else {
req.response.statusCode = HttpStatus.notFound;
httpResponseHandler('${req.method} ${req.uri.path} not found', req);
responseDataHandler('${req.method} ${req.uri.path} not found', req);
}
}
return route;
Expand Down
32 changes: 27 additions & 5 deletions packages/dox-core/lib/http/request/dox_request.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'dart:convert';
import 'dart:io';

import 'package:dox_core/dox_core.dart';
Expand All @@ -8,13 +9,20 @@ import 'package:dox_core/validation/dox_validator.dart';

class DoxRequest implements IDoxRequest {
final RouteData route;
final Uri uri;
final ContentType? contentType;

final HttpHeaders httpHeaders;

@override
final ContentType? contentType;

@override
final HttpRequest httpRequest;

@override
late Uri uri;

final String? clientIp;

@override
String method = 'GET';

Expand All @@ -41,6 +49,19 @@ class DoxRequest implements IDoxRequest {
_getCookies();
}

/// get route identifier
@override
String getRouteIdentifier() {
return base64.encode(
utf8.encode('${route.method}|${route.domain ?? ''}${route.path}'));
}

/// get route identifier
@override
RouteData getRouteData() {
return route;
}

/// http request data is form data
@override
bool isFormData() {
Expand Down Expand Up @@ -232,9 +253,9 @@ class DoxRequest implements IDoxRequest {
}
}

/// map request input keys
/// map the request input keys
@override
void mapInputs(Map<String, String> mapper) {
void processInputMapper(Map<String, String> mapper) {
mapper.forEach((String from, String to) {
if (from != to) {
dynamic temp = _allRequest[from];
Expand All @@ -244,7 +265,8 @@ class DoxRequest implements IDoxRequest {
});
}

/// support JSON.stringify to convert json
/// To support jsonEncode
@override
Map<String, dynamic> toJson() {
Map<String, dynamic> ret = <String, dynamic>{};
_allRequest.forEach((String key, dynamic value) {
Expand Down
6 changes: 5 additions & 1 deletion packages/dox-core/lib/http/request/http_request_body.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ class HttpBody {
static Future<Map<String, dynamic>> read(HttpRequest request) async {
if (HttpBody.isJson(request.headers.contentType)) {
String bodyString = await utf8.decoder.bind(request).join();
return jsonDecode(bodyString);
try {
return jsonDecode(bodyString);
} catch (err) {
return <String, dynamic>{};
}
}

if (HttpBody.isFormData(request.headers.contentType)) {
Expand Down
42 changes: 31 additions & 11 deletions packages/dox-core/lib/http/response/dox_response.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'package:dox_core/http/http_response_handler.dart';
class DoxResponse {
/// content can be anything
/// String, int, Map, json serializable object, Stream<List<int>>
dynamic content;
dynamic _contentData;

Map<String, dynamic> _headers = <String, dynamic>{};
int? _statusCode;
Expand All @@ -16,16 +16,15 @@ class DoxResponse {

/// content can be anything
/// String, int, Map, json serializable object, Stream<List<int>>
DoxResponse(this.content) {
if (content is StreamFile) {
_contentType = content.contentType;
content = content.stream;
DoxResponse(this._contentData) {
if (_contentData is StreamFile) {
_contentType = _contentData.contentType;
}

if (content is DownloadableFile) {
header(FILE_DOWNLOAD_HEADER, content.contentDisposition);
_contentType = content.contentType;
content = content.stream;
if (_contentData is DownloadableFile) {
header(FILE_DOWNLOAD_HEADER, _contentData.contentDisposition);
_contentType = _contentData.contentType;
_contentData = _contentData.stream;
}
}

Expand All @@ -38,8 +37,29 @@ class DoxResponse {
return this;
}

/// Set response status code default 200
/// ```
/// res.setContent({"foo": "bar"});
/// ```
DoxResponse content(dynamic content) {
_contentData = content;
return this;
}

/// Get original content data
/// ```
/// res.getContent();
/// ```
dynamic getContent() {
return _contentData;
}

/// set stream data to response
/// ```
/// res.stream(streamData);
/// ```
DoxResponse stream(Stream<List<int>> stream) {
content = stream;
content(stream);
return this;
}

Expand Down Expand Up @@ -103,7 +123,7 @@ class DoxResponse {
for (String cookie in _cookies) {
request.response.headers.add(HttpHeaders.setCookieHeader, cookie);
}
return httpResponseHandler(content, request);
return responseDataHandler(_contentData, request);
}
}

Expand Down
Loading

0 comments on commit 487ec36

Please sign in to comment.