restio_retrofit 0.3.0 copy "restio_retrofit: ^0.3.0" to clipboard
restio_retrofit: ^0.3.0 copied to clipboard

discontinued

HTTP client generator for Restio inspired by Retrofit.

Retrofit #

HTTP client generator for Restio inspired by Retrofit.

Installation #

In pubspec.yaml add the following dev dependencies:

dev_dependencies:
  restio_retrofit: ^0.3.0
  build_runner: ^1.10.0
copied to clipboard

Usage #

Define and Generate your API #

import 'package:restio/restio.dart';
import 'package:restio_retrofit/restio_retrofit.dart' as retrofit;

part 'httpbin.g.dart';

@retrofit.Api('https://httpbin.org')
abstract class HttpbinApi {
  factory HttpbinApi({Restio client, String baseUri}) = _HttpbinApi;

  @retrofit.Get('/get')
  Future<String> get();
}
copied to clipboard

It is highly recommended that you prefix the retrofit library.

Then run the generator

# Dart
pub run build_runner build

# Flutter
flutter pub run build_runner build
copied to clipboard

Use It #

import 'package:restio/restio.dart';

import 'httpbin.dart';

void main(List<String> args) async {
  final client = Restio();
  final api = HttpbinApi(client: client);
  final data = await api.get();
  print(data);
}
copied to clipboard

Examples #

API Declaration #

Annotations on the methods and its parameters indicate how a request will be handled.

Http Methods #

Every method must have an HTTP Method annotation that provides the request method and relative URL. There are eight built-in annotations: Method, Get, Post, Put, Patch, Delete, Options and Head. The relative URL of the resource is specified in the annotation.

@retrofit.Get('/get')
copied to clipboard

You can also specify query parameters in the URL.

@retrofit.Get('users/list?sort=desc')
copied to clipboard

URL Manipulation #

A request URL can be updated dynamically using replacement blocks and parameters on the method. A replacement block is an alphanumeric name surrounded by { and }. A corresponding parameter must be annotated with Path. If the Path name is omitted the parameter name is used instead.

@retrofit.Get('group/{id}/users')
Future<List<User>> groupList(@retrofit.Path('id') int groupId);
copied to clipboard

Query parameters can also be added.

@retrofit.Get('group/{id}/users')
Future<List<User>> groupList(@retrofit.Path('id') int groupId, @retrofit.Query() String sort);
copied to clipboard

For complex query parameter combinations a Map<String, ?>, Queries, List<Query> can be used. In this case, annotate the parameters with Queries.

You can set static queries for a method using the Query annotation.

@retrofit.Query('sort' ,'desc')
Future<List<User>> groupList(@retrofit.Path('id') int groupId);
copied to clipboard

Note that queries do not overwrite each other. All queries with the same name will be included in the request.

@retrofit.Get('group/{id}/users')
Future<List<User>> groupList(@retrofit.Path('id') int groupId, @retrofit.Queries() Map<String, dynamic> options);
copied to clipboard

If you desire add queries with no values, you can use List<String>.

@retrofit.Get('group/{id}/users')
Future<List<User>> groupList(@retrofit.Path('id') int groupId, @retrofit.Queries() List<String> flags);
copied to clipboard

Request Body #

An object can be specified for use as an HTTP request body with the Body annotation.

@retrofit.Get('group/{id}/users')
Future<void> createUser(@retrofit.Body() User user);
copied to clipboard

The object will also be converted using a converter specified on the Api instance. If no converter is added, only File, String, List<int>, Stream<List<int>> or RequestBody can be used.

Form and Multipart #

Methods can also be declared to send form-encoded and multipart data.

Form-encoded data is sent when Form annotation is present on the method. Each key-value pair (field) is annotated with Field containing the name (optional, the parameter name will be sed instead) and the object providing the value.

@retrofit.Form()
@retrofit.Post('user/edit')
Future<User> updateUser(@retofit.Field("first_name") String first, @retrofit.Field("last_name") String last);
copied to clipboard

You can set static field for a method using the Field annotation.

@retrofit.Field('first_name' ,'Tiago')
@retrofit.Field('last_name' ,'Melo')
Future<User> updateUserEmail(@retofit.Field() String email);
copied to clipboard

Note that fields do not overwrite each other. All fields with the same name will be included in the request.

Multipart requests are used when Multipart annotation is present on the method. Parts are declared using the Part annotation.

@retrofit.Multipart()
@retrofit.Put('user/photo')
Future<User> updateUser(@retrofit.Part() File photo);
copied to clipboard

The parameter type can be only File, String, Part or List<Part>. For File parameter type you can set the filename and contentType properties.

Header Manipulation #

You can set static headers for a method using the Header annotation.

@retrofit.Header('Accept' ,'application/vnd.github.v3.full+json')
@retrofit.Header('User-Agent' ,'Retrofit')
@retrofit.Get('users/{username}')
Future<User> getUser(@retrofit.Path() String username);
copied to clipboard

Note that headers do not overwrite each other. All headers with the same name will be included in the request.

A request Header can be updated dynamically using the Header annotation. A corresponding parameter must be provided to the Header. If the name is omitted, the parameter name will be used instead.

@retrofit.Get('user')
Future<User> getUser(@retrofit.Header('Authorization') String authorization);
copied to clipboard

Similar to query parameters, for complex header combinations a Map<String, ?>, Headers, List<Header> can be used. In this case, annotate the parameters with Headers.

@retrofit.Get('user')
Future<User> getUser(@retrofit.Headers() Map<String, dynamic> headers);
copied to clipboard

Headers that need to be added to every request can be specified using an interceptor.

Authentication #

Basic Authentication

A Basic authentication can be provided dynamically annotating a parameter with BasicUsername or BasicPassword annotation.

@retrofit.Get('/basic-auth/{user}/{passwd}')
Future<dynamic> basicAuth(
  @retrofit.Path() @retrofit.BasicUsername() String user,
  @retrofit.Path('passwd') @retrofit.BasicPassword() String password,
);
copied to clipboard

You can set static authentication for a method using the BasicAuth annotation.

@retrofit.Get('/basic-auth/restio/1234')
@retrofit.BasicAuth('restio', '1234')
Future<dynamic> basicAuth();
copied to clipboard

Digest Authentication

A Digest authentication can be provided dynamically annotating a parameter with DigestUsername or DigestPassword annotation.

@retrofit.Get('/digest-auth/auth/{user}/{passwd}/MD5')
Future<dynamic> digestAuth(
  @retrofit.Path() @retrofit.DigestUsername() String user,
  @retrofit.Path('passwd') @retrofit.DigestPassword() String password,
);
copied to clipboard

You can set static authentication for a method using the DigestAuth annotation.

@retrofit.Get('/digest-auth/auth/restio/1234/MD5')
@retrofit.DigestAuth('restio', '1234')
Future<dynamic> digestAuth();
copied to clipboard

Bearer Authentication

A Bearer authentication can be provided dynamically annotating a parameter with BearerToken or BearerPrefix annotation.

@retrofit.Get('/bearer')
Future<dynamic> bearerAuth(
  @retrofit.Path() @retrofit.BearerToken() String token,
);
copied to clipboard

You can set static authentication for a method using the BearerAuth annotation.

@retrofit.Get('/bearer')
@retrofit.BearerAuth('1234')
Future<dynamic> bearerAuth();
copied to clipboard

Hawk Authentication

In the same way you can provide Hawk authentication dynamically using HawkKey, HawkId, HawkAlgorithm or HawkExt annotation to the corresponding parameter or statically annotating a method with HawkAuth.

HTTP2 #

To indicate that a method will use HTTP2 connection, annotate one with Http2.

@retrofit.Get('/get')
@retrofit.Http2()
Future<dynamic> http2();
copied to clipboard

Converter #

You should use the BodyConverter class from restio package.

class FlutterBodyConverter extends BodyConverter {
  const FlutterBodyConverter();
  
  @override
  Future<String> encode<T>(
    T value,
    MediaType contentType,
  ) {
    final mimeType = contentType.mimeType;

    if (mimeType == 'application/json') {
      return compute(...);
    } else {
      throw RestioException('Content type $mimeType not supported');
    }
  }

  @override
  Future<T> decode<T>(
    String source,
    MediaType contentType,
  ) {
    final mimeType = contentType.mimeType;

    if (mimeType == 'application/json') {
      final data = await super.decode(source, contentType);

      if (isType<T, User>()) {
        return compute(...);
        // return User.fromJson(data) as T;
      } else if (isType<T, List<User>>()) {
        return compute(...);
        // return [for (final item in data) User.fromJson(item)] as T;
      }

      return data;
    } else {
      throw RestioException('Content type $mimeType not supported');
    }
  }
}

/// Checks whether [T1] is a type or subtype of [T2].
bool isType<T1, T2>() => <T1>[] is List<T2>;

// Set the custom converter.
Restio.bodyConverter = const FlutterBodyConverter();
copied to clipboard

Response #

The method return type can be:

  • Future<List<int>> for decompressed data (gzip, deflate or brotli)
  • Future<String> for String data
  • Future<dynamic> for JSON decoded data (provided by Converter)
  • Future<int> for the response code only
  • Future<Response> for get response class
  • Stream<List<int>> for Stream data
  • Future<?> for get complex data (provided by Converter)
  • Future<Result<?>> for get any data along with response code, response message, headers and cookies

You can get the uncompressed data annotating the method with Raw e using Future<List<int>>.

@retrofit.Raw()
@retrofit.Get('/gzip')
Future<List<int>> gzip();
copied to clipboard

If you use Future<Response> or Stream<List<int>> you are responsible for closing the response.

Result #

You can use Result<T> as return type for get any data along with response code, response message, headers and cookies. Use Result<void> when no data is returned.

@retrofit.Get('/users/{id}')
Future<Result<User>> getUser(@retrofit.Path() int id);

@retrofit.Post('/users')
Future<Result<void>> createUser(
    @retrofit.Body(contentType: 'application/json') User user);
copied to clipboard

Response Status Exception #

For default, if the response status is not between 200-299, will be throw the HttpStatusException.

You can specify the status code range annotating the method with Throws.

@retrofit.Throws(400, 600)
@retrofit.Get('/users/{id}')
Future<User> getUser(@retrofit.Path() int id)
copied to clipboard

There are seven built-in Throws annotations:

  • @retrofit.Throws.only(int code): Specify the status code that throws the exception.
  • @retrofit.Throws.not(int code): Specify the status code that not throws the exception.
  • @retrofit.Throws.redirect(): Throws the exception only if the response code is from redirects.
  • @retrofit.Throws.notRedirect(): Throws the exception only if the response code is not from redirects.
  • @retrofit.Throws.error(): Throws the exception only if the response code is a client error or server error.
  • @retrofit.Throws.clientError(): Throws the exception only if the response code is a client error.
  • @retrofit.Throws.serverError(): Throws the exception only if the response code is a server error.

You are responsible for closing the response on catch block.

try {
  final user = await api.getUser(0);
} on HttpStatusException catch(e) {
  final code = e.code;
  final response = e.response;
  await response.close();
}
copied to clipboard

Future<int>, Future<Response> and Future<Result<?>> do not throw the exception. For other response types, if you want to prevent the exception from being thrown, annotate the method with NotThrows.

Request Options & Extra #

If you want pass RequestOptions to the request, just add the parameter to the method.

@retrofit.Get('/')
Future<dynamic> get(RequestOptions options);
copied to clipboard

If you want pass Map<String, dynamic> as extra property to the request, annotate the parameter with Extra.

@retrofit.Get('/')
Future<dynamic> get(@retrofit.Extra() Map<String, dynamic> extra);
copied to clipboard
2
likes
40
points
28
downloads

Publisher

verified publishertiagohm.dev

Weekly Downloads

2024.10.03 - 2025.04.17

HTTP client generator for Restio inspired by Retrofit.

Repository (GitHub)

License

MIT (license)

Dependencies

analyzer, build, build_runner, code_builder, dart_style, meta, restio, source_gen

More

Packages that depend on restio_retrofit