服务计算第九次作业

一:负责的工作:

1:编写后端如下API:

”/“
-get API root遵照要求放回API列表
“/picture”
-get: 图片
/picture/upload
-post: 上传图片到(./picture)
"/commodities/
/commodities/{commodityName}
-get:商品详细信息
/commodities/{commodityName}/comments
-get 获取该商品所有评论
-post (表单:model.Comment) 发布
-patch (表单:model.Comment) 修改
-delete (表单:model.Comment) 删除
"/commodities
-get ;所有商品
-post : 上传商品信息
/users/{user}/cart
-在访问该路径时,需要先进行token验证:
-get 用户的购物车
-post (model.cart) 更新购物车

二:API详细介绍:

整个后端的实现分为三个层次,

  • 负责数据库MongoDB操作的db.go、
  • 负责路由的app.go
  • 以及搭建服务器和链接数据库的server.go;
  • model.go是数据json模型;
  • 所有的API实现都依照这一模式实现:
    在这里插入图片描述

1:"/"

http.HandlerFunc : writeApiRoot
返回API服务列表:通过一个map[string]string结构体记录服务列表,然后写入response返回

//Api信息
	apiStr := make(map[string]string)
	apiStr["all_commodities_url"] = "http://localhost:8080/commodities"
	apiStr["post_commoditie_url:http"] = "//localhost:8080/commodities"
	apiStr["get_commoditie_info_url"] = "http://localhost:8080/commodities/{commodity}"
	apiStr["get_comment_url"] = "http://localhost:8080/commodities/{commodity}/comments"
	apiStr["post_comment_url"] = "http://localhost:8080/commodities"
	apiStr["delete_comment_url"] = "http://localhost:8080/commodities"
	apiStr["get_alluser_url"] = "http://localhost:8080/users"
	apiStr["user_register_url"] = "http://localhost:8080/users/register"
	apiStr["get_a_user_url"] = "http://localhost:8080/users/{user}"
	apiStr["get_user_cart"] = "http://localhost:8080/users/{user}/cart"
	apiStr["update_comment_url"] = "http://localhost:8080/users/{user}/cart"
	apiStr["get_picture"] = "http://localhost:8080/picture/{picture}"
	apiStr["post_picture"] = "http://localhost:8080/picture/upload"
	//发送到根root
	err := json.NewEncoder(w).Encode(apiStr)

2: “/picture” get图片

此API是需要实现一个静态文件服务器,通过 如下代码从url映射文件夹:http.StripPrefix用来将/picture/从url中过滤出去,将文件映射到/picture/文件夹下

	app.handlers["/picture/"] = http.StripPrefix("/picture/", http.FileServer(http.Dir("./picture"))).ServeHTTP

3:"/picture/upload" 上传图片:

文件标签为image
HandlerFunc: recieveImage
关键代码:

  //获取图片文件
	f, h, err := r.FormFile("image")
	fmt.Println("recieve a img")
	//在路径./picture/下创建新文件
	filename := h.Filename
	defer f.Close()
	t, err := os.Create("./picture/" + filename)
	//将图片复制到同名新文件
	 io.Copy(t, f)

4:"/commodities"

HandlerFunc: GetCommodities
该路径下有两个API:

  • get方法: 从数据库中获取所有的商品信息并返回
  • 其它方法: 根据商品名字更新某商品的信息,如果不存在该商品则添加,如果存在则更新该商品信息。
// GetCommodities if r.Method is GET it will get all commodities, if r.Method is POST will add or update a commodity
func (a *App) GetCommodities(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	fmt.Println("get all commodities")
	//获取所有商品信息
	if r.Method == "GET" {
		//从数据库取所有商品信息的数据
		commodities, err := a.d.GetAllCommodity()
		if err != nil {
			sendErr(w, http.StatusInternalServerError, err.Error())
			return
		}
		//将信息写入response
		err = json.NewEncoder(w).Encode(commodities)
		if err != nil {
			sendErr(w, http.StatusInternalServerError, err.Error())
		}
	} else { //为商店添加新商品
		var commodity model.Commodity
		r.ParseMultipartForm(512)
		fmt.Println("Add a new commodity")
		for k, v := range r.MultipartForm.Value {
			fmt.Println("value,k,v = ", k, ",", v)
		}
		//将信息写入数据库
		commodity.Introduction = r.MultipartForm.Value["introduction"][0]
		commodity.Name = r.MultipartForm.Value["name"][0]
		commodity.Picture = r.MultipartForm.Value["picture"][0]
		//string转float64
		commodity.Price, _ = strconv.ParseFloat(r.MultipartForm.Value["price"][0], 64)
		a.d.PostCommodity(&commodity)
	}

}

其中操作数据库的方法 :a.d.GetAllCommodity()、a.d.PostCommidity() ; a是结构体App的指针,a的成员d是接口DB, 该接口定义了对数据库的所有操作函数。他们的定义如下:

//App define a app
type App struct {
	d        db.DB
	handlers map[string]http.HandlerFunc
}

//DB 对数据库的操作接口
type DB interface {
	//get获取所有商品|post:上传商品
	GetAllCommodity() ([]*model.Commodity, error)
	//
	GetOneCommodity(name string) (*model.Commodity, error)
	GetCommentsForCM(com string) ([]*model.Comment, error)
	WriteComment(comment *model.Comment)
	DeleteComment(comment *model.Comment)
	UpdateComment(comment *model.Comment)
	//
	GetUsersInfo() ([]*model.User, error)
	GetAUserInfo(string) ([]*model.User, error)
	UserRegister(string, string, float64) (*model.User, error)
	GetCart(username string) (*model.Cart, error)
	WriteCart(cart *model.Cart)
	PostCommodity(commodity *model.Commodity)

	AddToken(token *model.TokenKey)
	GetAToken(user string) (*model.TokenKey, error)
}

被调用的方法PostCommidity()就是用来更新数据库信息的,实现如下:
根据商品名选择要更新的商品,UpdateOpts为更新选项,为存在则更新,不存在则添加;
MongoDB是一个结构体,有一个唯一的成员database代表着服务器使用的数据库。

//PostCommodity update or add a commodity to the app
func (m MongoDB) PostCommodity(commodity *model.Commodity) {
	selector := bson.M{"name": commodity.Name}

	updateOpts := options.Update().SetUpsert(true)

	//data := bson.M{"$set": bson.M{"comment": comment.Comment}}
	data := bson.M{"$set": commodity}

	updateResult, err := m.database.Collection(commodityCollection).UpdateOne(context.Background(), selector, data, updateOpts)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Updateresult: ", updateResult)
}

5:"/commodities/"

该路径下有很多API,主要有两类
获取单一商品信息的操作:
-/commodities/{commoditiesname}
对该商品评论的操作:
-/commodities/{commoditiesname}/comments

需要通过正则表达式来判断是哪种具体情况:

//isComment return true if name is a comment or false is not a comment
func isComment(name string) bool {
	commentMode := regexp.MustCompile(`[A-Za-z0-9%]+/comments`)
	return commentMode.MatchString(name)

}

具体实现

// GetCommodity define all the api in the form as /commodities/
func (a *App) GetCommodity(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	//获取url“commodities/”后面的字段
	urlStr := r.URL.String()
	fmt.Println("get a commodity")
	commodityname := urlStr[len("/commodities")+1:]
	//判断是否是操作商品(/commodities/{}/comments评论的url
	if isComment(urlStr) {
		fmt.Println("Operate a commodity's comment")
		a.OperateCommentsForCM(w, r)
	} else { //请求单个商品的详细信息
		//将url编码解编码
		cName, _ := url.QueryUnescape(commodityname)
		println(cName)
		//从数据库获取信息
		commodity, err := a.d.GetOneCommodity(cName)
		if err != nil {
			sendErr(w, http.StatusInternalServerError, err.Error())
			return
		}
		//写信息
		err = json.NewEncoder(w).Encode(commodity)
		if err != nil {
			sendErr(w, http.StatusInternalServerError, err.Error())
		}
	}
}

当情况为对评论的操作,HandlerFunc : OperateCommentsForCM
该情况下实现了四种API:
分别是 GET获取 POST发布 DELETE删除 PATCH(通过用户名和商品名定位更新):Get方法是通过解析url定位信息,其余三种方法都是通过表单提取信息

// OperateCommentsForCM get post patch delete the coments of a commodity
func (a *App) OperateCommentsForCM(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" { //获取某商品的评论
		w.Header().Set("Content-Type", "application/json")
		//获取商品名
		urlStr := r.URL.String()
		println(urlStr)
		commodityname := urlStr[len("/commodities")+1 : len(urlStr)-len("/comments")]
		cName, _ := url.QueryUnescape(commodityname)
		fmt.Println("get comments for a commodity", cName)
		//从数据库取数据
		commemts, err := a.d.GetCommentsForCM(cName)
		if err != nil {
			sendErr(w, http.StatusInternalServerError, err.Error())
			return
		}
		//写数据
		err = json.NewEncoder(w).Encode(commemts)
		if err != nil {
			sendErr(w, http.StatusInternalServerError, err.Error())
		}
	} else if r.Method == "POST" { //发布新评论
		var comment model.Comment
		r.ParseMultipartForm(128)
		for k, v := range r.MultipartForm.Value {
			fmt.Println("value,k,v = ", k, ",", v)
		}
		comment.Username = r.MultipartForm.Value["username"][0]
		comment.Comment = r.MultipartForm.Value["comment"][0]
		comment.Commodity = r.MultipartForm.Value["commodity"][0]
		a.d.WriteComment(&comment)
		fmt.Println(comment)
	} else if r.Method == "DELETE" { //删除评论
		fmt.Println("Delete a commet")
		var comment model.Comment
		r.ParseMultipartForm(128)
		for k, v := range r.MultipartForm.Value {
			fmt.Println("value,k,v = ", k, ",", v)
		}
		comment.Username = r.MultipartForm.Value["username"][0]
		comment.Comment = r.MultipartForm.Value["comment"][0]
		comment.Commodity = r.MultipartForm.Value["commodity"][0]
		a.d.DeleteComment(&comment)
	} else if r.Method == "PATCH" { //修改某评论
		fmt.Println("Update a comment")
		var comment model.Comment
		r.ParseMultipartForm(128)
		comment.Username = r.MultipartForm.Value["username"][0]
		comment.Comment = r.MultipartForm.Value["comment"][0]
		comment.Commodity = r.MultipartForm.Value["commodity"][0]
		a.d.UpdateComment(&comment)
	}

}

每个API依旧是通过接口DB对数据实施操作:a.d.{}
具体代码看db.go

6:/users/{username}/cart

这里的基本实现与上面的/commodities/{commoditiesname}/comments实现大体一致,主要有获取信息的get和更新信息的post方法:
具体实现看HandlerFunc : GetAUserFunc

3:使用postman进行简单的测试:

3.1注册测试:

  • 注册得到token信息:
    在这里插入图片描述
  • 查看数据库数据是否正确写入:
  • token:记录用户和密钥
  • user :记录用户信息
  • 注册成功
    在这里插入图片描述

3.3 root API测试:

在这里插入图片描述

3.4 “/commodities” API测试:

在这里插入图片描述

3.5 "/commodities/{cname}测试:

在这里插入图片描述

3.6 "/commodities/{commodityname}/comments"测试:

1: get:

在这里插入图片描述

2:post: 发布:

在这里插入图片描述

在这里插入图片描述

3:patch:修改

在这里插入图片描述
在这里插入图片描述

4:delete:删除:

在这里插入图片描述
在这里插入图片描述

3.7: "/picture/"测试:

在这里插入图片描述
在这里插入图片描述

3.8 “/picture/upload”:

文件key要设置为image:

在这里插入图片描述
在这里插入图片描述

4:完整代码:

github仓库

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值