Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restructure response handler #82

Merged
merged 1 commit into from
Dec 16, 2023
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
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
Loading