{"id":32294466,"url":"https://github.com/leo1394/authenticated_http_client","last_synced_at":"2026-02-22T18:02:31.543Z","repository":{"id":274278911,"uuid":"922198879","full_name":"leo1394/authenticated_http_client","owner":"leo1394","description":"An advanced authenticated HTTP client introduces `factory` feature that generates request functions based on API definitions in WYSIWYG style and supports `mock` and `silent` modes for development.","archived":false,"fork":false,"pushed_at":"2026-01-19T06:29:18.000Z","size":147,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-19T14:52:00.152Z","etag":null,"topics":["authenticated-http-client","dart","http-client","http-interceptor","http-request","wysiwyg"],"latest_commit_sha":null,"homepage":"https://pub.dev/packages/authenticated_http_client","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leo1394.png","metadata":{"files":{"readme":"README-EN.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-01-25T15:29:42.000Z","updated_at":"2026-01-19T06:29:22.000Z","dependencies_parsed_at":null,"dependency_job_id":"57becf69-96a6-4beb-bcc2-9819f76f5f34","html_url":"https://github.com/leo1394/authenticated_http_client","commit_stats":null,"previous_names":["leo1394/authenticated_http_client"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/leo1394/authenticated_http_client","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leo1394%2Fauthenticated_http_client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leo1394%2Fauthenticated_http_client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leo1394%2Fauthenticated_http_client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leo1394%2Fauthenticated_http_client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leo1394","download_url":"https://codeload.github.com/leo1394/authenticated_http_client/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leo1394%2Fauthenticated_http_client/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29721054,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-22T15:10:41.462Z","status":"ssl_error","status_checked_at":"2026-02-22T15:10:04.636Z","response_time":110,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authenticated-http-client","dart","http-client","http-interceptor","http-request","wysiwyg"],"created_at":"2025-10-23T03:44:38.644Z","updated_at":"2026-02-22T18:02:31.536Z","avatar_url":"https://github.com/leo1394.png","language":"Dart","readme":"\n## authenticated_http_client\n[![pub package](https://img.shields.io/pub/v/authenticated_http_client.svg)](https://pub.dev/packages/authenticated_http_client)\n[![pub points](https://img.shields.io/pub/points/authenticated_http_client?color=2E8B57\u0026label=pub%20points)](https://pub.dev/packages/authenticated_http_client/score)\n[![GitHub Issues](https://img.shields.io/github/issues/leo1394/authenticated_http_client.svg?branch=master)](https://github.com/leo1394/authenticated_http_client/issues)\n[![GitHub Forks](https://img.shields.io/github/forks/leo1394/authenticated_http_client.svg?branch=master)](https://github.com/leo1394/authenticated_http_client/network)\n[![GitHub Stars](https://img.shields.io/github/stars/leo1394/authenticated_http_client.svg?branch=master)](https://github.com/leo1394/authenticated_http_client/stargazers)\n[![GitHub License](https://img.shields.io/badge/license-MIT%20-blue.svg)](https://raw.githubusercontent.com/leo1394/authenticated_http_client/master/LICENSE)\n\nAn advanced authenticated HTTP client introduces a `factory` feature that generates request futures based on API definitions in a WYSIWYG style, \nwith additional supports for `mock` and `silent` modes during development.\n\nIt all began with the forging pattern — `[mock] [silent] [method] /api/plan/{id}/details` — a skeletal incantation that would simplify every thing.\n\n- `factory`: generates AJAX request functions by api URI declaration in WYSIWYG style.\n- `mock`: mocking data from a local JSON file for specified requests during development. \n- `silent`: suppress routing redirection for unauthorized or maintenance responses for specified request.\n- `throttling`: queues excess requests using async/await for controlled upload pacing.\n- request futures functions generated can be accessed by dot notation.\n\nLanguage: English | [中文](README.md)\n## Platform Support\n\n| Android | iOS | MacOS | Web | Linux | Windows |\n| :-----: | :-: | :---: |:---:| :---: | :-----: |\n|   ✅    | ✅  |  ✅   |  ❌️  |  ✅   |   ✅    |\n\n## Requirements\n\n- Flutter \u003e=3.0.0 \u003c4.0.0\n- Dart: ^2.17.0\n- http: ^1.3.0\n- http_interceptor: ^2.0.0\n\n## Getting started\npublished on pub.dev, run this Flutter command\n```shell\nflutter pub add authenticated_http_client\n```\n\n## Usage showcase\n- Retrieve the auth_token and store it in the AuthenticatedHttpClient cache.\n```dart\n    var apiService = AuthenticatedHttpClient.getInstance().factory({ \"login\"  : \"POST /api/sign-in\" });\n    \n    apiService.login({\"username\": \"demo\", \"passwords\": \"test123\"}).then((response) {\n        // response here in format {code, message, data}\n        final {\"auth_token\": authToken, \"expired_at\": expiredAt} = response[\"data\"];\n        AuthenticatedHttpClient.getInstance().setAuthToken(authToken);\n    });\n```\n\n- Create request futures with support for path parameters.\n```dart\n    var apiService = AuthenticatedHttpClient.getInstance().factory({\n        \"requestName\"                : \"POST /api/submit/plan\",\n        \"requestNameWithColonParams\" : \"GET /api/plan/:id/details\", \n        \"requestNameWithBraceParams\" : \"GET /api/plan/{id}/details\"\n    });\n    apiService.requestName().then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });\n    apiService.requestNameWithColonParams({\"id\": 9527}).then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });\n    apiService.requestNameWithBraceParams({\"id\": 9527}).then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });\n```\n\n\n- `UP` request uploads a file.\n```dart\n    var apiService = AuthenticatedHttpClient.getInstance().factory({\n        \"uploadFile\"         : \"UP /api/file\" \n    });\n    String localPath = \"/local/path/for/upload\";\n    apiService.uploadFile({\"file\": localPath}, throttling: true).then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });\n```\n\n- `DOWN` request downloads a file.\n```dart\n    String url = \"https://assets.xxx.com/path/of/file\"; // or /static/file\n    \n    var apiService = AuthenticatedHttpClient.getInstance().factory({\n        \"download\"         : \"DOWN $url\" \n    });\n    void onReceiveProgress(int received, int total) {\n      print(\"Downloading Progress $received/$total\");\n    }\n    String savePath = \"/local/path/for/download\";\n    // if Authorization is not required, set authenticate: false\n    apiService.download(null, savePath: savePath, authenticate: false, onReceiveProgress: onReceiveProgress).then((bytes) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });\n```\n\n- Mock requests are served from JSON files in the mockDirectory (defaulting to /lib/mock), which can be configured via AuthenticatedHttpClient.getInstance().init() and need to be declared under assets section in pubspec.yaml.\n```dart\n    var apiService = AuthenticatedHttpClient.getInstance().factory({\n        \"mockRequest\"           : \"MOCK POST /api/task/config\", \n    });\n\n    // mock from _POST_api_task_config.json under mockDirectory /lib/mock\n    apiService.mockRequest().then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });\n```\n\n- Silent requests skip redirection for unauthorized responses or during maintenance.\n```dart\n    var apiService = AuthenticatedHttpClient.getInstance().factory({\n        \"silentRequest\"         : \"SILENT GET /api/message/check/unread\" \n    });\n\n    // Even http status 401 won't redirect to login   \n    apiService.silentRequest().then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });\n```\n\n- Throttling control, queue excess requests.\n```dart\n    var apiService = AuthenticatedHttpClient.getInstance().factory({\n        \"request\"         : \"SILENT POST /api/upload\" \n    });\n\n    apiService.request(throttling: true).then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });\n```\n\n- Every request would be marked with an unique id (say `requestId`) for tracking purpose, which also can be passed in as named argument.\n```dart\n    var apiService = AuthenticatedHttpClient.getInstance().factory({\n        \"request\"         : \"SILENT POST /api/upload\" \n    });\n    String requestId = Uuid().v1();\n    print(\"gonna send request with unique id : $requestId\");\n    apiService.request(requestId: requestId).then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });\n    \n```\n\n- AuthenticatedHttpClient.all is a static function, like Promise.all, with a delay feature to prevent potential server concurrency issues.\n```dart\n    var apiService = AuthenticatedHttpClient.getInstance().factory({\n        \"requestName\"           : \"POST /api/submit/plan\",\n        \"requestNameWithParams\" : \"GET /api/plan/:id/details\",\n    });\n\n    List\u003cFuture\u003e futures = [apiService.requestName(), apiService.requestNameWithParams({\"id\": 9528})];\n    AuthenticatedHttpClient.all(futures, delayInMilliSecs: 350).then((List\u003cdynamic\u003e results){\n        print(results[0]); // response of No.1 request\n        print(results[1]); // response of No.2 request\n    });\n```\n\n## Steps for Usage in Dart\n- Initialize a RouterHelper to manage when and how the authenticated HTTP client redirects to the login or maintenance route, if necessary.\n```dart\n    import 'package:authenticated_http_client/router_helper.dart';\n    \n    RouterHelper.init(\n        jump2LoginCallback: () { /* navigate to login route depends on routing library */ },\n        unAuthCode: \"101|103\"\n    );\n```\n\u003cdetails\u003e\n  \u003csummary\u003e`jump2LoginCallback` examples using different routing library\u003c/summary\u003e\n\n```dart\n    // FluroRouter example\n    BuildContext context; // keep current build context\n    function jump2LoginCallback() {\n        String? currentRoute = ModalRoute.of(context)?.settings.name;\n        if(currentRoute?.split(\"?\").first == \"/login\") { return ; }\n        FluroRouter().navigateTo(context, \"/login\", clearStack: true);\n    }\n```\n\n```dart\n    // GoRouter example\n    BuildContext context; // keep current build context\n    function jump2LoginCallback() {\n        String? currentRoute = GoRouter.of(context).location;\n        if(currentRoute?.split(\"?\").first == \"/login\") { return ; }\n        GoRouter(routes: []).go(\"/login\");\n    }\n```\n\n\u003c/details\u003e\n\n- Implement the CustomHttpHeadersInterceptor required for initializing the AuthenticatedHttpClient later.\n```dart\n    import 'package:authenticated_http_client/http_headers_interceptor.dart';\n    \n    class Constants {\n        static String udid = \"357292741221214\";\n        static String appVersion = \"1.0.1\";\n        static String appBuildNumber = \"1394\";\n        static String systemCode = \"e08f11c8-3b1c-4008-abba-045787e0b6c0\";\n    }\n    \n    class CustomHttpHeadersInterceptor extends HttpHeadersInterceptor {\n        @override\n        Map\u003cString, String\u003e headersInterceptor(Map\u003cString, String\u003e headers) {\n            headers[\"udid\"] = Constants.udid;\n            headers[\"version\"] = Constants.appVersion;\n            headers[\"system-code\"] = Constants.systemCode;\n            return headers;\n        }\n    }\n```\n\n- Initialize an authenticated HTTP client that appends a token to the Authorization header for every subsequent AJAX request.  \n```dart\n    import 'package:authenticated_http_client/authenticated_http_client.dart';\n    \n    AuthenticatedHttpClient.getInstance().init(\n        \"https://api.company.com\", \n        customHttpHeadersInterceptor: CustomHttpHeadersInterceptor()\n    );\n```\n\n- Ultimately, use any feature at your convenience.\n```dart\n    var apiService = AuthenticatedHttpClient.getInstance().factory({\n        \"login\"                 : \"POST /api/sign-in\",\n        \"requestName\"           : \"POST /api/submit/plan\",\n        \"requestNameWithParams\" : \"GET /api/plan/:id/details\", // or \"GET /api/plan/{id}/details\"\n        \"mockRequest\"           : \"MOCK POST /api/task/config\", // mock from _post_api_task_config.json under mockDirectory /lib/mock\n        \"silentRequest\"         : \"SILENT GET /api/message/check/unread\" // silent request won't jump when response met unauthorized or under maintenance\n    });\n    \n    apiService.login({\"username\": \"demo\", \"passwords\": \"test123\"}).then((response) {\n        // response here in format {code, message, data}\n        final {\"auth_token\": authToken, \"expired_at\": expiredAt} = response[\"data\"];\n        AuthenticatedHttpClient.getInstance().setAuthToken(authToken);\n    });\n    apiService.requestName().then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });\n    apiService.requestNameWithParams({\"id\": 9527}).then((response) {/* success */}).catchError((e, stackTrace){ /* fail */ }).whenComplete((){ /* finally */ });\n    \n    /// AuthenticatedHttpClient.all is a static function,similar to Promise.all,\n    /// which introduces a delay feature to prevent potential server concurrency issues\n    List\u003cFuture\u003e futures = [apiService.requestName(), apiService.requestNameWithParams({\"id\": 9528})];\n    AuthenticatedHttpClient.all(futures, delayInMilliSecs: 350).then((results){\n        print(results['0']); // response of No.1 request\n        print(results['1']); // response of No.2 request\n    });\n```\n\n## Additional information\nFeel free to file an issue if you have any problem.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleo1394%2Fauthenticated_http_client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleo1394%2Fauthenticated_http_client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleo1394%2Fauthenticated_http_client/lists"}