自定义 RPC框架1——HttpClient实现RPC

2023年7月15日11:08:30

HttPClient简介

在JDK中java.net包下提供了用户HTTP访问的基本功能,但是它缺少灵活性或许多应用所需要的功能。

HttpClient起初是Apache Jakarta Common 的子项目。用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本。2007年成为顶级项目。

通俗解释:HttpClient可以实现使用Java代码完成标准HTTP请求及响应。

代码实现

整体代码架构

自定义 RPC框架1——HttpClient实现RPC

  • server端是服务端提供远程调用方法
  • client是客户端,访问远程方法
  • pojo放通用的实体类

Pojo(实体类)

代码结构

自定义 RPC框架1——HttpClient实现RPC

user类

package com.shen.pojo;

import java.io.Serializable;
import java.util.Objects;

public class User implements Serializable {
    private String username;
    private String password;

    public User(){
    }

    public User(String username,String password){
        this.username = username;
        this.password = password;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;
        User user = (User) o;
        return getUsername().equals(user.getUsername()) && getPassword().equals(user.getPassword());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getUsername(), getPassword());
    }

    @Override
    public String toString() {
        return "{" +
                "\"username\":\"" + username + '\"' +
                ", \"password\":\"" + password + '\"' +
                '}';
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

server端(服务端)

代码结构

自定义 RPC框架1——HttpClient实现RPC

server端没有修改端口号,使用默认8080端口提供服务

控制器TestController

package com.example.server.controller;

import com.shen.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

@Controller
public class TestController {
    /**
     * CrossOrigin跨域请求注解,在响应头上增加跨域处理允许,可以让ajax跨域请求当前的服务方法
     * 如果用于注解类型,必须是控制器,代表当前控制器中所以方法,都允许跨域访问
     * @param users
     * @return
     */
    //使用请求体传递参数
    @RequestMapping(value = "/bodyParams",produces = {"application/json;charset=UTF-8"})
    @ResponseBody
    @CrossOrigin
    public String bodyParams(@RequestBody List<User> users) {
        System.out.println(users);
        return users.toString();
    }

    //无参方法
    @RequestMapping(value = "/test",produces = {"application/json;charset=UTF-8"})
    @ResponseBody
    public String test(){
        return  "{\"msg\":\"返回成功\"}";
    }

    //有参方法
    @RequestMapping(value = "/params",produces = {"application/json;charset=UTF-8"})
    @ResponseBody
    public String params(String username,String password){
        System.out.println("name:"+username+";password:"+password);
        return  "{\"msg\":\"登录成功\",\"username\":\""+username+"\",\"password\":\""+password+"\"}";
    }
}

启动器(ServerApplication)

package com.example.server;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServerApplication.class, args);
    }

}

导入pojo依赖

<dependency>
  <groupId>com.example</groupId>
  <artifactId>pojo</artifactId>
  <version>0.0.1-SNAPSHOT</version>
</dependency>

客户端(client)

代码结构

自定义 RPC框架1——HttpClient实现RPC

测试方法(TestHttpClient)

包含有参,无参的get和post请求测试,以及传输实体类的post的请求测试,启动server端后,直接启动main方法即可。

package com.shen.client;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.shen.pojo.User;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;

public class TestHttpClient {
    public static void main(String[] args) throws IOException, URISyntaxException {
        testGetNoParams();
        testGetParams();
        testPost();
    }

    /**
     * 无参数的get请求
     * 使用浏览器,访问网站的过程是
     * 1。打开浏览器
     * 2,输入地址
     * 3。访问
     * 4,看结果
     * 使用httpclient访问web服务的过程
     * 1,创建客户端,相当于打开浏览器
     * 2,创建请求地址,相当于输入地址
     * 3,发起请求,相当于访问网站
     * 4。处理响应结果,相当于浏览器显示结果
     */
    public static void testGetNoParams() throws IOException {
        //创建客户端对象
        HttpClient client = HttpClients.createDefault();
        //创建请求地址
        HttpGet get = new HttpGet("http://localhost:8080/test");
        //发起请求,接收响应对象
        HttpResponse response = client.execute(get);
        //获取响应体,响应数据是一个基于HTTP协议的标准字符串封装对象
        //所以,响应体和响应头,都是封装的http协议数据,直接使用可能有乱码或解码错误
        HttpEntity entity = response.getEntity();
        //通过HTTP实体工具类,转换响应体数据,使用的字符集是utf-8
        String str = EntityUtils.toString(entity,"UTF-8");
        System.out.println("服务器返回数据:["+str+"]");
        //回收资源
        client = null;
    }

    /**
     *  有参方法get
     * @throws IOException
     */
    public static void testGetParams() throws IOException, URISyntaxException {
        HttpClient client = HttpClients.createDefault();
        //基于builder构建请求地址
        URIBuilder uriBuilder = new URIBuilder("http://localhost:8080/params");
        //基于单参数传递,构建请求地址
        /*uriBuilder.addParameter("username","shenshen");
        uriBuilder.addParameter("password","123456");*/

        //基于多参数传递,构建请求地址
        List<NameValuePair> list = new ArrayList<>();
        list.add(new BasicNameValuePair("username","shenshen1"));
        list.add(new NameValuePair() {
            @Override
            public String getName() {
                return "password";
            }

            @Override
            public String getValue() {
                return "111111";
            }
        });
        uriBuilder.addParameters(list);

        //uri请求构建
        URI uri = uriBuilder.build();
        HttpResponse response = client.execute(new HttpGet(uri));
        String res = EntityUtils.toString(response.getEntity());
        System.out.println("params方法返回:"+res);
        client = null;
    }

    /**
     * post请求
     */
    public static void testPost() throws IOException, URISyntaxException {
        HttpClient client = HttpClients.createDefault();
        //无参数post请求
        HttpPost post = new HttpPost("http://localhost:8080/test");
        String res = EntityUtils.toString(client.execute(post).getEntity(),"UTF-8");
        System.out.println("post无参请求测试:"+res);

        //有参数post请求
        //请求头传递参数,和GET请求携带参数的方式一致
        URIBuilder uriBuilder = new URIBuilder("http://localhost:8080/params");
        uriBuilder.addParameter("username","shen");
        uriBuilder.addParameter("password","123456");
        URI uri = uriBuilder.build();
        HttpResponse response = client.execute(new HttpPost(uri));
        System.out.println(EntityUtils.toString(response.getEntity(),"UTF-8"));

        //请求体传递参数
        HttpPost  bodyParamsPost = new HttpPost("http://localhost:8080/bodyParams");
        //定义请求协议体,设置请求参数。使用请求体传递参数,需要定义请求体格式,默认是表单
        //使用uribuilder构建的uri对象,就是请求体传递参数的
        User user = new User("name1","word1");
        User user2 = new User("name2","word2");
        List<User> users = new ArrayList<>();
        users.add(user);
        users.add(user2);
        //集合users->json
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(users);
        //或者拼接一个json格式字符串,表示请求参数,一个List<User>
        //这个变量和json变量作用相同
        //String paramsString = "[" + user.toString() + "," + user2.toString() + "]";
        HttpEntity entity = new StringEntity(json,"application/json","UTF-8");
        bodyParamsPost.setEntity(entity);
        String responseStr = EntityUtils.toString(client.execute(bodyParamsPost).getEntity(),"UTF-8");
        //返回json格式转对象
        //1.list里面一个元素转成user对象
        String userString = responseStr.substring(1,responseStr.indexOf("},")+1);
        User user1 = objectMapper.readValue(userString,User.class);
        System.out.println("user1:"+user1);
        //2.直接转换list,构建jackson识别的java类型映射
        JavaType valueType = objectMapper.getTypeFactory().constructParametricType(List.class,User.class);
        List<User> users1 = objectMapper.readValue(responseStr,valueType);
        System.out.println("users1:"+users1);
    }
}

前端远程调用测试(TestApp)

修改启动端口号为80

package com.shen.client;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class TestApp {
    public static void main(String[] args) {
        SpringApplication.run(TestApp.class,args);
    }
}

前端代码编写index

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src = "http://code.jquery.com/jquery-latest.js"></script>
    <script type="text/javascript">

        //js中,ajax请求不能跨域
        //跨域-ajax所属的站点,和被请求的站点,不是同一个域
        //域 -ip,端口,域名,主机名,任何一个有变化,都是不同域
        //需要服务器返回,允许跨域
        function sendBodyParams(){
            $.ajax({
                "url":"http://localhost:8080/bodyParams",
                "type":"post",
                "data":"[{\"username\":\"abc\",\"password\":\"123\"}," +
                    "{\"username\":\"def\",\"password\":\"456\"}]",
                "contentType":"application/json",//必须设定,代表请求体的格式,默认是text/plain,默认是,参数名=参数值&参数名=参数值
                "dataType":"json",
                "success":function (data){
                    alert(data);
                    console.log(data);
                }
            })
        }

    </script>
</head>
<body style="text-align: center">
    <button onclick="sendBodyParams()">测试ajax请求,请求体传递json参数</button>
</body>
</html>

导入依赖

<!-

  • 作者:想飞的yu
  • 原文链接:https://blog.csdn.net/qq_45587153/article/details/124208950
    更新时间:2023年7月15日11:08:30 ,共 8018 字。