赞
踩
系统与系统之间的交互往往会使用到http请求,而我们发起http请求过程繁琐;因此我们需要对Http请求进行封装以方便使用;
最近项目中接触到了一个经常被其他项目调用的项目主体,研究其演进架构,将其精髓记录下来
首先我们来看一看最原始的Http请求的代码书写
- /**
- * http请求第一个版本(直接发起Http请求)
- *
- * @author hzm ${2020-06-20 17:57}
- */
- public class Http {
-
- /**
- * http请求版本1
- * @param param
- * @return
- */
- public Object excute(Map<String,String> param){
- String url = "www.baidu.com/key/user/getUserById";
- HttpClient client = new DefaultHttpClient();
- HttpPost post = new HttpPost(url);
- List<NameValuePair> nvps = new ArrayList<NameValuePair>();
- for (String key : param.keySet()) {
- nvps.add(new BasicNameValuePair(key, param.get(key)));
- }
- try {
- post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
- HttpResponse response = client.execute(post);
- return EntityUtils.toString(response.getEntity());
- } catch (IOException e) {
- return "";
- }
- }
- }
缺点:每次都使用Http的创建不方便,且书写Http请求过程复杂容易出错;
方案2:封装Http工具类进行调用
- /**
- * http请求封账工具类
- * 1,将通用变量抽离成私有变量
- * 2,参数则通过外部参数信息传入
- * 3,对返回结果进行处理
- *
- * @author hzm ${2020-06-20 17:57}
- */
- public class Http1 {
-
- private String domainUrl = "www.baidu.com";
- private String serviceName = "user";
- private String serviceInterface = "getUserById";
-
- /**
- * http请求版本1
- * @param param
- * @return
- */
- public static Object excute(Map<String,String> param){
- String url = domainUrl + "/key/" + serviceName + "/" + serviceInterface;
- HttpClient client = new DefaultHttpClient();
- HttpPost post = new HttpPost(url);
- List<NameValuePair> nvps = new ArrayList<NameValuePair>();
- for (String key : param.keySet()) {
- nvps.add(new BasicNameValuePair(key, param.get(key)));
- }
- try {
- post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
- HttpResponse response = client.execute(post);
- return EntityUtils.toString(response.getEntity());
- } catch (IOException e) {
- return "";
- }
- }
- }
缺点:1,对请求入参参数没有约定,容易超出边界;
2,返回结果数据可变化性不强,需调用者手动处理加工,加大了代码的开发难度;
方案3:通过代理模式封装Http请求类,由代理类统一执行器进行调用;
第一步:定义Http请求接口
- package com.example.http.http2;
-
- /**
- * http发起请求的客户端
- *
- * @author hzm ${2020-06-20 19:01}
- */
- public interface HttpClient2 {
-
-
- /**
- * 执行http请求接口封装: 将请求入参封装为HttpRequest、返回参数封装为HttpResponse
- * 实现统一调用和结果返回;
- * @param request 请求对象
- * @param <T>
- * @return HttpResponse 响应对象
- */
- public <T extends HttpResponse> T excute(HttpRequest<T> request);
-
-
- }
第二步:实现Http请求接口,并可以提供多种接口方式进行调用
- package com.example.http.http2;
-
- /**
- * 基本http客户端实现类
- * 相对封装工具类,入参和返回的封装容易出现问题
- * @author hzm ${2020-06-24 19:01}
- */
- public class BaseHttpClient2 implements HttpClient2{
-
- //todo 属性可以封装一些执行请求的指令参数:
- // 超时时间、 请求令牌 请求支持格式等;
-
- @Override
- public <T extends HttpResponse> T excute(HttpRequest<T> request) {
- //第一步:将请求参数解析出来:{
- // 1,header信息
- // 2、请求参数信息
- // 3、请求APP信息
- // 可扩展流程:参数校验/ 信息解析;
- // }
-
- //第二步:封装Http请求,
-
- //第三步:执行http请求调用结果
-
- //第四步:封装返回结果为指定的Resopnse结果值;
-
- //其他:异常处理
-
- return null;
- }
-
-
- }
第三步:封装Request接口和实现、以及Response接口和实现
HttpRequest实现基础参数封装;
BaseHttpRequest 进一步封装常用参数信息(Header信息及其他公用信息)
UserSearchRequest :封装该请求的接口参数入参及接口号信息
- package com.example.http.http2;
-
- import java.util.Map;
-
- /**
- * 我是谁
- *
- * @author hzm ${2020-06-20 18:18}
- */
- public interface HttpRequest<T extends HttpResponse> {
-
- /**
- * 获取应用
- * @return
- */
- public String getAppKey();
-
- /**
- * 获取方法
- * @return
- */
- public String getMethod();
-
- /**
- * 获取参数
- * @return
- */
- public Map<String,String> getParam();
-
- /**
- * 获取请求头信息
- * @return
- */
- public Map<String,String> getHeader();
-
-
- /**
- * 检查参数信息
- * @return
- */
- public boolean check();
- }
-
-
-
-
- package com.example.http.http2.Request;
-
- import com.example.http.http2.HttpRequest;
- import com.example.http.http2.HttpResponse;
-
- import java.util.HashMap;
- import java.util.Map;
-
- /**
- * 我是谁
- *
- * @author hzm ${Date}
- */
- public abstract class BaseHttpRequest<T extends HttpResponse> implements HttpRequest {
- protected Map<String,String> headerStringMap;
- protected Map<String,String> paramMap;
- protected String appKey;
-
-
- public void putHeaderParam(String key, String value){
- if(this.headerStringMap == null){
- this.headerStringMap = new HashMap<>();
- }
- headerStringMap.put(key,value);
- }
-
- public Map<String, String> getHeaderStringMap() {
- if(this.headerStringMap == null){
- this.headerStringMap = new HashMap<>();
- }
- return this.headerStringMap;
- }
-
- public void setHeaderStringMap(Map<String, String> headerStringMap) {
- this.headerStringMap = headerStringMap;
- }
-
- public Map<String, String> getParamMap() {
- if(this.paramMap == null){
- this.paramMap = new HashMap<>();
- }
- return this.paramMap;
- }
-
- public void setParamMap(Map<String, String> paramMap) {
- this.paramMap = paramMap;
- }
-
- @Override
- public String getAppKey() {
- return appKey;
- }
-
- public void setAppKey(String appKey) {
- this.appKey = appKey;
- }
- }
-
-
-
-
-
- package com.example.http.http2.Request;
-
- import com.example.http.http2.Response.UserSearchResponse;
-
- import java.util.Map;
-
- /**
- * 我是谁
- *
- * @author hzm ${Date}
- */
- public class UserSearchRequest extends BaseHttpRequest<UserSearchResponse> {
- /**用户id 姓名 地址*/
- private String id;
- private String name;
- private String adds;
-
- @Override
- public String getMethod() {
- return "getUserByContion";
- }
-
- @Override
- public Map<String, String> getParam() {
- //将参数设置进去
- return this.paramMap;
- }
-
- @Override
- public Map<String, String> getHeader() {
- return this.headerStringMap;
- }
-
- @Override
- public boolean check() {
- //todo 参数校验
- return false;
- }
-
- //get and set
-
-
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getAdds() {
- return adds;
- }
-
- public void setAdds(String adds) {
- this.adds = adds;
- }
-
- @Override
- public String toString() {
- return "UserSearchRequest{" +
- "id='" + id + '\'' +
- ", name='" + name + '\'' +
- ", adds='" + adds + '\'' +
- '}';
- }
- }
-
HttpResponse:
HttpResponse,封装响应的消息、响应执行情况,响应结果信息
UserSearchResponse, 封装请求返回参数信息
- package com.example.http.http2;
-
- import java.io.Serializable;
- import java.util.Map;
-
- /**
- * 基础httpResponse属性封装
- *
- * @author hzm ${2020-06-20 18:00}
- */
- public abstract class HttpResponse implements Serializable {
-
- /**
- * 执行信息(正确/错误)
- */
- private String message;
-
- /**
- * 响应结果:转换为字符串;
- */
- private String response;
-
- /**
- * 是否成功
- */
- private boolean isSuccess;
-
- /**
- * 请求入参参数
- */
- private Map<String,String> params;
-
-
- //get and set
- public String getMessage() {
- return message;
- }
-
- public void setMessage(String message) {
- this.message = message;
- }
-
- public String getResponse() {
- return response;
- }
-
- public void setResponse(String response) {
- this.response = response;
- }
-
- public boolean isSuccess() {
- return isSuccess;
- }
-
- public void setSuccess(boolean success) {
- isSuccess = success;
- }
-
- public Map<String, String> getParams() {
- return params;
- }
-
- public void setParams(Map<String, String> params) {
- this.params = params;
- }
- }
-
-
-
-
- package com.example.http.http2.Response;
-
-
- import com.example.http.http2.HttpResponse;
-
- /**
- * 我是谁
- *
- * @author hzm ${Date}
- */
- public class UserSearchResponse extends HttpResponse {
-
- private Long id;
- private String name;
- private String adds;
-
- //get and set
-
- public Long getId() {
- return id;
- }
-
- public void setId(Long id) {
- this.id = id;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public String getAdds() {
- return adds;
- }
-
- public void setAdds(String adds) {
- this.adds = adds;
- }
-
- @Override
- public String toString() {
- return "UserGetResponse{" +
- "id=" + id +
- ", name='" + name + '\'' +
- ", adds='" + adds + '\'' +
- '}';
- }
- }
缺点:新增加一个接口都需要实现这个接口的Request实现类+ Response实现类,工作量较大,且重复冗余;
设计模式:利用接口封装,将请求、入参、返参、其他解析、异常处理都通过接口封装的方式进行封装,
实现动态变量的扩展使用,且参数实现统一的约定;
方案4:AOP实现,代理进行AOP代理的各个方法的无感知调用并可加入权鉴信息在代理类中;
设计模式:利用MethodInterceptor实现对
ProxyFactory.getProxy(this.serviceInterface,this) 类的方法级别动态代理;将http请求信息封装在Invoke()方法中;
FactoryBean介绍: https://mp.csdn.net/console/editor/html/107254969
MethodInterceptor介绍:https://blog.csdn.net/Munger6/article/details/106695421
1、HttpApiMethodInterceptor : 实现MethodInterceptor 动态代理调用invoke方法走Http请求统一封装
- package com.example.http.http3;
-
- import org.aopalliance.intercept.MethodInterceptor;
- import org.aopalliance.intercept.MethodInvocation;
- import org.apache.http.client.HttpClient;
- import org.apache.http.impl.client.DefaultHttpClient;
- import org.springframework.beans.factory.InitializingBean;
-
- /**
- * 我是谁
- *
- * @author hzm ${2020-07-02 19:50}
- */
- public class HttpApiMethodInterceptor implements MethodInterceptor , InitializingBean {
- //http请求客户端
- HttpClient client;
-
- //请求url地址
- protected String url;
-
- //可设置其他的权限,密令等信息
-
-
-
- @Override
- public Object invoke(MethodInvocation methodInvocation) throws Throwable {
- //执行拦截到方法调用之后的请求逻辑
- /***第一步:构建httpRequest: 根据GET /POST 进行不同的请求创建 通过方法的注解写入***/
- // methodInvocation.getMethod().getAnnotation(RequestMethod.class).value(); 获取请求方式
- // methodInvocation.getMethod().getDeclaringClass().getSimpleName() 获取服务名
- // methodInvocation.getMethod().getName() 获取方法
- // methodInvocation.getArguments() 获取参数
- // methodInvocation.getMethod().getGenericReturnType() 获取返回参数
- /***第二步:构建httpRequest: 添加header信息(命令 权限等信息)***/
-
- /***第三步:执行请求***/
-
- /***第四步:将请求结果进行解析封装成对应的返回结果bean***/
-
- return null;
- }
-
-
- @Override
- public void afterPropertiesSet() throws Exception {
- //属性自动注入之后调用该方法, 创建http请求
- client = new DefaultHttpClient();
- }
- }
2、HttpApiProxyFactoryBean,封装动态代理对象交给getObject()方法给真实调用
- package com.example.http.http3;
-
- import org.springframework.aop.framework.ProxyFactory;
- import org.springframework.beans.factory.FactoryBean;
- import org.springframework.beans.factory.InitializingBean;
-
- /**
- * 优势:大量的方法暴露使用简便;
- * factoryBean 交给容器管理
- * <bean id="userSearchFacade" class="com.alibaba.ak.base.openapi.HttpApiProxyFactoryBean">
- * <property name="serviceInterface" value="com.example.http.http3.UserSearchFacade"/>
- * <property name="url" value="www.baidu.com"/>
- * </bean>
- * @author hzm ${Date}
- */
- public class HttpApiProxyFactoryBean extends HttpApiMethodInterceptor
- implements FactoryBean<Object>, InitializingBean {
-
- private Object proxy;
-
- private Class<?> serviceInterface;
-
-
- @Override
- public void afterPropertiesSet() throws Exception {
- super.afterPropertiesSet();
- this.proxy = ProxyFactory.getProxy(this.serviceInterface,this);
- }
-
-
- @Override
- public Object getObject() throws Exception {
- return this.proxy;
- }
-
- @Override
- public Class<?> getObjectType() {
- return this.serviceInterface;
- }
-
- @Override
- public boolean isSingleton() {
- return true;
- }
-
- public Class<?> getServiceInterface() {
- return serviceInterface;
- }
-
- public void setServiceInterface(Class<?> serviceInterface) {
- this.serviceInterface = serviceInterface;
- }
-
- // get and set 父类的属性
- public String getUrl() {
- return url;
- }
-
- public void setUrl(String url) {
- this.url = url;
- }
- }
3、Facade封装调用的方法与接口
- package com.example.http.http3;
-
- /**
- * 用户搜索门面
- *
- * @author hzm ${2020-07-02 10:26}
- */
- public interface UserSearchFacade {
-
-
- @RequestMethod("POST")
- Result<User> resetRelatedProjectSets(@Param("projectId") Integer var1, @Param("projectSetIds") List<Integer> var2, @Param("modifier") String var3);
-
-
- }
- <bean id="userSearchFacade" class="com.example.http.http3.OpenApiProxyFactoryBean">
- <property name="serviceInterface" value="com.example.http.http3.UserSearchFacade"/>
- <property name="serviceUrl" value="www.baidu.com"/>
- </bean>
xml配置注入代理类信息
这一实现方式我们就可以很方便的仅定义一个门面类即可;大大提升了代码的易维护性和参数的安全性
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。