Go(六)来来来,教你怎么远程调用

Go(六)来来来,教你怎么远程调用

作者:lomtom

个人网站:lomtom.cn 🔗

个人公众号:博思奥园 🔗

你的支持就是我最大的动力。

Go系列:

  1. Go(一)基础入门
  2. Go(二)结构体
  3. Go(三)Go配置文件
  4. Go(四)Redis还不会使用?
  5. Go(五)Go不知道怎么用Gorm?
  6. Go(六)来来来,教你怎么远程调用
  7. Go(七)你说你不会并发?
  8. Go(八)还不知道函数式选项模式?

每个微服务应用难免会有远程调用,那么在JAVA里面,有很多种远程调用的方法,最基础的手写HTTP调用,或者使用restTetmplate,再到使用openfeign仅仅写个接口就可以实现调用。

那么在Go语言里,Go也提供了Http调用的包net/http,或者使用http组件(gentleman 🔗grequests 🔗heimdall 🔗等)或者使用更高级的RPC调用,也有rpc框架(GRPC)。

那么在本章节探讨如何在Go语言里实现HTTP调用。

接口定义

首先进行接口的定义,方便后续进行远程调用。

这里以JAVA为例,以最常见的User实体,来编写相对应的操作。

例如:

GET 查询列表
GET 查询单个用户
POST 保存用户
PUT 修改单个用户
DELETE 删除单个用户

实体类

@Data
@ApiModel("用户实体")
@NoArgsConstructor
public class User {
    @ApiModelProperty(notes = "用户编号", example = "000001")
    private Long id;

    @NotEmpty(message = "用户名不能为空")
    @ApiModelProperty(notes = "用户名", example = "小欧")
    private String username;

    @NotEmpty(message = "密码不能为空")
    @ApiModelProperty(notes = "密码", example = "123456")
    private String password;

    @NotEmpty(message = "手机号不能为空")
    @ApiModelProperty(notes = "手机号", example = "110")
    private String telNew;

    @TableLogic
    @ApiModelProperty(notes = "是否删除", example = "true")
    private Boolean deleted;

    public User(Long id, String username, String password, String telNew, Boolean deleted) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.telNew = telNew;
        this.deleted = deleted;
    }
}

接口

其次,编写相应接口

@Api(tags = "用户管理")
@RestController
@RequestMapping("user")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class UserController {

    private final UserService userService;

    private final IdGen idGen;

    @ApiOperation("查看用户列表")
    @GetMapping
    public Result list(@RequestParam(required = false) Integer page,@RequestParam(required = false) Integer pageSize) {
        Page<User> userPage = new Page<>();
        userPage.setCurrent(page==null?1:page);
        userPage.setSize(pageSize==null?10:pageSize);
        Page<User> page1 = userService.page(userPage);
        return Result.ok("page", page1);
    }

    @ApiOperation("获取单个用户信息")
    @GetMapping("/{id}")
    public Result info(@PathVariable Long id) {
        User user = userService.getById(id);
        return Result.ok("user", user).put("id", id);
    }

    @ApiOperation("保存用户信息")
    @PostMapping
    public Result save(@RequestBody @Validated User user) {
        long id = idGen.nextId();
        user.setId(id);
        boolean save = userService.save(user);
        return Result.ok("save", save).put("id",id);
    }

    @ApiOperation("更新用户信息")
    @PutMapping
    public Result update(@RequestBody @Validated User user) {
        boolean update = userService.updateById(user);
        return Result.ok("update", update);
    }

    @ApiOperation("删除用户信息")
    @DeleteMapping("/{id}")
    public Result delete(
            @PathVariable
            @NotNull(message = "不能为空")
            Long id) {
        boolean remove = userService.removeById(id);
        return Result.ok("remove", remove).put("id",id);
    }
}

Http请求调用

基本的GET请求

// 基本的GET请求
func TestHttp(t *testing.T) {
	res, err := http.Get("http://localhost:8080/user")
	if err != nil {
		log.Fatalf("did not connect: %v", err)
		return
	}
	defer func(Body io.ReadCloser) {
		err := Body.Close()
		if err != nil {
			log.Fatalf("did not close: %v", err)
			return
		}
	}(res.Body)
	if res.StatusCode == 200 {
		log.Printf("success")
		name, err := ioutil.ReadAll(res.Body)
		if err != nil {
			log.Fatalf("could not greet: %v", err)
		}
		log.Printf("Greeting: %s", name)
	}
}

结果:

2021/09/07 18:17:38 Greeting: 
{
	"status":10020000,
	"resMsg":"请求成功",
	"data":{
		"page":{
		"records":[
			{
				"id":1,
				"username":"lomtom",
				"password":"123345",
				"telNew":"110",
				"deleted":false
			},{
				"id":2,
				"username":"小菜",
				"password":"123456",
				"telNew":"112",
				"deleted":false
				}],
		"total":2,
		"size":10,
		"current":1,
		"orders":[],
		"hitCount":false,
		"searchCount":true,
		"pages":1
		}
	}
}

GET请求 携带参数

若要在GET请求后携带参数,可以选择在URL后面进行拼接,当然也可以采用以下方式。

// GET请求 携带参数 (除了拼接)
func TestHttp1(t *testing.T) {
	params := url.Values{}
	Url, err := url.Parse("http://localhost:8080/user")
	if err != nil {
		return
	}
	params.Set("page", "2")
	params.Set("pageSize", "10")
	Url.RawQuery = params.Encode()
	urlPath := Url.String()
	res, err := http.Get(urlPath)
	if err != nil {
		log.Fatalf("did not connect: %v", err)
		return
	}
	defer func(Body io.ReadCloser) {
		err := Body.Close()
		if err != nil {
			log.Fatalf("did not close: %v", err)
			return
		}
	}(res.Body)
	if res.StatusCode == 200 {
		log.Printf("success")
		name, err := ioutil.ReadAll(res.Body)
		if err != nil {
			log.Fatalf("could not greet: %v", err)
		}
		log.Printf("Greeting: %s", name)
	}
}

结果:

2021/09/07 18:17:38 Greeting: 
{
  "status": 10020000,
  "resMsg": "请求成功",
  "data": {
    "page": {
      "records": [
        {
          "id": 1,
          "username": "lomtom",
          "password": "123345",
          "telNew": "110",
          "deleted": false
        },
        {
          "id": 2,
          "username": "小菜",
          "password": "123456",
          "telNew": "112",
          "deleted": false
        }
      ],
      "total": 2,
      "size": 10,
      "current": 1,
      "orders": [],
      "hitCount": false,
      "searchCount": true,
      "pages": 1
    }
  }
}

基本的POST请求(form)

注意:该方法对应的接口接受类型必须也为表单接收,那么在java的接口需要去掉save方法里的@RequestBody注解,否则将调用失败。

// 基本的POST请求 form
func TestHttp2(t *testing.T) {
	params := url.Values{}
	params.Set("username", "小小")
	params.Set("password", "123456")
	params.Set("telNew", "112")
	res, err := http.PostForm("http://localhost:8080/user", params)
	if err != nil {
		log.Fatalf("did not connect: %v", err)
		return
	}
	defer func(Body io.ReadCloser) {
		err := Body.Close()
		if err != nil {
			log.Fatalf("did not close: %v", err)
			return
		}
	}(res.Body)
	if res.StatusCode == 200 {
		log.Printf("success")
		name, err := ioutil.ReadAll(res.Body)
		if err != nil {
			log.Fatalf("could not greet: %v", err)
		}
		log.Printf("Greeting: %s", name)
	}
}

结果:

2021/09/07 18:22:12 Greeting: 
{
  "status": 10020000,
  "resMsg": "请求成功",
  "data": {
    "save": true,
    "id": 1435186634277519360
  }
}
2021-09-07 18:22:12.748 DEBUG 16028 --- [nio-8080-exec-2] c.l.d.mapper.UserMapper.insert           : ==>  Preparing: INSERT INTO user ( id, username, password, tel_new ) VALUES ( ?, ?, ?, ? )
2021-09-07 18:22:12.748 DEBUG 16028 --- [nio-8080-exec-2] c.l.d.mapper.UserMapper.insert           : ==> Parameters: 1435186634277519360(Long), 小小(String), 123456(String), 112(String)
2021-09-07 18:22:12.752 DEBUG 16028 --- [nio-8080-exec-2] c.l.d.mapper.UserMapper.insert           : <==    Updates: 1

POST请求(body)

该方法为将参数放在请求的body部分。

  1. 同样需要注意的是,在java的接口需要加上save方法里的@RequestBody注解,否则将调用失败。
  2. 请求参数不能是自定义的结构体,必须是map类型,否则接口将匹配失败,猜测与自定义结构体的转换有关。
  3. 需要设置请求头部参数resq.Header.Set("Content-Type", "application/json")
// POST请求 body
func TestHttp3(t *testing.T) {
	//params := struct {
	//	username, password, telNew string
	//}{"小小", "123456", "112"}
	//str, err := json.Marshal(params)
	//使用结构体 接口匹配不上

	params := make(map[string]interface{})
	params["username"] = "小小"
	params["password"] = "123456"
	params["telNew"] = "112"
	str, err := json.Marshal(params)
	resq, err := http.NewRequest("POST","http://localhost:8080/user", bytes.NewBuffer(str))
	if err != nil {
		log.Fatalf("error: %v", err)
		return
	}
	resq.Header.Set("Content-Type", "application/json")
	res, err := http.DefaultClient.Do(resq)
	if err != nil {
		log.Fatalf("did not connect: %v", err)
		return
	}
	defer func(Body io.ReadCloser) {
		err := Body.Close()
		if err != nil {
			log.Fatalf("did not close: %v", err)
			return
		}
	}(res.Body)
	if res.StatusCode == 200 {
		log.Printf("success")
		name, err := ioutil.ReadAll(res.Body)
		if err != nil {
			log.Fatalf("could not greet: %v", err)
		}
		log.Printf("Greeting: %s", name)
	}
}

结果:

2021/09/08 16:05:30 Greeting: 
{
  "status": 10020000,
  "resMsg": "请求成功",
  "data": {
    "save": true,
    "id": 1435514618490388480
  }
}
2021-09-08 16:05:30.857 DEBUG 6100 --- [nio-8080-exec-6] c.l.d.mapper.UserMapper.insert           : ==>  Preparing: INSERT INTO user ( id, username, password, tel_new ) VALUES ( ?, ?, ?, ? )
2021-09-08 16:05:30.873 DEBUG 6100 --- [nio-8080-exec-6] c.l.d.mapper.UserMapper.insert           : ==> Parameters: 1435514618490388480(Long), 小小(String), 123456(String), 112(String)
2021-09-08 16:05:30.879 DEBUG 6100 --- [nio-8080-exec-6] c.l.d.mapper.UserMapper.insert           : <==    Updates: 1

PUT 请求

对于net/http,暂时提供了两种接口GetPost,那么对于PUTDELETE的实现,就需要使用它的高级功能了。

  1. 首先使用http.NewRequest申明一个新的请求,设置相对应的请求方式、url、参数等
  2. 再使用http.DefaultClient.Do(resq)发起请求即可
// PUT 请求
func TestHttp4(t *testing.T) {
	//params := struct {
	//	id,username, password, telNew string
	//}{"1435514618490388480""小小", "123456", "112"}
	//str, err := json.Marshal(params)
	//使用结构体 接口匹配不上

	params := make(map[string]interface{})
	params["id"] = "1435514618490388480"
	params["username"] = "小道科"
	params["password"] = "123456"
	params["telNew"] = "112"
	str, err := json.Marshal(params)
	resq, err := http.NewRequest("PUT","http://localhost:8080/user", bytes.NewBuffer(str))
	if err != nil {
		log.Fatalf("error: %v", err)
		return
	}
	resq.Header.Set("Content-Type", "application/json")
	res, err := http.DefaultClient.Do(resq)
	if err != nil {
		log.Fatalf("did not connect: %v", err)
		return
	}
	defer func(Body io.ReadCloser) {
		err := Body.Close()
		if err != nil {
			log.Fatalf("did not close: %v", err)
			return
		}
	}(res.Body)
	if res.StatusCode == 200 {
		log.Printf("success")
		name, err := ioutil.ReadAll(res.Body)
		if err != nil {
			log.Fatalf("could not greet: %v", err)
		}
		log.Printf("Greeting: %s", name)
	}
}

结果:

2021/09/08 16:09:47 Greeting:
{
  "status": 10020000,
  "resMsg": "请求成功",
  "data": {
    "update": true
  }
}
2021-09-08 16:14:51.785 DEBUG 4380 --- [nio-8080-exec-9] c.l.d.mapper.UserMapper.updateById       : ==>  Preparing: UPDATE user SET username=?, password=?, tel_new=? WHERE id=? AND deleted=0
2021-09-08 16:14:51.785 DEBUG 4380 --- [nio-8080-exec-9] c.l.d.mapper.UserMapper.updateById       : ==> Parameters: 小道科(String), 123456(String), 112(String), 1435514618490388480(Long)
2021-09-08 16:14:51.787 DEBUG 4380 --- [nio-8080-exec-9] c.l.d.mapper.UserMapper.updateById       : <==    Updates: 1

DELETE请求

func TestHttp5(t *testing.T) {
	resq, err := http.NewRequest("DELETE","http://localhost:8080/user/1435514618490388480",nil)
	if err != nil {
		log.Fatalf("error: %v", err)
		return
	}
	res, err := http.DefaultClient.Do(resq)
	if err != nil {
		log.Fatalf("did not connect: %v", err)
		return
	}
	defer func(Body io.ReadCloser) {
		err := Body.Close()
		if err != nil {
			log.Fatalf("did not close: %v", err)
			return
		}
	}(res.Body)
	if res.StatusCode == 200 {
		log.Printf("success")
		name, err := ioutil.ReadAll(res.Body)
		if err != nil {
			log.Fatalf("could not greet: %v", err)
		}
		log.Printf("Greeting: %s", name)
	}
}

结果:

2021/09/08 16:14:14 Greeting:
{
  "status": 10020000,
  "resMsg": "请求成功",
  "data": {
    "remove": true
  }
}
2021-09-08 16:14:14.400 DEBUG 4380 --- [nio-8080-exec-5] c.l.d.mapper.UserMapper.deleteById       : ==>  Preparing: UPDATE user SET deleted=1 WHERE id=? AND deleted=0
2021-09-08 16:14:14.400 DEBUG 4380 --- [nio-8080-exec-5] c.l.d.mapper.UserMapper.deleteById       : ==> Parameters: 1435514618490388480(Long)
2021-09-08 16:14:14.404 DEBUG 4380 --- [nio-8080-exec-5] c.l.d.mapper.UserMapper.deleteById       : <==    Updates: 1
  1. 如果需要自定义请求头、自定义方法,都可以使用http.NewRequest http.DefaultClient.Do(resq)来完成。
  2. 如需设置Header参数,可采用resq.Header.Set("Content-Type", "application/json")(参考POST请求)
lomtom

标题:Go(六)来来来,教你怎么远程调用

作者:lomtom

链接:https://lomtom.cn/34e663c0