Study Record

[Flutter] 서버와 통신하기 (dio v5.1.1) 본문

Flutter/라이브러리

[Flutter] 서버와 통신하기 (dio v5.1.1)

초코초코초코 2023. 4. 14. 22:19
728x90

🎁 dio

Dart 와 Flutter 에서 사용할 수 있는 HTTP 통신을 지원하는 라이브러리로 사용방법이 간단하다. 인터셉트, 파일 업로드/다운로드, 요청 시간 설정 등 여러가지 기능을 지원한다.

 

 

dio | Dart Package

A powerful HTTP package for Dart/Flutter, which supports Global settings, Interceptors, FormData, Aborting and canceling a request, Files uploading and downloading, Requests timeout, Custom adapters, etc.

pub.dev

 

 

😶 초기 설정

터미널에서 "dart pub add dio" 를 실행하면 자동으로 dio 라이브러리를 추가해 준다.

dart pub add dio

 

혹은 직접 pubspec.yaml 파일에 라이브러리를 추가해 준 뒤, pub get 버튼을 눌러 프로젝트에 반영한다.

dependencies:
  ...
  dio: ^5.1.1

 

 

🎁 HTTP 요청

 

😶 get 요청

dio.get() 함수로 쉽게 요청할 수 있다. url 에 직접 파라미터를 넣을 수 있고 queryParameters 인자로 Map<String, dynamic> 형식으로 넣을 수도 있다.

import 'package:dio/dio.dart';

void request() async {
  final dio = Dio();
  
  Response response = await dio.get(
    'https://search.naver.com/search.naver?where=nexearch&sm=top_hty&fbm=1&ie=utf8&query=%EC%82%AC%EB%9E%91',
  );
  
  print(response.data.toString());
  
  Response response2 = await dio.get(
    'https://search.naver.com/search.naver',
    queryParameters: {
      'where': 'nexearch',
      'sm': 'top_hty',
      'fbm': 1,
      'ie': "utf8",
      'query': '%EC%82%AC%EB%9E%91',
    },
    // header 옵션 넣기
    options: Options(
      headers: {
        "headerOption": "23123",
      }
    ),
  );  
  
  print(response2.data.toString());
}

 

 

😶 post 요청

dio.post() 함수로 post 요청을 할 수 있다. data 인자로 파라미터를 넣을 수 있다. 직접 Map 형태로 넣거나 FormData.fromMap() 을 사용할 수 있다.

import 'package:dio/dio.dart';

void request() async {
  final dio = Dio();
  
  final formData = FormData.fromMap({
    'id': 12,
    'name': 'dio',
  });
  
  Response response = await dio.post('http://www.example.com/test', data: formData);
  
  Response response2 = await dio.post(
    'http://www.example.com/test', 
    data: {'id': 12, 'name': 'dio'},
    // header 옵션 넣기
    options: Options(
      headers: {
        "headerOption": "23123",
      },
    ),  
  );
  
  print(response.data.toString());
  print(response2.data.toString());
}

 

 

😶 옵션 설정하기

dio 옵션으로 baseurl, 연결 시간, 수신 시간을 설정할 수 있다. baseUrl 은 dio 로 요청할 때 앞에 기본으로 붙는 url 로 dio.get("/test") 라면 baseUrl + "/test" 로 http 요청을 시도한다.

import "package:dio/dio.dart"

void configureDio() async{
  final dio = Dio();
  // Set default configs
  dio.options.baseUrl = 'https://www.naver.com';
  dio.options.connectTimeout = Duration(seconds: 5);
  dio.options.receiveTimeout = Duration(seconds: 3);

  // Or create `Dio` with a `BaseOptions` instance.
  final options = BaseOptions(
    baseUrl: 'https://api.pub.dev',
    connectTimeout: Duration(seconds: 5),
    receiveTimeout: Duration(seconds: 3),
  );
  final anotherDio = Dio(options);
  
  // http://www.naver.com/test
  Response resp = await dio.get("/test");
  
  // http://api.pub.dev/test
  Response resp2 = await anotherDio.get("/test");
}

 

 

😶  Request 요청하기

dio.request() 를 사용하여 그때그때 적합한 요청을 만들 수 있다.  

final dio = Dio();

dio.options.baseUrl = "https://search.naver.com";

final response = await dio.request(
  '/test',
  queryParameters: {
    "id": 2,
    "name": "dsasd",
  },
  options: Options(method: 'GET'),
);

final response2 = await dio.request(
  '/test',
  data: {
    "id": 2,
    "name": "dsasd",
  },
  options: Options(
    method: 'POST',
    headers: {
      "headerOption": "rewer",
    },
  ),
);

 

 

 

😶  Interceptors 적용하기 + 요청 Log  남기기

interceptors 를 적용하여 요청을 보낼때(onRequest), 응답을 받을 때(onResponse), 에러가 났을 때(onError)를 캐치하여 여러 가지 작업을 할 수 있다. dio 가 http 요청을 보낼 때 onRequest() 에서 super.onRequest(options, handler) 를 실행할 때 실제로 요청이 보내진다.  그 이전에 options 를 수정하면 수정된 값으로 요청할 수 있다. 

import "package:dio/dio.dart";

final dio = Dio();

// interceptors 추가하기
dio.interceptors.add(CustomInterceptors());

// interceptor 를 상속한 클래스 만들기
class CustomInterceptors extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    print('REQUEST[${options.method}] => PATH: ${options.baseUrl}${options.path}${options.queryParameters}');
    
    // 요청 전 header 를 추가한다.
    options.headers.addAll({
        'authorization' : 'Bearer token',
    });
    
    // 실제로 http 요청이 보내진다.
    super.onRequest(options, handler);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    print('RESPONSE[${response.statusCode}] => PATH: ${response.requestOptions.path}');
    super.onResponse(response, handler);
  }

  @override
  void onError(DioError err, ErrorInterceptorHandler handler) {
    print('ERROR[${err.response?.statusCode}] => PATH: ${err.requestOptions.path}');
    
    // 에러가 난 요청의 response
    final errResponse = err.response;
    
    // 에러가 난 요청의 requestOptions
    final errRequestOption = err.requestOptions;
    
    super.onError(err, handler);
  }
}

 

+ reject(), resolve()

 

Interceptor 를 상속한 클래스에서 onRequest, onResponse, onError 의 handler 에는 reject(DioError), resolve(Response) 함수가 있는데 reject() 는 실제 요청의 결과가 어떻든 에러라는 결과를 반환하고 resolve() 는 실제 요청이 실패해도 성공한 것으로 반환한다. 따라서 onError() 에서 handler.solve(Response resp) 를 실행하면 요청이 성공한 것으로 간주되고 데이터는 resp 를 반환한다.

// example
class CustomInterceptors extends Interceptor {

  @override
  void onError(DioError err, ErrorInterceptorHandler handler) {
    print('ERROR[${err.response?.statusCode}] => PATH: ${err.requestOptions.path}');
    
    if(err.response.statusCode != null && err.response.statusCode == 411){
      return handler.reject(err);	
    } else {
      return handler.resolve(Response(data: 'fake data'));
    }
  }

}

 

 

😶  Handling Error 

try-catch 문으로 dio 에러를 직접 관리할 수 있다.

import "package:dio/dio.dart";

final dio = Dio();

try {
  await dio.get('https://api.pub.dev/not-exist');
} on DioError catch (e) {
  
  if (e.response != null) {
    print(e.response.data)
    print(e.response.headers)
    print(e.response.requestOptions)
  } else {
    // Something happened in setting up or sending the request that triggered an Error
    print(e.requestOptions)
    print(e.message)
  }
}

 

 

 

728x90