First time with Flutter. I'm using dio to send HTTP requests, and I have to add a header to all requests, which I do with an interceptor, like this:
Dio dio = new Dio();
dio.interceptors.add(InterceptorsWrapper(
onRequest:(RequestOptions options) async {
options.headers["X-Requested-With"] = "XMLHttpRequest";
})
);
It works in main.dart, but if I want to import another class like MyHomePage.dart and do HTTP requests there, I'd have to redefine the interceptor in that class too.
How can I implement this interceptor for my whole application without adding it in every .dart file?
Create a function that houses the DIO and then call it where needed
Dio getDio() {
Dio dio = new Dio();
dio.interceptors.add(InterceptorsWrapper(onRequest: (RequestOptions options) async {
options.headers["X-Requested-With"] = "XMLHttpRequest";
}));
return dio;
}
This worked good for me, without interceptors, just create a class and use it in your app.
import 'package:dio/dio.dart';
import '../helpers/api_url.dart';
class dioClient {
Dio dio = Dio();
static Dio simpleDio() {
return Dio(BaseOptions(
baseUrl: apiUrl(),
headers: {'Content-Type': 'application/json; charset=UTF-8'}));
}
static Dio dioWithCookie(String cookie) {
return Dio(BaseOptions(baseUrl: apiUrl(), headers: {
'Content-Type': 'application/json; charset=UTF-8',
'Cookie': cookie
}));
}
}
Related
I'm trying to make a request to my server with a Flutter Web Application using Dio package. It's returning the XMLHttpRequest error but the server is receveing the request normally.
I'm aware that with disable security command the problem vanish, but I need a solution for production as weel.
Dio code:
import 'package:dio/dio.dart';
import '../interceptor/api_interceptor.dart';
class DioApi {
static String backendUrl = "http://localhost:8080/api/";
static Dio _getDio({Map<String, dynamic>? headers}) {
BaseOptions baseOptions = BaseOptions(
baseUrl: backendUrl,
receiveDataWhenStatusError: true,
connectTimeout: 60 * 1000,
receiveTimeout: 60 * 1000,
);
if (headers != null) {
baseOptions.headers.addAll(headers);
}
Dio dio = Dio(baseOptions);
dio.interceptors.add(ApiInterceptor());
return dio;
}
static Future<Response> get(
String url,
{Map<String, dynamic>? headers, Map<String, dynamic>? queryParameters}) async {
return _getDio(headers: headers).get(url, queryParameters: queryParameters);
}
static Future<Response> post(
String url,
dynamic data,
{Map<String, dynamic>? headers, Map<String, dynamic>? queryParameters}) async {
return _getDio(headers: headers).post(url, data: data, queryParameters: queryParameters);
}
}
--disable-web-security can't help me and CORS on server side seems fine.
Where we can set timeout in below code?
As you can see I am using retrofit for api call.
Dio Object
class DioObject{
static Dio getDio(){
debugPrint("Bearer:- ${PrefHelper().pref?.getString(PrefHelper.AUTHORIZATION)}");
final dio = Dio(); // Provide a dio instance
dio.options.headers["Authorization"] =
"Bearer ${PrefHelper().pref?.getString(PrefHelper.AUTHORIZATION)}"; // config your dio headers globally
dio.options.headers["Content-Type"] =
"application/json;charset=UTF-8"; // config your dio headers globally
return dio;
}
}
Api call
final client = RestClient(DioObject.getDio());
var response = await client.xyz();
Rest API
#RestApi(baseUrl: "*****/api")
abstract class RestClient {
factory RestClient(Dio dio, {String baseUrl}) = _RestClient;
#GET("/UserAccessPoints/")
Future<CommonResponse> xyz();
}
You can do it the same way you added your custom headers, or doing it all at once with a BaseOptions object
final dio = Dio();
//Dio Options
dio.options = BaseOptions(
contentType: 'application/json',
connectTimeout: 4000,
sendTimeout: 4000,
receiveTimeout: 10000,
headers : ...
);
The open api request I'm trying to use requires an image binary value with content-type of multipart/form-data format.
I know you can't use dart:io in flutter web. I tried to upload an image in multipart/form-data format to the api server in flutter web while looking at several posts.
However, only a message appeared stating that the image could not be recognized.
This is the last thing I tried to create multipart types in flutter web.
import 'package:dio/dio.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:file_picker/file_picker.dart';
PlatformFile? objFile;
pickImage() async {
var result = await FilePicker.platform.pickFiles(
withReadStream: true,
);
setState(() {
objFile = result!.files.single;
});
uploadImage();
}
uploadImage() async {
FormData formData = FormData.fromMap({'image' : MultipartFile(test!, objFile!.size, filename: objFile!.name)});
Dio dio = new Dio();
var response = await dio.post('API url', data: formData);
}
I additionally used Multipart.form Bytes from http , Multipart.form Bytes from dio . But the result was the same.
The value checked by the request body through the postman interceptor.
content-type=multipart/form-data;bounary=--dio-boundary-1105759322
----dio-boundary-1105759322
content-disposition: form-data; name="image"; filename="test.jpeg"
content-type: application/octet-stream
ÿØÿÛC
%# , #&')*)-0-(0%()(ÿÛC
(((((((((((((((((((((((((((((((((((((((((((((((((((ÿÀŽv"ÿÄÿÄC!1AQaq"‘2¡±#BÁÑR3CðñSbr’á‚Â$&4c“ÿÄÿÄ&!1A2Q"a3BRÿÚ?ù× „É<$/cŸt8D`aú¦Ä#bálŒZVM„ٔʓTL›eOò¢“
èKÇ(p¢‰¥C’ÄÙ‚Ñx²Ù1Jcœ)B›¢$ ¢‚&
‚7› ˜Žp”{&ÊÀÁAî¤Æ
‚nÈ CØÃêOýÒ›§á$sÊ‚r¡ìLÂ…;"éMI½î«gæV<æ6οÙ%_ƒY®}7Òû€¯MŒ&g¹å|µ£ëÐúc\tÚƵƈúÕ]#kQ‹D/Ÿú·cu9«Hà/¢lÚ–êè·¼&Þt
¯H‚&ɶìÛà®iƒh²SöãÔTs[l›/?[s(’˜¨o€¤Û‹*¥AÖ”ðbUgYR’!äJ!M‹™‹«›î©aÉ*ᕨ4p SÉ…¤)‰ì§=‘âJ» oÙGDRåÌy0—²û r ò€·²?Te8±KSTR8ŹDAååþ7)Oˆk)õ²Qk#Ù€Œ ?DÜû&Ä›„ÍÅ”lQjð¡NÑ%HTWP˜²wýÒc(Ÿð¤ð¢S<*6º>ÊaCœ „Ù0
^J(ª%¢ƒFPm‘^u4^èM‘åL…##•0Qÿ ºi…32§ÙC•D¿&Èw’ˆº‘Ü"…”<&ýРwP {p ¸DCd¼&ÿ©#¨ˆ› La~¨p¦„)’÷‚ˆº²æÒ›ªĘ̀Šaá€0‹n <ò¦M“YM„ L«=ÕnæÊlªŽÂƒóc„m‚—È™Uó ªºäªÛ•F†\…}7?¨ªZL`*£è¾ŽÝÌ1¤ÜBúk6
---------------------------SKIP------------------------------
PTiMÂ!¢(èÊ€YÊÂœ"ÑÂ_T<Ñ5îPp™ð ¨„ôOˤ?¢z\ÂÚ¡½ÐiÊc쨟ÝHŸ¢“3ÝA˜( ‘ÊH›(l€Å¼)Ä‘rEÈ[€‹¬”¼x
W7q?ΣHt®“§¤y\½Ìÿ:ÿÍtÖ§T°AÊÕ\ËZVƒÔPha30%1*¶›Ž!7è¥|f›„îÕQ±„9N6åW,¨^Ù8PHN./Ê€îª2ß*{(l¡™šOU¢Ôå3œ*ꜨŠ‹“3¼$«B*ÌŒS„+EÒ‘Ý VHpV±`²³ó€µgܪ‚#“Ü)À!NPCƒÝIÅԛ–”xý”²™# ?U‚‹n€å!Œ¦&é*ƒ™¨wÄÖØY¢>«}&ü¢×\Ý?ó*9ç%Òº˜#çò H€¥&ꃒ¤(
‚0O8##EÎéÊœ#TÕr‚ºT¹ÈÔ7T“2¢ƒœbÅsuOî¶Ô0>‹ŸT|Gô•Óa®ïšÔÇe¤T
he<,¨[ü¶[…·M#ZOˆjtˤÝE© QÿÙ
----dio-boundary-1105759322--
When I use the MultipartFile.fromFile method used in flutter ios, I got the response normally. So I'm pretty sure there must be some mistake or misinformation in the flutter web setup.
Thanks in advance!
this is how I managed to upload an image to Laravel backend using Flutter Web
import 'dart:io';
import 'package:dio/dio.dart' as dio;
import 'package:file_picker/file_picker.dart';
Future pickupUserImage2() async {
PlatformFile? objFile;
var picked = await FilePicker.platform.pickFiles();
objFile = picked!.files.single;
String? apiUrl = Get.find<MainController>().apiUrl;
String url = '';
if (apiUrl != null) url = apiUrl + 'images';
List<int> list;
list = List<int>.from(objFile.bytes!);
dio.FormData formData = dio.FormData.fromMap({'file': dio.MultipartFile.fromBytes(list, filename: objFile.name)});
DioNetworking _dio = new DioNetworking();
dynamic result = await _dio.postData(
url,
formData,
contentType: 'multipart/form-data',
);
if (result['status'] == true) {
showToast(result['message']);
return ImageModel.Image.fromJson(result['data']);
}
}
I added a file using MultipartFile.fromBytes which has the first parameter as List which I created from this method "List.from(objFile.bytes!);"
Notes :
ImageModel.Image is a model I created to handle the image result
DioNetworking is a class that perform dio requests , I just created it to do the
authentication stuff
import 'package:dio/dio.dart';
class DioNetworking {
Dio _dio = new Dio();
Future postData(
String url,
dynamic data, {
String? contentType,
}) async {
try {
Response response = await _dio.post(url,
data: data,
options: Options(headers: {
'content-type': contentType != null ? contentType : 'application/json',
'Accept': 'application/json',
// 'Authorization': 'Bearer ${token ?? ''}'
// other headers
}));
if (response.statusCode == 200) {
dynamic data = response.data;
return data;
} else {
print(response.statusCode);
}
} on DioError catch (e) {
print(e);
if (e.type == DioErrorType.connectTimeout) {
return {'status': 'Connect Timed Out'};
}
if (e.type == DioErrorType.receiveTimeout) {
return {'status': 'Receive Timed Out'};
}
if (e.type == DioErrorType.response) {
print(e.response!.data);
print(e.response!.headers);
}
}
}
}
I tried using http package of flutter and create a custom client with headers.
Code
class ApiClient extends http.BaseClient {
final http.Client _inner;
ApiClient(this._inner);
_setHeaders() => {
'Content-type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer token here...'
};
Future<http.StreamedResponse> send(http.BaseRequest request) {
request.headers.addAll(_setHeaders());
return _inner.send(request);
}
}
How can I add a base URL to my custom client?
Since ApiClient inherits http.BaseClient, you should be able to have access to other methods as well. Simply access the method on your ApiClient for example.
var baseUrl = Uri.parse('https://example.com/');
var response = await ApiClient.post(baseUrl);
I use a similar approach on my projects:
class ApiClient extends http.BaseClient {
final http.Client _inner;
final String baseUrl;
ApiClient(this._inner, this.baseUrl);
Uri url(String path, [Map<String, String?>? queryParameters]) {
return Uri.parse('$baseUrl$path').replace(queryParameters: queryParameters);
}
// other methods ...
}
Usage sample:
final api = ApiClient(inner, 'https://testhost/api/v1');
final response = await api.post(api.url('/test', {'q': 'a'}));
Postman form-data request
Request body containing
Expected Response body
Dio Flutter code
import 'package:dio/dio.dart';
import 'package:flutter_project/config/config.dart';
import '../model/postImage.dart';
import 'dart:io';
Future<PostImage> postImage(File image) async{
String fileName = image.path.split('/').last;
print(fileName);
try{
Dio dio = new Dio();
FormData formData = FormData.fromMap({
"file": await MultipartFile.fromFile(image.path,filename: fileName)
});
Map<String, String> headers= <String,String>{
'Content-Type':'multipart/form-data'
};
print("${baseURL}files/upload");
Response response = await dio.post("${baseURL}files/upload",data: formData);
if(response.statusCode == 200){
print("Uploaded");
}
else{
print(response.data);
}
}
catch(e){
print(e);
}
}
Expection that I am getting : DioError [DioErrorType.RESPONSE]: Http status error [404]
I added contentType: new MediaType("image", "jpeg")//in the formData. It works now.
Full Code :
import 'package:dio/dio.dart';
import 'package:flutter_project/config/config.dart';
import '../model/postImage.dart';
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';
Future<PostImage> postImage(File image) async {
String fileName = image.path.split('/').last;
print(fileName);
try {
Dio dio = new Dio();
FormData formData = FormData.fromMap({
"file": await MultipartFile.fromFile(
image.path,
filename: fileName,
contentType: new MediaType("image", "jpeg"),
)
});
Map<String, String> headers = <String, String>{
'Content-Type': 'multipart/form-data'
};
print("${baseURL}files/upload");
Response response =
await dio.post("${baseURL}files/upload", data: formData);
if (response.statusCode == 200) {
print("Uploaded");
} else {
print(response.data);
}
print('Out');
} catch (e) {
print(e);
}
}
When I encountered an HTTP 4xx error while using Dio and I was sure I had my Authorization set, what I did to make it work is to set the Options() parameter explicitly. Precisely, I set the method type (for a get request) and authorisation token like this: Dio().get(url, options: Options(method: "GET", headers: { "Authorization": "Bearer ${LocalStorage().getString(Keys.token)}", })));.
You can also set content-type if need be. To make it easier, you can simply copy-paste into the headers: argument, some of the header values set in/by your postman when you make the same request using postman.