当前位置:   article > 正文

Http请求封装的三进阶_封装http请求

封装http请求

       系统与系统之间的交互往往会使用到http请求,而我们发起http请求过程繁琐;因此我们需要对Http请求进行封装以方便使用;

最近项目中接触到了一个经常被其他项目调用的项目主体,研究其演进架构,将其精髓记录下来

       首先我们来看一看最原始的Http请求的代码书写

  1. /**
  2. * http请求第一个版本(直接发起Http请求)
  3. *
  4. * @author hzm ${2020-06-20 17:57}
  5. */
  6. public class Http {
  7. /**
  8. * http请求版本1
  9. * @param param
  10. * @return
  11. */
  12. public Object excute(Map<String,String> param){
  13. String url = "www.baidu.com/key/user/getUserById";
  14. HttpClient client = new DefaultHttpClient();
  15. HttpPost post = new HttpPost(url);
  16. List<NameValuePair> nvps = new ArrayList<NameValuePair>();
  17. for (String key : param.keySet()) {
  18. nvps.add(new BasicNameValuePair(key, param.get(key)));
  19. }
  20. try {
  21. post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
  22. HttpResponse response = client.execute(post);
  23. return EntityUtils.toString(response.getEntity());
  24. } catch (IOException e) {
  25. return "";
  26. }
  27. }
  28. }

         缺点:每次都使用Http的创建不方便,且书写Http请求过程复杂容易出错;   

     

        方案2:封装Http工具类进行调用

  1. /**
  2. * http请求封账工具类
  3. * 1,将通用变量抽离成私有变量
  4. * 2,参数则通过外部参数信息传入
  5. * 3,对返回结果进行处理
  6. *
  7. * @author hzm ${2020-06-20 17:57}
  8. */
  9. public class Http1 {
  10. private String domainUrl = "www.baidu.com";
  11. private String serviceName = "user";
  12. private String serviceInterface = "getUserById";
  13. /**
  14. * http请求版本1
  15. * @param param
  16. * @return
  17. */
  18. public static Object excute(Map<String,String> param){
  19. String url = domainUrl + "/key/" + serviceName + "/" + serviceInterface;
  20. HttpClient client = new DefaultHttpClient();
  21. HttpPost post = new HttpPost(url);
  22. List<NameValuePair> nvps = new ArrayList<NameValuePair>();
  23. for (String key : param.keySet()) {
  24. nvps.add(new BasicNameValuePair(key, param.get(key)));
  25. }
  26. try {
  27. post.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
  28. HttpResponse response = client.execute(post);
  29. return EntityUtils.toString(response.getEntity());
  30. } catch (IOException e) {
  31. return "";
  32. }
  33. }
  34. }

        缺点:1,对请求入参参数没有约定,容易超出边界;

                   2,返回结果数据可变化性不强,需调用者手动处理加工,加大了代码的开发难度; 

 

       方案3:通过代理模式封装Http请求类,由代理类统一执行器进行调用;

       第一步:定义Http请求接口

  1. package com.example.http.http2;
  2. /**
  3. * http发起请求的客户端
  4. *
  5. * @author hzm ${2020-06-20 19:01}
  6. */
  7. public interface HttpClient2 {
  8. /**
  9. * 执行http请求接口封装: 将请求入参封装为HttpRequest、返回参数封装为HttpResponse
  10. * 实现统一调用和结果返回;
  11. * @param request 请求对象
  12. * @param <T>
  13. * @return HttpResponse 响应对象
  14. */
  15. public <T extends HttpResponse> T excute(HttpRequest<T> request);
  16. }

        第二步:实现Http请求接口,并可以提供多种接口方式进行调用

  1. package com.example.http.http2;
  2. /**
  3. * 基本http客户端实现类
  4. * 相对封装工具类,入参和返回的封装容易出现问题
  5. * @author hzm ${2020-06-24 19:01}
  6. */
  7. public class BaseHttpClient2 implements HttpClient2{
  8. //todo 属性可以封装一些执行请求的指令参数:
  9. // 超时时间、 请求令牌 请求支持格式等;
  10. @Override
  11. public <T extends HttpResponse> T excute(HttpRequest<T> request) {
  12. //第一步:将请求参数解析出来:{
  13. // 1,header信息
  14. // 2、请求参数信息
  15. // 3、请求APP信息
  16. // 可扩展流程:参数校验/ 信息解析;
  17. // }
  18. //第二步:封装Http请求,
  19. //第三步:执行http请求调用结果
  20. //第四步:封装返回结果为指定的Resopnse结果值;
  21. //其他:异常处理
  22. return null;
  23. }
  24. }

        第三步:封装Request接口和实现、以及Response接口和实现

             HttpRequest实现基础参数封装;

             BaseHttpRequest 进一步封装常用参数信息(Header信息及其他公用信息)

             UserSearchRequest  :封装该请求的接口参数入参及接口号信息

  1. package com.example.http.http2;
  2. import java.util.Map;
  3. /**
  4. * 我是谁
  5. *
  6. * @author hzm ${2020-06-20 18:18}
  7. */
  8. public interface HttpRequest<T extends HttpResponse> {
  9. /**
  10. * 获取应用
  11. * @return
  12. */
  13. public String getAppKey();
  14. /**
  15. * 获取方法
  16. * @return
  17. */
  18. public String getMethod();
  19. /**
  20. * 获取参数
  21. * @return
  22. */
  23. public Map<String,String> getParam();
  24. /**
  25. * 获取请求头信息
  26. * @return
  27. */
  28. public Map<String,String> getHeader();
  29. /**
  30. * 检查参数信息
  31. * @return
  32. */
  33. public boolean check();
  34. }
  35. package com.example.http.http2.Request;
  36. import com.example.http.http2.HttpRequest;
  37. import com.example.http.http2.HttpResponse;
  38. import java.util.HashMap;
  39. import java.util.Map;
  40. /**
  41. * 我是谁
  42. *
  43. * @author hzm ${Date}
  44. */
  45. public abstract class BaseHttpRequest<T extends HttpResponse> implements HttpRequest {
  46. protected Map<String,String> headerStringMap;
  47. protected Map<String,String> paramMap;
  48. protected String appKey;
  49. public void putHeaderParam(String key, String value){
  50. if(this.headerStringMap == null){
  51. this.headerStringMap = new HashMap<>();
  52. }
  53. headerStringMap.put(key,value);
  54. }
  55. public Map<String, String> getHeaderStringMap() {
  56. if(this.headerStringMap == null){
  57. this.headerStringMap = new HashMap<>();
  58. }
  59. return this.headerStringMap;
  60. }
  61. public void setHeaderStringMap(Map<String, String> headerStringMap) {
  62. this.headerStringMap = headerStringMap;
  63. }
  64. public Map<String, String> getParamMap() {
  65. if(this.paramMap == null){
  66. this.paramMap = new HashMap<>();
  67. }
  68. return this.paramMap;
  69. }
  70. public void setParamMap(Map<String, String> paramMap) {
  71. this.paramMap = paramMap;
  72. }
  73. @Override
  74. public String getAppKey() {
  75. return appKey;
  76. }
  77. public void setAppKey(String appKey) {
  78. this.appKey = appKey;
  79. }
  80. }
  81. package com.example.http.http2.Request;
  82. import com.example.http.http2.Response.UserSearchResponse;
  83. import java.util.Map;
  84. /**
  85. * 我是谁
  86. *
  87. * @author hzm ${Date}
  88. */
  89. public class UserSearchRequest extends BaseHttpRequest<UserSearchResponse> {
  90. /**用户id 姓名 地址*/
  91. private String id;
  92. private String name;
  93. private String adds;
  94. @Override
  95. public String getMethod() {
  96. return "getUserByContion";
  97. }
  98. @Override
  99. public Map<String, String> getParam() {
  100. //将参数设置进去
  101. return this.paramMap;
  102. }
  103. @Override
  104. public Map<String, String> getHeader() {
  105. return this.headerStringMap;
  106. }
  107. @Override
  108. public boolean check() {
  109. //todo 参数校验
  110. return false;
  111. }
  112. //get and set
  113. public String getId() {
  114. return id;
  115. }
  116. public void setId(String id) {
  117. this.id = id;
  118. }
  119. public String getName() {
  120. return name;
  121. }
  122. public void setName(String name) {
  123. this.name = name;
  124. }
  125. public String getAdds() {
  126. return adds;
  127. }
  128. public void setAdds(String adds) {
  129. this.adds = adds;
  130. }
  131. @Override
  132. public String toString() {
  133. return "UserSearchRequest{" +
  134. "id='" + id + '\'' +
  135. ", name='" + name + '\'' +
  136. ", adds='" + adds + '\'' +
  137. '}';
  138. }
  139. }

        HttpResponse:  

                 HttpResponse,封装响应的消息、响应执行情况,响应结果信息

                 UserSearchResponse,  封装请求返回参数信息

  1. package com.example.http.http2;
  2. import java.io.Serializable;
  3. import java.util.Map;
  4. /**
  5. * 基础httpResponse属性封装
  6. *
  7. * @author hzm ${2020-06-20 18:00}
  8. */
  9. public abstract class HttpResponse implements Serializable {
  10. /**
  11. * 执行信息(正确/错误)
  12. */
  13. private String message;
  14. /**
  15. * 响应结果:转换为字符串;
  16. */
  17. private String response;
  18. /**
  19. * 是否成功
  20. */
  21. private boolean isSuccess;
  22. /**
  23. * 请求入参参数
  24. */
  25. private Map<String,String> params;
  26. //get and set
  27. public String getMessage() {
  28. return message;
  29. }
  30. public void setMessage(String message) {
  31. this.message = message;
  32. }
  33. public String getResponse() {
  34. return response;
  35. }
  36. public void setResponse(String response) {
  37. this.response = response;
  38. }
  39. public boolean isSuccess() {
  40. return isSuccess;
  41. }
  42. public void setSuccess(boolean success) {
  43. isSuccess = success;
  44. }
  45. public Map<String, String> getParams() {
  46. return params;
  47. }
  48. public void setParams(Map<String, String> params) {
  49. this.params = params;
  50. }
  51. }
  52. package com.example.http.http2.Response;
  53. import com.example.http.http2.HttpResponse;
  54. /**
  55. * 我是谁
  56. *
  57. * @author hzm ${Date}
  58. */
  59. public class UserSearchResponse extends HttpResponse {
  60. private Long id;
  61. private String name;
  62. private String adds;
  63. //get and set
  64. public Long getId() {
  65. return id;
  66. }
  67. public void setId(Long id) {
  68. this.id = id;
  69. }
  70. public String getName() {
  71. return name;
  72. }
  73. public void setName(String name) {
  74. this.name = name;
  75. }
  76. public String getAdds() {
  77. return adds;
  78. }
  79. public void setAdds(String adds) {
  80. this.adds = adds;
  81. }
  82. @Override
  83. public String toString() {
  84. return "UserGetResponse{" +
  85. "id=" + id +
  86. ", name='" + name + '\'' +
  87. ", adds='" + adds + '\'' +
  88. '}';
  89. }
  90. }

       缺点:新增加一个接口都需要实现这个接口的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请求统一封装

  1. package com.example.http.http3;
  2. import org.aopalliance.intercept.MethodInterceptor;
  3. import org.aopalliance.intercept.MethodInvocation;
  4. import org.apache.http.client.HttpClient;
  5. import org.apache.http.impl.client.DefaultHttpClient;
  6. import org.springframework.beans.factory.InitializingBean;
  7. /**
  8. * 我是谁
  9. *
  10. * @author hzm ${2020-07-02 19:50}
  11. */
  12. public class HttpApiMethodInterceptor implements MethodInterceptor , InitializingBean {
  13. //http请求客户端
  14. HttpClient client;
  15. //请求url地址
  16. protected String url;
  17. //可设置其他的权限,密令等信息
  18. @Override
  19. public Object invoke(MethodInvocation methodInvocation) throws Throwable {
  20. //执行拦截到方法调用之后的请求逻辑
  21. /***第一步:构建httpRequest: 根据GET /POST 进行不同的请求创建 通过方法的注解写入***/
  22. // methodInvocation.getMethod().getAnnotation(RequestMethod.class).value(); 获取请求方式
  23. // methodInvocation.getMethod().getDeclaringClass().getSimpleName() 获取服务名
  24. // methodInvocation.getMethod().getName() 获取方法
  25. // methodInvocation.getArguments() 获取参数
  26. // methodInvocation.getMethod().getGenericReturnType() 获取返回参数
  27. /***第二步:构建httpRequest: 添加header信息(命令 权限等信息)***/
  28. /***第三步:执行请求***/
  29. /***第四步:将请求结果进行解析封装成对应的返回结果bean***/
  30. return null;
  31. }
  32. @Override
  33. public void afterPropertiesSet() throws Exception {
  34. //属性自动注入之后调用该方法, 创建http请求
  35. client = new DefaultHttpClient();
  36. }
  37. }

                  2、HttpApiProxyFactoryBean,封装动态代理对象交给getObject()方法给真实调用

  1. package com.example.http.http3;
  2. import org.springframework.aop.framework.ProxyFactory;
  3. import org.springframework.beans.factory.FactoryBean;
  4. import org.springframework.beans.factory.InitializingBean;
  5. /**
  6. * 优势:大量的方法暴露使用简便;
  7. * factoryBean 交给容器管理
  8. * <bean id="userSearchFacade" class="com.alibaba.ak.base.openapi.HttpApiProxyFactoryBean">
  9. * <property name="serviceInterface" value="com.example.http.http3.UserSearchFacade"/>
  10. * <property name="url" value="www.baidu.com"/>
  11. * </bean>
  12. * @author hzm ${Date}
  13. */
  14. public class HttpApiProxyFactoryBean extends HttpApiMethodInterceptor
  15. implements FactoryBean<Object>, InitializingBean {
  16. private Object proxy;
  17. private Class<?> serviceInterface;
  18. @Override
  19. public void afterPropertiesSet() throws Exception {
  20. super.afterPropertiesSet();
  21. this.proxy = ProxyFactory.getProxy(this.serviceInterface,this);
  22. }
  23. @Override
  24. public Object getObject() throws Exception {
  25. return this.proxy;
  26. }
  27. @Override
  28. public Class<?> getObjectType() {
  29. return this.serviceInterface;
  30. }
  31. @Override
  32. public boolean isSingleton() {
  33. return true;
  34. }
  35. public Class<?> getServiceInterface() {
  36. return serviceInterface;
  37. }
  38. public void setServiceInterface(Class<?> serviceInterface) {
  39. this.serviceInterface = serviceInterface;
  40. }
  41. // get and set 父类的属性
  42. public String getUrl() {
  43. return url;
  44. }
  45. public void setUrl(String url) {
  46. this.url = url;
  47. }
  48. }

                    3、Facade封装调用的方法与接口

  1. package com.example.http.http3;
  2. /**
  3. * 用户搜索门面
  4. *
  5. * @author hzm ${2020-07-02 10:26}
  6. */
  7. public interface UserSearchFacade {
  8. @RequestMethod("POST")
  9. Result<User> resetRelatedProjectSets(@Param("projectId") Integer var1, @Param("projectSetIds") List<Integer> var2, @Param("modifier") String var3);
  10. }
  1. <bean id="userSearchFacade" class="com.example.http.http3.OpenApiProxyFactoryBean">
  2. <property name="serviceInterface" value="com.example.http.http3.UserSearchFacade"/>
  3. <property name="serviceUrl" value="www.baidu.com"/>
  4. </bean>

              xml配置注入代理类信息

              这一实现方式我们就可以很方便的仅定义一个门面类即可;大大提升了代码的易维护性和参数的安全性

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/259355
推荐阅读
相关标签
  

闽ICP备14008679号