每个微服务应用难免会有远程调用,那么在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);
}
}
// 基本的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
请求后携带参数,可以选择在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
}
}
}
注意:该方法对应的接口接受类型必须也为表单接收,那么在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
该方法为将参数放在请求的body
部分。
save
方法里的@RequestBody
注解,否则将调用失败。map
类型,否则接口将匹配失败,猜测与自定义结构体的转换有关。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
对于net/http
,暂时提供了两种接口Get
和Post
,那么对于PUT
和DELETE
的实现,就需要使用它的高级功能了。
http.NewRequest
申明一个新的请求,设置相对应的请求方式、url
、参数等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
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
http.NewRequest
和http.DefaultClient.Do(resq)
来完成。Header
参数,可采用resq.Header.Set("Content-Type", "application/json")
(参考POST请求)