using System.Collections.Specialized;
using System.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Dpz.Core.Service;
public interface IBingWallpaper
{
/// <summary>
/// 随机获取一张壁纸
/// </summary>
/// <returns></returns>
Task<Wallpaper> GetRandomWallpaperAsync();
/// <summary>
/// 获取今日的所有壁纸
/// </summary>
/// <returns></returns>
Task<List<Wallpaper>> GetTodayWallpapersAsync();
/// <summary>
/// 清除缓存
/// </summary>
/// <returns></returns>
Task ClearCacheAsync();
}
public class Wallpaper
{
private const string Host = "https://cn.bing.com";
private string _url = "";
[JsonProperty("url")]
public string Url
{
get => _url.StartsWith("http", StringComparison.OrdinalIgnoreCase) ? _url : Host + _url;
set => _url = value;
}
[JsonProperty("copyright")]
public string? CopyRight { get; set; }
[JsonProperty("copyrightlink")]
public string? CopyRightLink { get; set; }
}
public class BingWallpaper(
ILogger<BingWallpaper> logger,
IHttpClientFactory httpClientFactory,
IFusionCache fusionCache
) : IBingWallpaper
{
private const string CachePrefix = "Dpz.Core.Service.BingWallpaper";
private readonly Wallpaper _defaultWallpaper = new()
{
Url = "/th?id=OHR.RedAlley_ZH-CN2795378972_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp",
CopyRight = "武侯祠内红墙和竹林掩映下的小巷,中国成都 (© Eastimages/Getty Images)",
CopyRightLink =
"https://www.bing.com/search?q=%E6%AD%A6%E4%BE%AF%E7%A5%A0&form=hpcapt&mkt=zh-cn",
};
private async Task<List<Wallpaper>> FetchWallpaperAsync(
CancellationToken cancellationToken = default
)
{
try
{
//var request = new RestRequest("/HPImageArchive.aspx", Method.GET);
var parameters = new NameValueCollection
{
{ "format", "js" },
//请求图片截止天数 0 今天 -1 截止中明天 (预准备的) 1 截止至昨天,类推(目前最多获取到7天前的图片)
{ "idx", "0" },
//1-8 返回请求数量,目前最多一次获取8张
{ "n", "8" },
//地区
{ "mkt", "zh-CN" },
};
var httpClient = httpClientFactory.CreateClient("edge");
var request = new HttpRequestMessage(
HttpMethod.Get,
$"https://cn.bing.com/HPImageArchive.aspx?{parameters.ToQueryString()}"
);
var response = await httpClient.SendAsync(request, cancellationToken);
var content = await response.Content.ReadAsStringAsync(cancellationToken);
var result = JObject.Parse(content)["images"]?.ToObject<List<Wallpaper>>() ?? [];
return result;
}
catch (Exception e)
{
logger.LogError(e, "获取bing壁纸失败");
var result = new List<Wallpaper> { _defaultWallpaper };
return result;
}
}
private async Task<List<Wallpaper>> GetWallpaperAsync()
{
return await fusionCache.GetOrSetAsync<List<Wallpaper>>(
BuildCacheKey(nameof(GetWallpaperAsync)),
async (context, cancellationToken) =>
{
context.Tags = [CachePrefix, BuildMethodTag(nameof(GetWallpaperAsync))];
return await FetchWallpaperAsync(cancellationToken);
},
options => options.SetDuration(TimeSpan.FromDays(1))
);
}
public async Task<Wallpaper> GetRandomWallpaperAsync()
{
return await fusionCache.GetOrSetAsync<Wallpaper>(
BuildCacheKey(nameof(GetRandomWallpaperAsync)),
async (context, _) =>
{
context.Tags = [CachePrefix, BuildMethodTag(nameof(GetRandomWallpaperAsync))];
var list = await GetWallpaperAsync();
var result = list.OrderBy(_ => Guid.NewGuid()).FirstOrDefault();
return result ?? _defaultWallpaper;
},
options => options.SetDuration(TimeSpan.FromDays(1))
);
}
public async Task<List<Wallpaper>> GetTodayWallpapersAsync()
{
return await GetWallpaperAsync();
}
public async Task ClearCacheAsync()
{
await fusionCache.RemoveByTagAsync(CachePrefix);
}
private static string BuildCacheKey(string methodName) => CachePrefix + ":" + methodName;
private static string BuildMethodTag(string methodName) => CachePrefix + ":" + methodName;
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
代码解释
这是一个用于获取 Bing 每日壁纸的服务实现,采用了接口与实现分离的设计模式。以下是详细说明:
一、接口定义 (IBingWallpaper)
定义了三个核心方法:
GetRandomWallpaperAsync(): 随机获取一张壁纸GetTodayWallpapersAsync(): 获取今日的所有壁纸(最多8张)ClearCacheAsync(): 清除缓存
二、数据模型 (Wallpaper)
用于承载壁纸信息:
public class Wallpaper
{
public string Url { get; set; } // 壁纸URL(自动补全域名)
public string? CopyRight { get; set; } // 版权信息
public string? CopyRightLink { get; set; } // 版权链接
}
关键特性:
Url属性使用了智能 getter:如果 URL 不是以http开头,会自动拼接https://cn.bing.com- 使用 JSON 序列化特性方便与 API 响应映射
三、核心实现 (BingWallpaper)
1. 依赖注入
通过主构造函数注入三个依赖:
ILogger: 日志记录IHttpClientFactory: HTTP 客户端工厂IFusionCache: 缓存组件
2. FetchWallpaperAsync() - 数据获取
private async Task<List<Wallpaper>> FetchWallpaperAsync(...)
- 调用 Bing API:
https://cn.bing.com/HPImageArchive.aspx - 请求参数:
format=js: JSON 格式idx=0: 获取今天的壁纸n=8: 返回最多8张mkt=zh-CN: 中国区域
- 解析 JSON 响应中的
images数组 - 异常处理:失败时返回默认壁纸(成都武侯祠)
3. GetWallpaperAsync() - 带缓存的获取
private async Task<List<Wallpaper>> GetWallpaperAsync()
- 使用
FusionCache实现缓存机制 - 缓存策略:
- 缓存时长:1天
- 缓存键:
Dpz.Core.Service.BingWallpaper:GetWallpaperAsync - 标签:支持按标签批量清除
4. GetRandomWallpaperAsync() - 随机壁纸
public async Task<Wallpaper> GetRandomWallpaperAsync()
- 获取今日所有壁纸
- 使用
OrderBy(_ => Guid.NewGuid())实现随机排序 - 返回第一个元素(失败时返回默认壁纸)
- 同样带有1天缓存
5. ClearCacheAsync() - 清除缓存
public async Task ClearCacheAsync()
- 通过标签
CachePrefix清除所有相关缓存
四、设计亮点
缓存分层:
- 原始数据缓存(壁纸列表)
- 随机结果缓存(避免频繁随机计算)
容错机制:
- API 请求失败时提供默认壁纸
- 避免服务完全不可用
可测试性:
- 接口与实现分离
- 依赖注入便于 Mock
缓存管理:
- 统一的缓存键和标签命名规则
- 支持批量清除缓存
性能优化:
- 使用 HttpClientFactory 避免 Socket 耗尽
- 缓存减少 API 调用频率
五、使用场景
适用于需要展示 Bing 每日壁纸的应用场景,如:
- 网站首页背景
- 移动应用启动屏
- 桌面客户端壁纸更新
AI 正在分析代码…
评论加载中...