《Flutter 接入 Wallhaven API 惊魂记:从技术狂喜到 “社死” 边缘的 24 小时!》

Flutter 接入 Wallhaven API 惊魂记:从技术狂喜到 "社死" 边缘的 24 小时

闷热的办公室里,我专注地在电脑上用 Flutter 开发桌面壁纸软件。当发现 Wallhaven 丰富的 API 接口时,那些 "categories""purity" 等筛选参数,仿佛预示着能轻松实现高质量图源整合,我立刻兴奋地编写调用代码。​

可现实很快泼来冷水。Wallhaven 官网的 API 文档对参数描述模糊不清,"categories" 默认值与组合规则、"purity" 的内容过滤标准都语焉不详。我只能坐在工位反复调试参数,刷新接口。​

起初一切正常,直到调整 "categories" 和 "purity" 组合参数时,一张限制级图片突然在电脑屏幕上弹出。我慌得手忙脚乱,鼠标乱点,余光瞥见隔壁同事投来疑惑的目光,后背瞬间冷汗直冒。好不容易关掉页面,尴尬得如坐针毡。​

冷静后排查发现,"purity" 默认不排除限制级内容,"categories" 默认值也会返回边缘内容。我迅速在参数中加入 "SFW" 标识,精准设置类别,并增加本地图片审核逻辑。经过数小时调试,终于让软件呈现出符合定位的壁纸。​

这次经历给我敲响警钟:API 接入不能轻信文档,内容筛选参数务必反复测试,在公共场合调试更要提前评估风险。技术路上,一个小疏忽就可能带来 "社死" 危机,严谨才是开发的生存法则。

1、Wallhaven 简介

Wallhaven 堪称壁纸爱好者的天堂,是全球知名的高质量壁纸下载网站。它拥有超百万张高品质壁纸,内容涵盖极为丰富,无论是二次元动漫迷钟情的精美插画,摄影爱好者喜爱的壮丽风景,还是游戏玩家热衷的游戏场景,甚至是人物特写等各类风格壁纸,都能在这里轻松找到。

在搜索和筛选功能方面,Wallhaven 表现得十分出色。用户既可以通过输入英文关键词精准搜索目标壁纸,也能查看热门标签,快速浏览当下流行的壁纸分类。网站还设有排行榜,依据浏览量、点赞数等数据,为用户呈现最受欢迎的壁纸作品。值得一提的是,其独特的 "categories" 和 "purity" 等筛选参数,本应助力用户进一步精准定位所需壁纸,却因官网文档描述模糊,给开发者带来不少困扰,这也是我此次接入 API 时遭遇 "社死危机" 的重要原因。

官网地址

官网地址: wallhaven.cc/ ,wallhaven 国内是无法正常访问的,需要 VPN 才能访问。

接口文档

接口文档地址: wallhaven.cc/help/api ,这里只对 搜索api进行介绍,其它接口大家感兴趣可以自行前往查看。

基础路径:wallhaven.cc/api/v1/sear... , 参数前往官网查看吧,还是有点多的。

关键参数

  • apikey:需要注册账号后生成,注册账号有的 ip 地址可能会失败,无法注册,多换几个ip试试,我是用日本IP才注册成功的。注册成功后通过下方操作即可生成自己的apikey参数。

  • purity :100/110/111(sfw/sketchy/nsfw),这个参数就是控制图片最大等级的,分别对应:安全、r16、r18!!!!!!!!!!
  • sorting (整体排序): date_added , relevance, random, views, favorites, toplist,不知道relevance 有啥用,我把 relevance 换成 hot 参数,请求结果就和官网热门结果一致了,但是官网文档却没给出 hot 参数。

示例

/api/wallhaven.dart

dart 复制代码
// wallhaven 壁纸api
// ignore_for_file: equal_keys_in_map

import 'package:dio/dio.dart';
import 'package:wallpaper_app/http/dio_instance.dart';

// 搜索壁纸
Future<Response> wallhavenSearch({
  required String q,
  required int page,
  required String type,
  String? ratio,
  String? order,
  String? apikey,
  String? categories,
  String? purity,
}) {
  final params = {
    'apikey': apikey,
    'q': q,
    'categories': categories,
    'purity': purity,
    'sorting': type,
    'order': order,
    'page': page.toString(),
    'ratios': ratio,
  };
  return DioInstance.instance().get(
    path: 'https://wallhaven.cc/api/v1/search',
    param: params,
  );
}

2、代理设置

Wallhaven 被墙了,接口自然也被墙,默认 flutter windows不走代理,就算你开着VPN也不能正常请求接口,需要对接口也做代理。

下面是一些踩坑记录和最终解决方法:

2.1 方案一:给dio网络做单独代理(初始方案)

以下是ai帮忙做的代理设置,因为不是最终方案,没做具体的记录,而且我的dio是经过封装的配置略有差异,但是都是给dio请求做的代理配置。

dio全局静态代理配置

通过 Dio 的HttpClientAdapter设置固定代理,适合开发环境使用:

dart 复制代码
import 'package:dio/dio.dart';

void main() {
  Dio dio = Dio();
  
  // 配置HTTP代理
  (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
      (client) {
    client.findProxy = (uri) {
      return "PROXY 192.168.1.100:8888"; // 替换为你的代理服务器地址和端口
    };
    
    // 忽略证书验证(仅用于测试环境)
    client.badCertificateCallback =
        (X509Certificate cert, String host, int port) => true;
  };
  
  // 发起请求
  dio.get('https://api.example.com/data');
}
方案弊端
  • 通过 Flutter 内置的 Image 组件加载图片 (如 Image.networkImage.asset 等)。这些加载方式是 Flutter 框架提供的内置功能,它们直接通过底层的网络请求系统(如 HttpClient)发起图片请求,而不是通过 Dio 库。因此,即使 Dio 配置了代理,这些请求也不会受 Dio 配置影响。
  • 通过其他图片加载库加载图片 (如 cached_network_image 等)。这些库可能有自己的网络请求实现方式,不一定使用 Dio。因此,这些请求也不会走 Dio 的代理配置。

该方案只能使接口正常获取到数据,拿到图片路径后依然无法正常加载图片。ai提供的解决方案是通过 dio 下载图片后在展示图片,因为嫌麻烦,没有尝试过行不行,大家有兴趣可以自行测试。

2.2 方案二:配置全局 HttpOverrides.global 代理

  1. 首先判断本地的 7890 端口是否开启代理,基本所有的VPN软件都是走的这个端口。
  2. 如果开启了代理,全局的网络都走 120.0.0.1:7890 代理。
HttpOverrides 全局代理
dart 复制代码
······
······
// 判断代理是否开启
Future<bool> isProxyAvailable(String host, int port) async {
  try {
    final socket = await Socket.connect(host, port,
        timeout: const Duration(milliseconds: 500));
    socket.destroy();
    return true;
  } catch (_) {
    return false;
  }
}

void main() async {
  ······
  const proxyHost = '127.0.0.1';
  const proxyPort = 7890; // 根据你的代理端口修改

  if (await isProxyAvailable(proxyHost, proxyPort)) {
    HttpOverrides.global = MyHttpOverrides(proxyHost, proxyPort);
  }
  
  ······
  runApp(MultiProvider(
    providers: [
      ······
    ],
    child: MyApp(),
  ));
}

class MyApp extends StatelessWidget {
    ······
    ······
}

class MyHttpOverrides extends HttpOverrides {
  final String host;
  final int port;
  MyHttpOverrides(this.host, this.port);

  @override
  HttpClient createHttpClient(SecurityContext? context) {
    var client = super.createHttpClient(context);
    client.findProxy = (uri) => "PROXY $host:$port";
    client.badCertificateCallback =
        (X509Certificate cert, String host, int port) => true;
    return client;
  }
}
方案弊端
  • 所有的网络都走了代理,对于这个项目完全多此一举。
  • 代理状态(关闭/开启)变化时,必须重新启动软件接口才能正常访问,无法动态判读。

2.3 方案三:动态判断是否需要代理(方案二升级)

方案二已经解决dio和图片问题,稍作优化即可完美解决代理问题。

HttpOverrides 全局智能代理
  • 不再需要判断代理是否开启,至少在国内 Wallhaven 现在必须开VPN才能正常访问。
  • 直接根据请求中是否包含 wallhaven.cc 判断是否需要代理。
  • 这样做只有包含 wallhaven.cc 的请求才走代理,其它接口均不走,就算代理状态变化了也不影响其它接口正常调用。

先定义一个代理管理文件 /lib/proxy/my_http_overrides.dart

dart 复制代码
import 'dart:io';

class MyHttpOverrides extends HttpOverrides {
  final String host = '127.0.0.1';
  final int port = 7890;

  @override
  HttpClient createHttpClient(SecurityContext? context) {
    var client = super.createHttpClient(context);
    client.findProxy = (uri) {
      // 这里动态判断是否需要代理
      if (shouldUseProxy(uri.host)) {
        return "PROXY $host:$port";
      }
      return "DIRECT";
    };
    client.badCertificateCallback =
        (X509Certificate cert, String host, int port) => true;
    return client;
  }
}

// 你可以自定义判断逻辑,比如只对 wallhaven.cc 代理
bool shouldUseProxy(String host) {
  // 只对 wallhaven.cc 及其子域名走代理
  return host.contains('wallhaven.cc');
}

然后在main.dart中直接调用即可。

dart 复制代码
void main() async {
  ······

  HttpOverrides.global = MyHttpOverrides();
  
  ······
}
方案弊端

暂时没有发型,硬要说有的话可能就是 如果存在多个接口都需要代理时,你都要加上判断,优化一下判断逻辑即可。

3、效果预览

wallhaven官网: 自己的软件:限制级别选 模糊 即可,和官网一模一样。

4、项目开源地址

开源地址

gitee.com/zsnoin-can/...

软件体验地址

gitee.com/zsnoin-can/...

详细介绍

juejin.cn/post/749455...

相关推荐
YCOSA20256 小时前
雨晨 Windows 10 企业版 LTSC x64 中度精简 1809 迎双节 17763.9257 特别版
windows
sukalot6 小时前
windows 驱动实例分析系列: HidHide 驱动分析 - drivers 篇(四)
windows·驱动开发
程序员老刘10 小时前
跨平台开发地图 | 2026年9月
flutter·客户端
ι:12 小时前
NUC12 Pro 安装 Ubuntu 24.04 LTS 详细教程
linux·windows·ubuntu·nuc
YCOSA202512 小时前
微软宣布将于10月7日举行Windows与Surface发布会,聚焦“Windows、NVIDIA RTX Spark、Surface,以及更广泛PC生态”
windows
Source.Liu12 小时前
【A11】 labelprinter(Tauri 版)新建步骤
windows·rust
Punchline27813 小时前
解决 Windows 环境变量 Path 设置时提示“字符太长”的完整指南
windows·环境变量·path
HouWan14 小时前
Flutter iOS UISceneDelegate 迁移指南:理清新的 Scene 生命周期
flutter·ios·app
chushiyunen14 小时前
claude code笔记(二)、claude桌面端(内网不好用)
windows
d_benhua15 小时前
以太网测试步骤
windows