Server response with output from Future Object - dart

i created a async/await function in another file thus its handler is returning a Future Object. Now i can't understand how to give response to client with content of that Future Object in Dart. I am using basic dart server with shelf package.Below is code where ht.handler('list') returns a Future Object and i want to send that string to client as response. But i am getting internal server error.
import 'dart:io';
import 'package:args/args.dart';
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;
import 'HallTicket.dart' as ht;
// For Google Cloud Run, set _hostname to '0.0.0.0'.
const _hostname = 'localhost';
main(List<String> args) async {
var parser = ArgParser()..addOption('port', abbr: 'p');
var result = parser.parse(args);
// For Google Cloud Run, we respect the PORT environment variable
var portStr = result['port'] ?? Platform.environment['PORT'] ?? '8080';
var port = int.tryParse(portStr);
if (port == null) {
stdout.writeln('Could not parse port value "$portStr" into a number.');
// 64: command line usage error
exitCode = 64;
return;
}
var handler = const shelf.Pipeline()
.addMiddleware(shelf.logRequests())
.addHandler(_echoRequest);
var server = await io.serve(handler, _hostname, port);
print('Serving at http://${server.address.host}:${server.port}');
}
Future<shelf.Response> _echoRequest(shelf.Request request)async{
shelf.Response.ok('Request for "${request.url}"\n'+await ht.handler('list'));
}

The analyzer gives your the following warning for your _echoRequest method:
info: This function has a return type of 'Future', but
doesn't end with a return statement.
And if you check the requirement for addHandler you will see it expects a handler to be returned.
So you need to add the return which makes it work on my machine:
Future<shelf.Response> _echoRequest(shelf.Request request) async {
return shelf.Response.ok(
'Request for "${request.url}"\n' + await ht.handler('list2'),
headers: {'Content-Type': 'text/html'});
}

Related

Dart server side: How to receive data from the Postman (form-data)?

I am using dart:io to create the server. I send the request from the Postman with form-data. I need to use form-data because my old API from another language uses it and the app uses it too.
At the moment. I am trying to get the data and files with this code:
Future main(List<String> arguments) async {
HttpServer server = await HttpServer.bind('localhost', 8085);
server.listen((HttpRequest request) async {
String jsonString = await request.cast<List<int>>().transform(utf8.decoder).join();
print("jsonString:\n$jsonString");
await request.response.close();
});
}
When I send the data and a file from the Postman with this below.
I will get the error below.
Unhandled exception:
FormatException: Unexpected extension byte (at offset 435)
If I don't send the file as image 1, I got this.
jsonString:
----------------------------166099235909119466948633
Content-Disposition: form-data; name="key 1"
Content-Type: application/json
value 1
----------------------------166099235909119466948633
Content-Disposition: form-data; name="key 2"
value 2
----------------------------166099235909119466948633--
I can't convert the above results to variables.
I don't know how to do that. Has anyone an example for doing this or suggest any package to me? This is my first time creating a dart server.
I follow this.
You can get the data and files from the request by using shelf_multipart (Other packages may be used in conjunction with this one and find more methods on GitHub).
If you want to see results quickly that it can be done. Follow this below.
I am using 3 packages including the shelf, shelf_router, and shelf_multipart packages.
You need to add these packages to your pubspec.yaml.
(You can copy and paste these into your pubspec.yaml.)
dependencies:
shelf: ^1.4.0
shelf_router: ^1.1.3
shelf_multipart: ^1.0.0
Then copy my code and past it to your main.dart:
import 'dart:convert';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_router/shelf_router.dart';
import 'package:shelf_multipart/form_data.dart';
import 'package:shelf_multipart/multipart.dart';
Future main(List<String> arguments) async {
final service = Service();
final server = await shelf_io.serve(service.handler, 'localhost', 8085);
print('Server running on localhost:${server.port}');
}
class Service {
Handler get handler {
final router = Router();
router.post("/example", (Request request) async {
if (request.isMultipart && request.isMultipartForm) {
Map<String, dynamic>? data = await RequestConverter.formData(request);
return data != null
? Response.ok("form-data: true, receive-data: true, data: $data")
: Response.ok("form-data: true, receive-data: false");
}
return Response.ok("form-data: false");
});
router.all('/<ignored|.*>', (Request request) {
return Response.notFound('Page not found');
});
return router;
}
}
class RequestConverter {
static Future<Map<String, dynamic>?> formData(Request request) async {
try {
Map<String, dynamic> data = {};
Map<String, dynamic> files = {};
final List<FormData> formDataList = await request.multipartFormData.toList();
for (FormData formData in formDataList) {
if (formData.filename == null) {
String dataString = await formData.part.readString();
data[formData.name] = Json.tryDecode(dataString) ?? dataString; //Postman doesn't send data as json
} else if (formData.filename is String) {
files[formData.name] = await formData.part.readBytes();
}
}
return {"data": data, "files": files};
} catch (e) {
return null;
}
}
}
class Json {
static String? tryEncode(data) {
try {
return jsonEncode(data);
} catch (e) {
return null;
}
}
static dynamic tryDecode(data) {
try {
return jsonDecode(data);
} catch (e) {
return null;
}
}
}
After this, you can start your server in your terminal. For me I am using:
dart run .\bin\main.dart
Finally, open the Postman and paste http://localhost:8085/example to the URL field, select the POST method, and form-data. You can add the data into the KEY and VALUE fields. Then press send.
This is my example in the Postman:
This solution work with http.MultipartRequest() from the Flutter app.

How to Implement Flutter Web SSL Certificate (SSL Pinning)

I am building a flutter web app and I need to use SSL to talk to the server using a .pem certificate.
I am using HttpClient and IOClient to get it to work and the code for this looks as following:
fetchData()async{
HttpClient _client = HttpClient(context: await globalContext);
_client.badCertificateCallback =
(X509Certificate cert, String host, int port) => false;
IOClient _ioClient = new IOClient(_client);
var response = await _ioClient.get(Uri.parse('https://appapi2.test.bankid.com/rp/v5.1'));
print(response.body);
}
Future<SecurityContext> get globalContext async {
final sslCert1 = await
rootBundle.load('assets/certificates/bankid/cert.pem');
SecurityContext sc = new SecurityContext(withTrustedRoots: false);
sc.setTrustedCertificatesBytes(sslCert1.buffer.asInt8List());
return sc;
}
I get the following error when trying to run fetchData:
Unsupported operation: SecurityContext constructor
I have also tried using the flutter plugin DIO that looks like this:
void bid() async {
final dio = Dio();
ByteData bytes = await rootBundle
.load('assets/certificates/bankid/FPTestcert4_20220818.pem');
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(client) {
SecurityContext sc = SecurityContext();
sc.setTrustedCertificatesBytes(bytes.buffer.asUint8List());
HttpClient httpClient = HttpClient(context: sc);
return httpClient;
};
try {
var response = await dio.get('https://appapi2.test.bankid.com/rp/v5.1');
print(response.data);
} catch (error) {
if (error is DioError) {
print(error.toString());
} else {
print('Unexpected Error');
}
}
}
When running this I get the following error:
Error: Expected a value of type 'DefaultHttpClientAdapter', but got one of type
'BrowserHttpClientAdapter'
I understand that I get the error above because of the casting that the httpClientAdapter is used as a DefaultHttpClientAdapter but since the app is running in the browser its using BrowserHttpClientAdapter, but how do I solve this?
Is it possible to make this work?

How to set up tests for a Dart HTTP server

I'm trying to build a Dart HTTP server and I want to test the API. I'm not able to set up the tests, though.
Here is what I have so far in my_server_test.dart:
import 'dart:io';
import 'package:my_server/my_server.dart';
import 'package:test/test.dart';
void main() {
HttpServer server;
setUp(() async {
final server = await createServer();
await handleRequests(server);
});
tearDown(() async {
await server.close(force: true);
server = null;
});
test('First try', () async {
final client = HttpClient();
final request = await client.get(InternetAddress.loopbackIPv4.host, 4040, '/');
final response = await request.close();
print(response);
});
}
And here is the server code in my_server.dart:
import 'dart:io';
import 'package:hundetgel_server/routes/handle_get.dart';
Future<HttpServer> createServer() async {
final address = InternetAddress.loopbackIPv4;
const port = 4040;
return await HttpServer.bind(address, port);
}
Future<void> handleRequests(HttpServer server) async {
await for (HttpRequest request in server) {
switch (request.method) {
case 'GET':
handleGet(request);
break;
default:
handleDefault(request);
}
}
}
void handleGet(HttpRequest request) {
request.response
..write('Hello')
..close();
}
void handleDefault(HttpRequest request) {
request.response
..statusCode = HttpStatus.methodNotAllowed
..write('Unsupported request: ${request.method}.')
..close();
}
When I run the test I just get a timeout:
TimeoutException after 0:00:30.000000: Test timed out after 30 seconds. See https://pub.dev/packages/test#timeouts
dart:isolate _RawReceivePortImpl._handleMessage
NoSuchMethodError: The method 'close' was called on null.
Receiver: null
Tried calling: close(force: true)
dart:core Object.noSuchMethod
2
main.<fn>
test/my_server_test.dart:15
===== asynchronous gap ===========================
dart:async _completeOnAsyncError
test/my_server_test.dart main.<fn>
test/my_server_test.dart:1
main.<fn>
test/my_server_test.dart:14
2
✖ First try
Exited (1)
How do I set up the server so I can start testing it?
It seems that you are await handleRequests in your setUp. Your handleRequests is a forever loop waiting for incoming requests. So it never halts. So you setup never finishes. That is the problem.
Thus, try to change
await handleRequests(server);
to
handleRequests(server); // NO await
Thus the handleRequests will run in the "background".
EDIT about the null exception of server variable:
change
final server = await createServer();
to
server = await createServer();
because the old code shadows the outside server variable - that variable is never assigned a value.

Reusable Sync HttpClient Request

I need a reusable function that makes an HTTP request and awaits for its completion before returning the response as a String.
Here's the main function:
main() async {
var json;
json = await makeRequest('https://...');
print(json);
print('*** request complete ***');
}
(First Case) This is the reusable function that makes the HTTP request:
makeRequest(String url) async {
var request = await new HttpClient().postUrl(Uri.parse(url));
// Includes the access token in the request headers.
request.headers.add(...);
// Waits until the request is complete.
var response = await request.close();
await for (var contents in response.transform(UTF8.decoder)) {
return contents;
}
}
This works as expected and the output is:
// Response contents as a String...
*** request complete ***
(Second Case) Then I tried to do this and it didn't work:
makeRequest(String url) async {
var request = await new HttpClient().postUrl(Uri.parse(url));
// Includes the access token in the request headers.
request.headers.add(...);
// Waits until the request is complete.
var response = await request.close();
var json = '';
await response.transform(UTF8.decoder).listen((contents) {
// At first I tried to return contents here, but then I added onDone().
json += contents;
}, onDone: () {
return json;
});
return json;
}
I've tried defining the function within listen with async and await, returning contents within listen without onDone(), but the output is the same:
// Empty line.
*** request complete ***
// Waits a few seconds doing nothing before terminating...
Does anyone know why the second case doesn't work?
EDIT:
After updating the code it does what it was supposed to do, but takes a few seconds before terminating execution:
Future<String> twitterRequest(String url) async {
var request = await new HttpClient().postUrl(Uri.parse(url));
// Includes the access token in the request headers.
request.headers.add(...);
// Waits until the request is complete.
var response = await request.close();
var json = '';
await for (var contents in response.transform(UTF8.decoder)) {
json += contents;
// Putting a break here produces the same output but terminates immediately (as wanted).
}
return json;
}
Output:
// Prints response contents...
*** request complete ***
// Takes a few seconds before execution terminates. With the break the code terminates immediately.
EDIT2:
After submitting this issue on GitHub, I found out that instances of the HttpClient have a connection pool and keep persistent connections by default, which keeps the Dart VM alive. Please consult the issue page to find out about the possible solutions.
It's probably caused by the await before the response.transform.
You might want something like
return response.transform(UTF8.decoder).join('');
The pause is not related to makeRequest(). The Dart VM seems to wait for something before it exits. Adding exit(0); as last line in main() makes the application exit immediately.
Update
According to the response on the Dart SDK issue
This is caused by the HttpClient instance having a connection pool
which can keep the Dart VM alive. There are two ways of avoiding this:
1) Close the HttpClient explicitly
2) Use non-persistent connections
import 'dart:async';
import 'dart:convert' show UTF8;
import 'dart:io';
Future main() async {
await makeRequest();
print('end of main');
}
Future makeRequest() async {
var client = new HttpClient();
var request = await client.postUrl(Uri.parse('https://example.com'));
var response = await request.close();
var contents = await response.transform(UTF8.decoder).join();
print(contents);
client.close(); // Close the client.
}
import 'dart:async';
import 'dart:convert' show UTF8;
import 'dart:io';
Future main() async {
await makeRequest();
print('end of main');
}
Future makeRequest() async {
var request = await new HttpClient().postUrl(Uri.parse('https://example.com'));
request.persistentConnection = false; // Use non-persistent connection.
var response = await request.close();
var contents = await response.transform(UTF8.decoder).join();
print(contents);
}

Dart Language: receive a file from a POST and print its contents on the server

I would like to know how can a server side application receive a file (via POST) and then print its contents on the server side.
The most "up to date" related question here was this one: Dart how to upload image
But it is not working anymore (Uncaught Error: type 'String' is not a subtype of type 'HttpBodyFileUpload' of 'fileUploaded').
EDIT:
This is how I send the file (this method is working fine):
import 'dart:html';
import 'dart:async';
HttpRequest request = new HttpRequest();
final _HOST = "127.0.0.1", _PORT = 8123;
Future sendFile(File file) {
var completer = new Completer(); // No need for a Completer. It will be removed.
Uri uri = new Uri(host: _HOST, port: _PORT);
request.open("POST", uri.toString());
var filename = file.name;
final FormData formData = new FormData();
formData.append('file', filename);
request.onLoadEnd.listen((_) {
completer.complete(request.response);
});
request.send(formData);
return completer.future;
}
The server side (I'm stuck here):
void _handlePost(HttpRequest req) {
HttpBodyHandler.processRequest(req).then((body) {
HttpBodyFileUpload fileUploaded = body.body['file'];
print(fileUploaded.content);
});
}
You are appending the filename instead of the Blob (File) to your FormData object. In Dart it looks like there is a special function for appending blobs called appendBlob(name, blob, [filename]).

Resources