0x00 存在意义

权且当作Docker打包的练习。

显然可以通过构造请求获得每天的壁纸,但是如果想要优雅地在其它地方使用这一网络资源,封装一个RESTful API将会保证整洁美观,在编写CSS等场景中会非常方便。

0x01 代码实现

首先是项目的目录结构:

│  bw.exe
│ Dockerfile
│ go.mod
│ go.sum
│ main.go
│ README.md

├─command
│ command.go

├─image
│ favicon.ico

├─roothandler
│ roothandler.go

└─xmlhandler
response.go
xmlhandler.go

接口逻辑

先向上游请求XML,通过处理获得关键信息,然后形成JSON作为接口返回格式(也包括直接重定向到图片,但是没有更多文本信息,可用于CSS引入)。

接口参数

接口设置了几个参数:

  • resolution: [1920, 1366, 3840]

    图片的分辨率,默认为1920,递增依次为HD、Full HD、UHD(4K)品质。在请求XML时未被使用,会在后续拼接到图片的链接中。

  • format: [json, image]

    返回形式,默认为json。在请求XML时未被使用,接口根据这个参数决定返回类型。

  • index: [0, +∞)

    图片序号,从0开始。在请求XML时需要使用。

  • mkt: [zh-CN, en-US, ja-JP, en-AU, en-UK, de-DE, en-NZ, en-CA]

    地区,决定了使用语言,默认为zh-CN。在请求XML时需要使用。

处理XML

先向接口上游请求数据,以XML形式返回。

xmlhandler/xmlhandler.go

package xmlhandler

import (
"fmt"
"io/ioutil"
"net/http"
"time" "github.com/beevik/etree"
) const (
BingURL = "https://cn.bing.com"
BingAPI = "https://cn.bing.com/HPImageArchive.aspx?format=xml&idx=%d&n=1&mkt=%s"
) var Resolution map[string]string
var Markets map[string]bool func init() {
Resolution = map[string]string{
"1366": "1366x768.jpg", // HD
"1920": "1920x1080.jpg", // Full HD
"3840": "UHD.jpg", // 3840×2160
}
Markets = map[string]bool{
"en-US": true,
"zh-CN": true,
"ja-JP": true,
"en-AU": true,
"en-UK": true,
"de-DE": true,
"en-NZ": true,
"en-CA": true,
}
} // Get: parse .XML from Bing API
func Get(index uint, market string, resolution string) (*Response, error) {
if _, ok := Resolution[resolution]; !ok {
return nil, fmt.Errorf("resolution %s not supported", resolution)
}
if _, ok := Markets[market]; !ok {
return nil, fmt.Errorf("market %s not supported", market)
} // new http.Client
var client = &http.Client{
Timeout: 5 * time.Second,
} // new http.Request
var request, err1 = http.NewRequest(http.MethodGet, fmt.Sprintf(BingAPI, index, market), nil)
if err1 != nil {
return nil, fmt.Errorf("http.NewRequest error: %s", err1)
}
request.Header.Add("Referer", "https://cn.bing.com")
request.Header.Add(
"User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36 Edg/92.0.902.84",
) // request
var resp, err2 = client.Do(request)
if err2 != nil {
return nil, fmt.Errorf("client.Do error: %s", err2)
}
defer resp.Body.Close() // parse body
// nice etree!!! (github.com/beevik/etree)
var body, err3 = ioutil.ReadAll(resp.Body)
if err3 != nil {
return nil, fmt.Errorf("ioutil.ReadAll error: %s", err3)
}
var doc = etree.NewDocument()
if err4 := doc.ReadFromBytes(body); err4 != nil {
return nil, fmt.Errorf("ReadFromBytes error: %s", err4)
} // return Response
img := doc.SelectElement("images").SelectElement("image")
return &Response{
SDate: img.SelectElement("startdate").Text(),
EDate: img.SelectElement("enddate").Text(),
URL: fmt.Sprintf("%s%s_%s", BingURL, img.SelectElement("urlBase").Text(), Resolution[resolution]),
Copyright: img.SelectElement("copyright").Text(),
CopyrightLink: img.SelectElement("copyrightlink").Text(),
}, nil
}

xmlhandler/response.go

定义了Response结构体,作为程序内部首次请求的返回格式。

package xmlhandler

// definition of struct Response
type Response struct {
SDate string `json:"sdate"`
EDate string `json:"edate"`
URL string `json:"url"`
Copyright string `json:"copyright"`
CopyrightLink string `json:"copyright_link"`
}

形成JSON

roothandler/roothandler.go

package roothandler

import (
"Bing-Wallpaper-RESTful/xmlhandler"
"net/http"
"strconv" "github.com/gin-gonic/gin"
) // RootHandler: serve as default API using gin
func RootHandler(c *gin.Context) {
// set default query params
resolution := c.DefaultQuery("resolution", "1920")
format := c.DefaultQuery("format", "json")
index := c.DefaultQuery("index", "0")
market := c.DefaultQuery("mkt", "zh-CN") // check index
uIndex, err := strconv.ParseUint(index, 10, 64)
if err != nil {
// index invalid
c.String(http.StatusInternalServerError, "image index invalid")
return
} // check format
if format != "json" && format != "image" {
c.String(http.StatusInternalServerError, "format invalid, only `json` or `image` available")
return
} // fetch info from Bing using xmlhandler
response, err := xmlhandler.Get(uint(uIndex), market, resolution)
if err != nil {
c.String(http.StatusInternalServerError, err.Error())
return
} // redirect to image URL directly
if format == "image" && response.URL != "" {
c.Redirect(http.StatusTemporaryRedirect, response.URL)
return
}
// render response as JSON
c.JSON(http.StatusOK, response)
}

命令行设计

command/command.go

规定命令行参数并定义具体执行函数。

package command

import (
"Bing-Wallpaper-RESTful/roothandler"
"fmt" "github.com/gin-gonic/gin"
"github.com/spf13/cobra"
"github.com/thinkerou/favicon"
) // command-line
var Cmd = &cobra.Command{
Use: "run",
Short: "Run this API service",
Long: `Run this API service`,
Run: run,
} func run(cmd *cobra.Command, args []string) {
gin.SetMode(gin.ReleaseMode)
router := gin.Default()
router.Use(favicon.New("./image/favicon.ico"))
router.GET("/", roothandler.RootHandler)
router.Run(":9002") // default port: 9002
fmt.Println("API is running...")
}

主函数

main.go

package main

import (
"Bing-Wallpaper-RESTful/command"
"fmt"
"os" "github.com/spf13/cobra"
) var argVerbose bool
var rootCmd *cobra.Command func init() {
rootCmd = &cobra.Command{
Use: "bw",
Short: "Bing wallpaper API",
Long: "Top level command for Bing wallpaper API service",
}
rootCmd.PersistentFlags().BoolVarP(&argVerbose, "verbose", "v", false, "verbose output")
rootCmd.AddCommand(
command.Cmd,
)
} func main() {
if err1 := rootCmd.Execute(); err1 != nil {
fmt.Println(err1)
os.Exit(1)
}
}

0x02 程序试用

打包和帮助

command.go中的run函数对Web框架gin进行了设置,将作为发行版进行打包,仅对根路径/进行服务,接受GET,占用端口9002。

虽然Golang图形库比较欠缺发展,但是命令行的格式化库确实很优雅。

go build -o bw.exe

运行

bw run

同时可以看到gin的日志信息。

CSS引入

测试CSS导入,format要设置为image。运行Live Server。

0x03 Docker

FROM golang:alpine

ENV GO111MODULE=on \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64 \
GOPROXY="https://goproxy.cn,direct" RUN mkdir -p /home/www/bw
WORKDIR /home/www/bw COPY . . RUN go build -o bw . WORKDIR /dist RUN cp /home/www/bw/bw . RUN mkdir image . RUN cp /home/www/bw/image/favicon.ico ./image/favicon.ico
# server port
EXPOSE 9002 # run
CMD ["/dist/bw", "run"]

由于有favicon.ico这一静态资源,所以需要注意复制。打包二进制时默认不会嵌入静态资源,需要使用第三方包(大量可选)或者1.16版本新增的embed功能进行嵌入,不然只能如上复制一遍资源。

用官方镜像打包使得体积过大了。

Bing每日壁纸的RESTful接口实现的更多相关文章

  1. 【开源小软件 】Bing每日壁纸 让桌面壁纸保持更新

    发布一个开源小软件,Bing每日壁纸. 该小软件可以自动获取Bing的精美图片设置为壁纸,并且支持随机切换历史壁纸,查看壁纸故事. 欢迎大家下载使用,点star!有问题请留言或者提issue. 开源地 ...

  2. 获取Bing每日壁纸用作首屏大图

    获取Bing每日壁纸用作首屏大图 Bing 搜索每天都会更换一张精美的图片作为壁纸,除了特殊时候不太好看外(比如春节那几天),没多大问题.移动端还有上每日故事,与图片现配.现在我的博客首屏图片就是Bi ...

  3. 上班从换一张桌面壁纸开始——开源小工具Bing每日壁纸

    发布一个自用的开源小软件,Bing每日壁纸,使用c# winform开发.该小软件可以自动获取Bing的精美图片设置为壁纸,并且支持随机切换历史壁纸,查看壁纸故事. 功能特性 自动获取Bing最新图片 ...

  4. DzzOffice添加动态壁纸例子-Bing每日壁纸

    Bing每日壁纸介绍:bing网站每天会更新一张不同的精选图片. 此压缩包内的程序,可以自动同步更新cn.bing.com网站每天更新的图片,作为dzzoffice的壁纸使用.实现自动每天更换不同的云 ...

  5. 【开源小软件 】Bing每日壁纸 V1.2.1

    Bing每日壁纸发布V1.2版本,下载地址Release V1.2.1 该小软件可以自动获取Bing的精美图片设置为壁纸,并且支持随机切换历史壁纸,查看壁纸故事. 本次新增国际化支持,以及桌面widg ...

  6. 如何获取 bing 每日壁纸(超高清版)

    目录 需求描述 实现方式 简单粗暴 如何下载 如何更高清 排坑指南 初级 优点 给有好奇心的孩子 进阶 接口 自动保存 网站集成 爬虫 需求描述 必应作为一个在壁纸圈做搜索引擎最优秀的站点,其每日壁纸 ...

  7. Bing每日壁纸API

    懒人直接出图 https://www.shadow-forum.com/api/bing/bing.php API API地址: https://bing.biturl.top 调用方式: HTTP ...

  8. 一个爬取Bing每日壁纸的python脚本

    1. 背景 Bing搜索每天的背景图片有些比较适合做桌面,但是有的提供下载有的不提供下载.每天去点击下载又不太方便,所以第一次学习了一下python爬虫怎么写,写的很简单. 2. 相关技术 2.1 P ...

  9. 获取Bing每日图片API接口

    bing图片每日更新,对于这一点感觉挺不错的,如果能够把bing每日图片作为博客背景是不是很不错呢?首先我们进入Bing首页,会发现自动转到中国版.不过这没关系,中国版更符合国情,速度也比国际版快一些 ...

随机推荐

  1. 三分钟入门 InnoDB 存储引擎中的表锁和行锁

    各位对 "锁" 这个概念应该都不是很陌生吧,Java 语言中就提供了两种锁:内置的 synchronized 锁和 Lock 接口,使用锁的目的就是管理对共享资源的并发访问,保证数 ...

  2. Spring 框架中都用到了哪些设计模式

    Spring 框架中都用到了哪些设计模式? Spring 框架中使用到了大量的设计模式,下面列举了比较有代表性的: 1.代理模式-在 AOP 和 remoting 中被用的比较多. 2.单例模式:在 ...

  3. springmvc学习指南 之---第27篇 spring如何实现servlet3.0无web.xml 配置servlet对象的

    writedby 张艳涛 基于web.xml配置,有人说麻烦,tomcat给按照servlet3.0,实现了基于注解@WebServlet,有人说springmvc的springmvc.xml配置麻烦 ...

  4. vue 跨域 springCloud @CrossOrigin注解

    vue 跨域  springCloud @CrossOrigin注解 一丶什么是跨域 跨域问题来源于浏览器的同源策略,浏览器为了提高网站的安全性,在发送ajax请求时,只有在当前页面地址与请求地址的协 ...

  5. ASPX页面传参中文乱码处理

    前端 function() { var msg='这是一段中文参数'; window.location.href="New.aspx?name="+escape(msg); } 后 ...

  6. QGIS打印布局中绘制多个子图

    QGIS如何绘制多图 数据准备 这是一份英国大曼彻斯特地区的数据,里面包含了教育.收入.人口密度.绿地比例.城市比例等数据,我们准备把这些数据在地图上呈现出来,为此,我们需要做在地图中绘制6幅子图,这 ...

  7. shell的图形排列

    目录 一.矩形 二.直角三角形 三.倒直角三角形 四.靠右的直角三角形 五.等腰三角形 六.平行四边形 七.等腰梯形 八.菱形 九.可变动菱形 一.矩形 二.直角三角形 三.倒直角三角形 四.靠右的直 ...

  8. zookeeper的简介和相关命令操作

    .personSunflowerP { background: rgba(51, 153, 0, 0.66); border-bottom: 1px solid rgba(0, 102, 0, 1); ...

  9. UNIX环境高级编程APUE练习3.2-不用fcntl实现dup2的功能

    1 题面 编写与dup2功能相同的函数,要求不调用fcntl函数,并且要有正确的出错处理. 2 基本思路 不能用fcntl,能够返回一个文件描述符的只有open和dup.而open会创建一个新的文件表 ...

  10. Docker部署Mysql实践

    前言:由于Docker部署容器时,没有指定IP,当机器重启后,容器的IP会变化,所以在创建容器的时候,最好能固定IP:同时,在Ubuntu系统中,每次执行命令,都需要root权限,命令需要加sudo标 ...