【java学习】HTTP接口调用:Httpclient、Okhttp、HttpURLConnection、RestTemplate 和 Feign

1,概念

1)REST API规范

2)Spring MVC常用注解

2,java对象

1)Request对象

1>类图

javax.servlet.ServletRequest	--	父接口
		|	继承
javax.servlet.http.HttpServletRequest	-- 接口   表示请求
		|	实现
org.apache.catalina.connector.RequestFacade 类(tomcat)

2>工作机制及生命周期

HttpServletRequest 实例对象是什么时候创建和销毁的呢?

  1. client请求server;
  2. server根据http协议格式解析请求内容;创建请求对象: HttpServletRequest 的实现类 RequestFacade 的对象
  3. 通过set方法,将解析出的数据封装到请求对象中, HttpServletRequest 实例初始化完毕。
  4. 建立响应对象,server向client发送响应。
  5. 销毁HttpServletRequest 实例对象。

3>使用

//方法参数声明HttpServletRequest对象,会自动接收到HttpServletRequest请求数据。
@GetMapping
public String get(HttpServletRequest request){
   //获取客户端的IP地址
   //如果使用了反向代理那么拿到的数据是127.0.0.1或 192.168.1.110;
   request.getRemoteAddr();
   //如果使用了路由转发那么拿到的是转发服务的ip(服务器ip),此时可以使用HuTool的工具类拿到真实客户端ip
	 ServletUtil.getClientIP(request);
}

hutool工具类:

<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.3.5</version>
</dependency>

4>常用api

举例:当前请求 GET /day14/demo1?name=zhangsan HTTP/1.1

1.属性获取

方法说明备注
String getMethod()获取请求方式 :GET
String getContextPath()获取虚拟目录:/day14
String getServletPath()获取Servlet路径: /demo1
String getQueryString()获取get方式请求参数:name=zhangsan
String getProtocol()获取协议及版本:HTTP/1.1
String getRemoteAddr()获取客户机的IP地址注意如果反向代理或者路由转发等无法获取到客户机ip地址,见上文
String getParameter(String name)根据参数名称获取参数值
String[] getParameterValues(String name)根据参数名称获取参数值的数组
Enumeration getParameterNames()获取所有请求的参数名称
Map<String,String[]> getParameterMap()获取所有参数的map集合
  1. header内容获取
方法说明备注
String getHeader(String name)通过请求头的名称获取请求头的值
Enumeration getHeaderNames()获取所有的请求头名称
  1. body请求体获取,只有post方法支持
方法说明备注
BufferedReader getReader()获取字符输入流,只能操作字符数据
ServletInputStream getInputStream()获取字节输入流,可以操作所有类型数据
  1. 请求转发(路由转发)
    一种在服务器内部的资源跳转方式(路由转发,区别于网址重定向)
    特点:
    a. 浏览器地址栏路径不发生变化
    b. 转发只能访问当前服务器下的资源
    c. 转发是一次请求,可以使用request对象来共享数据
方法说明备注
RequestDispatcher getRequestDispatcher(String path)通过request对象获取请求转发器对象路由跳转、请求转发
forward(ServletRequest request, ServletResponse response)使用RequestDispatcher对象来进行转发路由跳转、请求转发
  1. 共享数据:

域对象:
一个有作用范围的对象,可以在范围内共享数据

request域
代表一次请求的范围,一般用于请求转发的多个资源中共享数据

方法说明备注
void setAttribute(String name,Object obj)存储数据
Object getAttitude(String name)通过键获取值
void removeAttribute(String name)通过键移除键值对

5>中文乱码问题

  1. get方式:tomcat 8 已经将get方式乱码问题解决了
  2. post方式:会乱码
    解决:在获取参数前,设置request的编码request.setCharacterEncoding("utf-8");

2)Response对象

1>使用

//方法参数直接声明HttpServletResponse 即可以操作响应对象
public FreeLoginUrlVo freeLogin(HttpServletResponse response) {
        String url = "https://xxx";
        try {
        		//url重定向 传递302消息给浏览器 请求参数将失效
            response.sendRedirect(url);
        } catch (IOException e) {
            log.error("免登录发生错误,请检查当前免登录配置和第三方设备配置", e);
        }
        return freeLoginUrlVo;
    }

2>常见API

  1. 设置响应消息
方法说明备注
setStatus(int sc)设置状态码
setHeader(String name, String value)设置响应头
  1. 设置响应体
方法说明备注
PrintWriter getWriter()获取字符输出流
ServletOutputStream getOutputStream()获取字节输出流
  1. 重定向
    特点:
  2. 传递302消息给浏览器,地址栏发生变化
  3. 请求参数将失效
  4. 重定向是两次请求,不能使用request对象来共享数据
  5. 重定向可以访问其他站点(服务器)的资源
方法说明备注
response.sendRedirect(url)url重定向

3>中文乱码问题

  1. PrintWriter pw = response.getWriter();获取的流的默认编码是ISO-8859-1
  2. 设置该流的默认编码
  3. 告诉浏览器响应体使用的编码
//简单的形式,设置编码,是在获取流之前设置
response.setContentType("text/html;charset=utf-8");

3)

3,Httpclient(推荐)

HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 Http 协议的客户端编程工具包,并且它支持 HTTP 协议最新版本和建议。HttpClient 相比传统 JDK 自带的 URLConnection,提升了易用性和灵活性,使客户端发送 HTTP 请求变得容易,提高了开发的效率。

1)使用

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>

1>GET

//创建 HttpClient 的实例  记得传输完成后要close
try(CloseableHttpClient client = getClient()){
	HttpGet httpGet = new HttpGet(url);
	byte[] bytes = null;
	try{
		HttpResponse response = client.execute(httpGet);
		//下载文件的字节流  response.getEntity()才是真实的返回数据 响应内容长度:responseEntity.getContentLength()
		bytes = EntityUtils.toByteArray(response.getEntity());	
	}catch (IOException e) {
	}finally {
	   if (httpClient != null) {
	        httpClient.close();
	   }
	   if (response != null) {
	        response.close();
	   }
	}
	//将文件保存到指定路径
	filePath = filePath + File.separator + eventName + ".docx";
	try (FileOutputStream fos = new FileOutputStream(filePath)) {
	    fos.write(bytes);
	    fos.flush();
	}
}catch(){

}


getClient()中设置了忽略对服务端的SSL证书校验

private CloseableHttpClient getClient() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (TrustStrategy) (arg0, arg1) -> true).build();
        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
        CloseableHttpClient client = HttpClients.custom()
                .setSSLSocketFactory(sslConnectionSocketFactory)
                .build();
        return client;
    }

2>POST

//创建 HttpClient 的实例  
HttpClient client = getClient();
HttpPost httpPost = new HttpPost(url);
//设置Header
httpPost.addHeader("Content-Type", "application/json; charset=utf-8");
httpPost.setHeader("Accept", "application/json");
//设置body参数
HttpEntity httpEntity = new StringEntity(JSONObject.toJSONString(userVo), Charset.forName("UTF-8"));
httpPost.setEntity(httpEntity);
//请求
HttpResponse response = client.execute(httpPost);
//获取请求结果
JSONObject result = JSONObject.parseObject(EntityUtils.toString(response.getEntity()), JSONObject.class);

4,HttpURLConnection

HttpURLConnection 是 Java 的标准类,它继承自 URLConnection,可用于向指定网站发送 GET 请求、POST 请求。HttpURLConnection 使用比较复杂,不像 HttpClient 那样容易使用。

5,RestTemplate

RestTemplate 是 Spring 提供的用于访问 Rest 服务的客户端。

1)使用

  • url:请求地址。
  • method:HTTP方法。
  • requestEntity:请求体的实体对象,可包括请求头和请求参数等信息。
  • responseType:响应体的类型,可以是简单类型,也可以是复合类型,如List、Map等。
  • uriVariables:可选参数,表示URL中的占位符,例如/user/{userId}中的userId。
HTTP methodRestTemplate类方法说明
DELETEdelete(String url, Object… uriVariables)
delete(String url, Class responseType, Object… uriVariables)将响应体转换为指定的Java对象。
deleteForObject(String url, Object… uriVariables)将响应体转换为指定的Java对象。
GETgetForObject(String url, Class responseType, Object… uriVariables)将响应体转换为指定的Java对象。
getForEntity(String url, Class responseType, Object… uriVariables)将响应体封装到ResponseEntity对象中。
POSTpostForEntity(String url, Object request, Class responseType, Object… uriVariables)将请求体和响应体封装到ResponseEntity对象中。
postForObject(String url, Object request, Class responseType, Object… uriVariables)将响应体转换为指定的Java对象。
PUTput(String url, Object request, Object… uriVariables)
PATCHatchForObject(String url, Object request, Class responseType, Object… uriVariables)发送PATCH请求,并将响应体转换为指定的Java对象。
HEADheadForHeaders(String url, Object… uriVariables)发送HEAD请求,并获取响应头信息
OPTIONSoptionsForAllow(String url, Object… uriVariables)发送OPTIONS请求,并获取允许的HTTP请求方法。
any(通用请求方法)exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, Class responseType, Object… uriVariables)发送任意HTTP请求,并将响应体转换为指定的Java对象。
exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, ParameterizedTypeReference responseType, Object… uriVariables)发送任意HTTP请求,并将响应体封装到ResponseEntity对象中。

2)无参数请求,将返回提转换为ResponseEntity对象

//也可以@Autowired
RestTemplate restTemplate = new RestTemplate();
//getForObject, 将返回的请求体 映射为一个对象
ResponseEntity<String> entity = restTemplate.getForEntity(uri, String.class);

3)RequestParam + 路径参数:

String uri = "https://sandbox-openapi.chanjet.com/accounting/gl/statistics/pay/{bookid}?period={period}";
// params
Map<String, String> params = new HashMap<>();
params.put("bookid", bookid);
params.put("period", period);

//直接给值也可以 restTemplate.getForEntity(uri, String.class, bookid, period);
ResponseEntity<String> entity = restTemplate.getForEntity(uri, String.class, params);

4)body参数

// body  post的请求体
JSONObject json = new JSONObject();
json.put("XXX", XXX);
json.put("XXX", XXX);
HttpEntity<JSONObject> request = new HttpEntity<>(json, httpHeaders);
//如果还有路径或者request参数:param,直接加后面 client.postForEntity(uri, request, String.class, param);
ResponseEntity<String> entity = restTemplate.postForEntity(uri, request, String.class);

5)header

// 添加headers
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
headers .add("openToken", openToken);
HttpEntity<Object> objectHttpEntity = new HttpEntity<>(headers);
ResponseEntity<String> entity = restTemplate.postForEntity(uri, request, String.class);

6)添加超时时间

//设置超时时间
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(60 * 1000);
requestFactory.setReadTimeout(60 * 1000);
restTemplate.setRequestFactory(requestFactory);

7)文件传输

//对应postman的form-data参数
 MultiValueMap<String, Object> parameters = new LinkedMultiValueMap<>();
        parameters.add("file", new FileSystemResource(file));
        parameters.add("id", id);
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(parameters, header);

        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<SendResponseEntity> responseEntity = restTemplate.postForEntity(url, requestEntity, SendResponseEntity.class);
        

8)绕过证书访问https+ip

   <dependency>
   	 <groupId>org.apache.httpcomponents</groupId>
     <artifactId>httpclient</artifactId>
   </dependency>

建立一个HttpClientUtils类,该类的主要作用就是设置RestTemplate,使其不会对域名检查:

import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

public class HttpClientUtils {
/*
* https ip地址请求忽略证书设置
*
* */
    public static CloseableHttpClient acceptsUntrustedCertsHttpClient() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        HttpClientBuilder b = HttpClientBuilder.create();

        // setup a Trust Strategy that allows all certificates.
        //
        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                return true;
            }
        }).build();
        b.setSSLContext(sslContext);

        // don't check Hostnames, either.
        //      -- use SSLConnectionSocketFactory.getDefaultHostnameVerifier(), if you don't want to weaken
        HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;

        // here's the special part:
        //      -- need to create an SSL Socket Factory, to use our weakened "trust strategy";
        //      -- and create a Registry, to register it.
        //
        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
        Registry socketFactoryRegistry = RegistryBuilder.create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslSocketFactory)
                .build();

        // now, we create connection-manager using our Registry.
        //      -- allows multi-threaded use
        PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager( socketFactoryRegistry);
        connMgr.setMaxTotal(200);
        connMgr.setDefaultMaxPerRoute(100);
        b.setConnectionManager( connMgr);

        // finally, build the HttpClient;
        //      -- done!
        CloseableHttpClient client = b.build();

        return client;
    }

}

配置RestTemplate:

package com.qax.ngsoc.workorder.commont;

import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

/**
 * RestTemplate配置类
 */
@Configuration
public class RestTemplateNoSSLConfig {
    @Bean(name="restTemplateHttp")
    public RestTemplate restTemplate()  {
        return new RestTemplate();
    }

    @Bean(name="restTemplateHttps")
    public RestTemplate httpsRestTemplate(HttpComponentsClientHttpRequestFactory httpsFactory){
        RestTemplate restTemplate = new RestTemplate(httpsFactory);
        restTemplate.setErrorHandler(new ResponseErrorHandler() {
            @Override
            public boolean hasError(ClientHttpResponse clientHttpResponse) {
                return false;
            }

            @Override
            public void handleError(ClientHttpResponse clientHttpResponse) {
                //默认处理非200的返回,会抛异常
            }
        });
        return restTemplate;
    }

    @Bean(name = "httpsFactory")
    public HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory() throws Exception{
        CloseableHttpClient httpClient = HttpClientUtils.acceptsUntrustedCertsHttpClient();
        HttpComponentsClientHttpRequestFactory httpsFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        httpsFactory.setReadTimeout(2000);
        httpsFactory.setConnectTimeout(2000);
        return httpsFactory;
    }
}


调用https:


    @Resource(name = "restTemplateHttps")
    private RestTemplate noSSLRestTemplate;

6,spring-cloud-starter-openfeign(推荐)

1)OpenFeign概念

通过java接口注解的方式调用Http请求。更加直观。

spring-cloud-starter-feign
Feign是Netflix开源的轻量级RESTful的HTTP服务客户端。
Feign可以(通过客户端负载均衡的方式)作为客户端,去调用服务注册中心的服务。

Feign和OpenFeign作用一样,都是进行远程调用的组件,里面都内置了Ribbon。Feign 可以与 Eureka 和 Ribbon 组合使用以支持负载均衡。

OpenFeign在Feign的基础上支持了Spring MVC的注解,如@RequesMapping等等。
OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,
并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

2)使用

首先,对于服务提供者,不需要进行任何配置,feign的访问对于服务提供者其实就是一次http访问。
需要对服务调用者进行feign的相关配置:

1>创建spring boot项目

使用feign调用,通信的两者之间必须都在同一个注册中心(Eureka或nacos)。

在Eureka上注册服务: 服务的发现:Eureka + 服务的消费:Ribbon

2>openfeign依赖

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

3>启用Feign功能

添加注解:@EnableFeignClients

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableDiscoveryClient
public class ConsumerServiceApplication {
}

4>配置yml文件

Eureka相关配置好即可。

5>声明一个Client接口

在client包下创建接口:

//name不可缺省,其它可以不填。configuration 是对Feign调用的一些配置;url是访问链接,比如指向本地进行联调
@FeignClient(name="userinfo-service", configuration = FeignConfig.class, url = "")
@Component
public interface UserinfoServiceClient {

    @GetMapping("/userinfo/getUserinfoFromRedis")   //注意路径正确
    public ResponseMessage<UserinfoForm> getUserinfoFromRedis(@RequestParam("id") Long id);
}

相当于绑定了一个名叫userinfo-service( 不区分大小写)的服务提供者提供的/userinfo/getUserinfoFromRedis接口。

6>实验与测试:调用feign服务

    @Autowired
    UserinfoServiceClient userinfoServiceClient;
	……
	userinfoServiceClient.getUserinfoFromRedis(id).getData();

7>超时设置

给FeignClient,添加属性配置:configuration

@FeignClient(name = "user-service", configuration = FeignConfig.class)

配置类:


@Configuration
public class FeignConfig{    
    private static final int CONNECT_TIME_OUT_MILLIS = 1000*600;
    private static final int READ_TIME_OUT_MILLIS = 1000*600;
    @Bean
    public Request.Options options() {
        return new Request.Options(CONNECT_TIME_OUT_MILLIS, READ_TIME_OUT_MILLIS);
    }

		@Bean
    public Retryer feignRetryer(){
        return new Retryer.Default(100, TimeUnit.SECONDS.toMillis(1),5);
    }

    @Bean
    Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
}

到此就完了。也可以通过java代码设置:

public UserinfoServiceClient getUserinfoServiceClient () {
        Request.Options connectionConfig =
                new Request.Options(CONNECT_TIME_OUT_MILLIS, READ_TIME_OUT_MILLIS);
        Feign.Builder builder = Feign.builder();
        builder.decoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
        builder.encoder(new SpringEncoder(this.messageConverters));
        builder.options(connectionConfig);
        SSLContext context = null;
        try {
            context = new SSLContextBuilder().loadTrustMaterial(null, (chain, authType) -> true).build();
        } catch (Exception e) {
        }
        Client trustSSLSockets = new Client.Default(context.getSocketFactory(), new NoopHostnameVerifier());
        builder.client(trustSSLSockets);
        builder.contract(new SpringMvcContract());
        //设置feign访问的url,与url属性功能一致。
        return builder.target(UserinfoServiceClient .class,  userUrl);
    }
  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值