使用Golang API获取数据的实现方法

使用Golang API获取数据的实现方法 我正在使用 Golang Gin 框架的 REST API 创建一个博客。当我通过 ID 调用博客时,博客内容可以无错误地显示在屏幕上,但当我调用数据中的所有博客时,会出现无效日期错误。

main.go:

type Blog struct {
	ID       int        `db:"id" json:"id"`
	Title    string     `db:"title" json:"title"`
	Body     string     `db:"body" json:"body"`
	ImageURL string     `db:"image" json:"image"`
	Date     *time.Time `db:"date" json:"date"`
}


// get blog by ID
	r.GET("/blog/:id", func(c *gin.Context) {
		var blog Blog
		id := c.Param("id")

		// get blog from database
		err := db.Get(&blog, "SELECT * FROM blogs WHERE id=$1", id)
		if err != nil {
			if err == sql.ErrNoRows {
				c.JSON(http.StatusNotFound, gin.H{"error": "blog not found"})
			} else {
				c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get blog"})
			}
			return
		}

		c.JSON(http.StatusOK, blog)
	})

	// get all blogs
	r.GET("/blogs", func(c *gin.Context) {
		var blogs []Blog

		// get all blogs from database
		err := db.Select(&blogs, "SELECT * FROM blogs")
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get blogs"})
			return
		}

		c.JSON(http.StatusOK, blogs)
	})

app.js:

useEffect(() => {
    async function fetchData() {
      try {
        const response = await axios.get('http://localhost:8000/blog/1'); //this is working

        setFirstPost(response.data);
      } catch (error) {
        setError(error.message);
      }
    }
    fetchData();

    async function fetchSecondData() {
      try {
        const response = await axios.get('http://localhost:8000/blogs');//not working

        setSecondPost(response.data);
      } catch (error) {
        setError(error.message);
      }
    }
    fetchSecondData();
  }, []);

  return (
    <div className="blog">
           <div className='blog-page'> 
                <h1> BLOG</h1>
            </div>

      {firstPost && (
        <div className="blog-first">
          <div className="blog-firstpage">
            <img src={firstPost.image} alt={firstPost.title} />
          </div>
          <div className="blog-text">
            <h2>{firstPost.title}</h2>
            <h3>{new Date(firstPost.date).toLocaleString('en-US', { dateStyle: 'medium' })}</h3>
            <p>{firstPost.body}</p>
          </div>
          
        </div>
      )}

      {secondPost && (
        <div className="blog">
          <div className="blog-secondpage">
            <img src={secondPost.image} alt={secondPost.title} />
          </div>
          <div className="blog-text">
            <h2>{secondPost.title}</h2>
            <h3>{new Date(secondPost.date).toLocaleString('en-US', { dateStyle: 'medium' })}</h3>
            <p>{secondPost.body}</p>
          </div>
         
        </div>
      )}

      {error && <p>{error}</p>}
    </div>
  );
}

export default Blog;

更多关于使用Golang API获取数据的实现方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

没有错误,运行正常,但显示的是无效日期。

更多关于使用Golang API获取数据的实现方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


错误是… 只需用以下代码展示:

err := db.Select(&blogs, "SELECT * FROM blogs")
if err != nil {
    log.Println("Error getting blogs:", err)
    c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get blogs"})
    return
}

但是显示了一个无效的日期

SELECT * FROM blogs 表明你可能获取了多行数据。但你的 Go 代码显示只获取了一行。并且你的模板显示没有对行进行遍历(.range)展示。

因此,你可以将你的问题分解为三个部分:

  1. 查询在 pgAdmin(或类似工具)中是否有效?有多少行数据?
  2. Go 是否正确获取了数据?
  3. 模板是否正确显示了数据?它是否正确地对行进行了迭代(.range)?

还是我遗漏了什么?

问题出现在日期字段的处理上。当查询单个博客时,数据库返回的日期字段能被正确解析为 *time.Time 类型。但查询所有博客时,如果数据库中存在 NULL 日期值,Go 会尝试将 NULL 解析为 time.Time 类型导致错误。

以下是修复方案:

// 修改 Blog 结构体,使用 sql.NullTime 处理可能为 NULL 的日期
type Blog struct {
	ID       int            `db:"id" json:"id"`
	Title    string         `db:"title" json:"title"`
	Body     string         `db:"body" json:"body"`
	ImageURL string         `db:"image" json:"image"`
	Date     sql.NullTime   `db:"date" json:"date"`
}

// 修改获取单个博客的接口,处理 NullTime
r.GET("/blog/:id", func(c *gin.Context) {
	var blog Blog
	id := c.Param("id")

	err := db.Get(&blog, "SELECT * FROM blogs WHERE id=$1", id)
	if err != nil {
		if err == sql.ErrNoRows {
			c.JSON(http.StatusNotFound, gin.H{"error": "blog not found"})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get blog"})
		}
		return
	}

	// 手动构建响应,处理日期字段
	response := map[string]interface{}{
		"id":    blog.ID,
		"title": blog.Title,
		"body":  blog.Body,
		"image": blog.ImageURL,
		"date":  nil,
	}
	
	if blog.Date.Valid {
		response["date"] = blog.Date.Time
	}

	c.JSON(http.StatusOK, response)
})

// 修改获取所有博客的接口
r.GET("/blogs", func(c *gin.Context) {
	var blogs []Blog

	err := db.Select(&blogs, "SELECT * FROM blogs")
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get blogs"})
		return
	}

	// 处理每个博客的日期字段
	var response []map[string]interface{}
	for _, blog := range blogs {
		item := map[string]interface{}{
			"id":    blog.ID,
			"title": blog.Title,
			"body":  blog.Body,
			"image": blog.ImageURL,
			"date":  nil,
		}
		
		if blog.Date.Valid {
			item["date"] = blog.Date.Time
		}
		
		response = append(response, item)
	}

	c.JSON(http.StatusOK, response)
})

或者,如果希望保持 *time.Time 类型,可以在查询时排除 NULL 值或使用 COALESCE:

// 方法二:修改 SQL 查询,处理 NULL 日期
r.GET("/blogs", func(c *gin.Context) {
	var blogs []Blog

	// 使用 COALESCE 或排除 NULL 值
	err := db.Select(&blogs, "SELECT id, title, body, image, date FROM blogs WHERE date IS NOT NULL")
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get blogs"})
		return
	}

	c.JSON(http.StatusOK, blogs)
})

前端也需要相应调整,处理可能的 null 日期:

// 在 app.js 中修改日期显示逻辑
{secondPost && secondPost.date && (
    <h3>{new Date(secondPost.date).toLocaleString('en-US', { dateStyle: 'medium' })}</h3>
)}

使用 sql.NullTime 是推荐的方法,因为它能正确处理数据库中的 NULL 值,避免解析错误。

回到顶部