zl程序教程

您现在的位置是:首页 >  其他

当前栏目

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

2023-04-18 16:14:45 时间

每个微服务应用难免会有远程调用,那么在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.NewRequesthttp.DefaultClient.Do(resq)来完成。
  2. 如需设置Header参数,可采用resq.Header.Set("Content-Type", "application/json")(参考POST请求)