golang实现Medium平台OAuth2 API集成插件库Medium的使用
Golang实现Medium平台OAuth2 API集成插件库Medium的使用
Medium SDK for Go
这个仓库包含了用于将Medium的OAuth2 API集成到Go应用中的开源SDK。
安装
go get github.com/Medium/medium-sdk-go
使用
创建一个客户端,然后调用相关命令。
package main
import (
medium "github.com/medium/medium-sdk-go"
"log"
)
func main() {
// 访问 https://medium.com/me/applications 获取你的applicationId和applicationSecret
m := medium.NewClient("YOUR_APPLICATION_ID", "YOUR_APPLICATION_SECRET")
// 构建用于获取授权码的URL
url := m.GetAuthorizationURL("secretstate", "https://yoursite.com/callback/medium",
medium.ScopeBasicProfile, medium.ScopePublishPost)
// (将用户发送到授权URL以获取授权码)
// 将授权码交换为访问令牌
at, err := m.ExchangeAuthorizationCode("YOUR_AUTHORIZATION_CODE", "https://yoursite.com/callback/medium")
if err != nil {
log.Fatal(err)
}
// 成功交换后,访问令牌会自动设置在客户端上
// 但如果你已经有令牌,可以直接设置
m.AccessToken = at.AccessToken
// 如果你有自签发的访问令牌,可以跳过上述步骤
// 直接创建一个新客户端
m2 := medium.NewClientWithAccessToken("SELF_ISSUED_ACCESS_TOKEN")
// 获取由访问令牌标识的用户资料详情
// 空字符串表示当前用户,否则需要指定用户ID(65个字符的字母数字字符串)
u, err := m2.GetUser("")
if err != nil {
log.Fatal(err)
}
// 创建草稿文章
p, err := m.CreatePost(medium.CreatePostOptions{
UserID: u.ID,
Title: "Title",
Content: "<h2>Title</h2><p>Content</p>",
ContentFormat: medium.ContentFormatHTML,
PublishStatus: medium.PublishStatusDraft,
})
if err != nil {
log.Fatal(err)
}
// 当访问令牌过期时,使用刷新令牌获取新的令牌
nt, err := m.ExchangeRefreshToken(at.RefreshToken)
if err != nil {
log.Fatal(err)
}
// 确认一切正常。p.URL包含创建的文章位置
log.Println(url, at, u, p, nt)
}
贡献
欢迎提出问题、评论、错误报告和拉取请求。如果你之前没有为Medium项目贡献过代码,请前往Open Source Project并填写OCLA(应该非常简单)。
作者
Jamie Talbot
Dan Pupius
Andrew Bonventre
许可证
版权所有 2015 A Medium Corporation
根据Apache License Version 2.0授权。详细信息请参阅附带的LICENSE文件。
更多关于golang实现Medium平台OAuth2 API集成插件库Medium的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang实现Medium平台OAuth2 API集成插件库Medium的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang实现Medium平台OAuth2 API集成
Medium提供了一个OAuth2 API,允许开发者集成Medium的发布功能到自己的应用中。下面我将介绍如何使用Golang实现Medium平台的OAuth2集成。
准备工作
- 首先需要在Medium开发者页面注册你的应用,获取Client ID和Client Secret。
- 设置回调URL(redirect_uri)。
实现步骤
1. 安装必要的依赖
go get golang.org/x/oauth2
2. 配置OAuth2客户端
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"golang.org/x/oauth2"
"golang.org/x/oauth2/medium"
)
var (
oauth2Config = &oauth2.Config{
ClientID: os.Getenv("MEDIUM_CLIENT_ID"),
ClientSecret: os.Getenv("MEDIUM_CLIENT_SECRET"),
RedirectURL: "http://localhost:8080/callback",
Scopes: []string{"basicProfile", "publishPost"},
Endpoint: medium.Endpoint,
}
// 随机字符串,用于防止CSRF攻击
oauthStateString = "random"
)
3. 实现授权处理
func handleMain(w http.ResponseWriter, r *http.Request) {
var htmlIndex = `<html>
<body>
<a href="/login">Medium Log In</a>
</body>
</html>`
fmt.Fprintf(w, htmlIndex)
}
func handleLogin(w http.ResponseWriter, r *http.Request) {
url := oauth2Config.AuthCodeURL(oauthStateString)
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
}
func handleCallback(w http.ResponseWriter, r *http.Request) {
state := r.FormValue("state")
if state != oauthStateString {
fmt.Printf("invalid oauth state, expected '%s', got '%s'\n", oauthStateString, state)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return
}
code := r.FormValue("code")
token, err := oauth2Config.Exchange(context.Background(), code)
if err != nil {
fmt.Printf("oauth2Conf.Exchange() failed with '%s'\n", err)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return
}
// 使用token访问Medium API
client := oauth2Config.Client(context.Background(), token)
resp, err := client.Get("https://api.medium.com/v1/me")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer resp.Body.Close()
// 处理获取到的用户信息
// ...
fmt.Fprintf(w, "登录成功!Token: %+v", token)
}
4. 发布文章到Medium
func publishPost(client *http.Client, title, content string) error {
post := struct {
Title string `json:"title"`
Content string `json:"content"`
Format string `json:"format"`
PublishStatus string `json:"publishStatus"`
}{
Title: title,
Content: content,
Format: "markdown", // 或 "html"
PublishStatus: "draft", // 或 "public"
}
reqBody, err := json.Marshal(post)
if err != nil {
return err
}
resp, err := client.Post(
"https://api.medium.com/v1/users/me/posts",
"application/json",
bytes.NewBuffer(reqBody),
)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusCreated {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("failed to publish post: %s", body)
}
return nil
}
5. 启动HTTP服务器
func main() {
http.HandleFunc("/", handleMain)
http.HandleFunc("/login", handleLogin)
http.HandleFunc("/callback", handleCallback)
fmt.Println("Server started at http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
完整示例
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"golang.org/x/oauth2"
"golang.org/x/oauth2/medium"
)
var (
oauth2Config = &oauth2.Config{
ClientID: os.Getenv("MEDIUM_CLIENT_ID"),
ClientSecret: os.Getenv("MEDIUM_CLIENT_SECRET"),
RedirectURL: "http://localhost:8080/callback",
Scopes: []string{"basicProfile", "publishPost"},
Endpoint: medium.Endpoint,
}
oauthStateString = "random"
)
func main() {
http.HandleFunc("/", handleMain)
http.HandleFunc("/login", handleLogin)
http.HandleFunc("/callback", handleCallback)
fmt.Println("Server started at http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
func handleMain(w http.ResponseWriter, r *http.Request) {
var htmlIndex = `<html>
<body>
<a href="/login">Medium Log In</a>
</body>
</html>`
fmt.Fprintf(w, htmlIndex)
}
func handleLogin(w http.ResponseWriter, r *http.Request) {
url := oauth2Config.AuthCodeURL(oauthStateString)
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
}
func handleCallback(w http.ResponseWriter, r *http.Request) {
state := r.FormValue("state")
if state != oauthStateString {
fmt.Printf("invalid oauth state, expected '%s', got '%s'\n", oauthStateString, state)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return
}
code := r.FormValue("code")
token, err := oauth2Config.Exchange(context.Background(), code)
if err != nil {
fmt.Printf("oauth2Conf.Exchange() failed with '%s'\n", err)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return
}
client := oauth2Config.Client(context.Background(), token)
// 获取用户信息
resp, err := client.Get("https://api.medium.com/v1/me")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Fprintf(w, "User Info: %s\n", body)
// 发布文章示例
err = publishPost(client, "Hello Medium", "This is a test post from my Go application.")
if err != nil {
fmt.Fprintf(w, "Error publishing post: %v", err)
} else {
fmt.Fprintf(w, "Post published successfully!")
}
}
func publishPost(client *http.Client, title, content string) error {
post := struct {
Title string `json:"title"`
Content string `json:"content"`
Format string `json:"format"`
PublishStatus string `json:"publishStatus"`
}{
Title: title,
Content: content,
Format: "markdown",
PublishStatus: "draft",
}
reqBody, err := json.Marshal(post)
if err != nil {
return err
}
resp, err := client.Post(
"https://api.medium.com/v1/users/me/posts",
"application/json",
bytes.NewBuffer(reqBody),
)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusCreated {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("failed to publish post: %s", body)
}
return nil
}
注意事项
- 确保环境变量
MEDIUM_CLIENT_ID
和MEDIUM_CLIENT_SECRET
已正确设置 - 在生产环境中,应该使用更安全的随机字符串作为state参数
- 考虑使用会话管理来存储token
- 处理token过期和刷新逻辑
- 错误处理应该更加完善
这个示例提供了Medium OAuth2集成的基本框架,你可以根据需要扩展功能,如获取用户所有文章、更新已有文章等。