Java实现HTTP请求的几种方式-HttpURLConnection(一)

背景

在实际开发过程中,我们经常需要调用对方提供的接口或测试自己写的接口是否合适。很多项目都会封装规定好本身项目的接口规范,所以大多数需要去调用对方提供的接口或第三方接口(短信、天气等)。

测试前准备

第一步(服务提供者)

准备两个项目:
项目A: 服务提供者
项目B:服务消费者

在项目A中做了两个接口,一个Post方式,一个Get方式,用于测试下面四种方式的调试

    @ApiOperation(value = "测试HTTP请求(POST方式)", notes = "\n开发者:")
    @PostMapping(value = "/testHttpAccess")
    //@SImplePermission
    public JsonResult<TestHttpAccessResponse> testHttpAccess(@RequestBody TestHttpAccessRequest request) {
        return JsonResult.success(testService.testHttpAccess(request));
    }


    @ApiOperation(value = "测试HTTP请求(GET方式)", notes = "\n开发者:")
    @GetMapping(value = "/testHttpAccessGet")
    //@SImplePermission
    public JsonResult<TestHttpAccessResponse> testHttpAccessGet(TestHttpAccessRequest request) {
        return JsonResult.success(testService.testHttpAccess(request));
    }

参数类和响应类:

@Data
@ApiModel("测试HTTP请求入参")
public class TestHttpAccessRequest {
    @ApiModelProperty(value = "名字")
    private String name;

    @ApiModelProperty(value = "年龄")
    private Integer age;

    @ApiModelProperty(value = "地址")
    private String address;
}


@Data
@ApiModel("测试HTTP请求响应")
public class TestHttpAccessResponse {
    @ApiModelProperty(value = "名字")
    private String name;

    @ApiModelProperty(value = "年龄")
    private Integer age;

    @ApiModelProperty(value = "地址")
    private String address;
}

Service:

public TestHttpAccessResponse testHttpAccess(TestHttpAccessRequest request) {
        return BeanUtil.toBean(request, TestHttpAccessResponse.class);
    }

第二步(服务提供者)

为了能够演示token校验,我写了一个最简单的token检验切面
注解@SImplePermission,只要有这个注解才对token进行校验,可以直接在Controller的方法中使用

@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SimplePermission {

}

切面:SimplePermissionInterceptor,对token进行校验
因为项目A我还有其他的切面,这个切面必须是最前面的,所以设置了order=-1

@Aspect
@Order(value = -1)
@Component
public class SimplePermissionInterceptor {

    @Resource
    private HttpServletRequest request;

    @Resource
    private AuthService authService;

    @Pointcut("@annotation(com.zby.annotation.SimplePermission)")
    public void pointcut() {
    }


    @Around("pointcut()")
    public Object intercept(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        SimplePermission annotation = method.getAnnotation(SimplePermission.class);
        if (Objects.nonNull(annotation)) {
            String value = request.getHeader(AuthConstant.AUTHORIZATION);
            if (StringUtils.isBlank(value) || !authService.isLogined(value)) {
                throw new BizException(NacosServiceProviderReturnCode.NEED_LOGIN);
            }
        }
        return joinPoint.proceed();
    }
}

登录服务:我做了一个简易的鉴权逻辑,用于测试请求头

@Data
@ApiModel(value = "登录请求")
public class LoginRequest {
    @ApiModelProperty(value = "用户名")
    private String userName;

    @ApiModelProperty(value = "密码")
    @NotNull(message = "密码不能为空")
    private String password;
}



@Data
@ApiModel(value = "登录响应")
public class LoginResponse {
    @ApiModelProperty(value = "用户名")
    private String uname;

    @ApiModelProperty(value = "鉴权")
    private String token;
}





@Service
public class AuthService {
    private static String userName = "xxx";
    private static String password = "xxx";
    private static Map<String, String> tokenMap = new HashMap<>();

    /**
     * 测试功能,仅使用一个用户名和密码进行登录
     *
     * @param loginRequest
     */
    public LoginResponse login(LoginRequest loginRequest) {
        if (!loginRequest.getUserName().equals(userName)) {
            throw new BizException(NacosServiceProviderReturnCode.LOGIN_USER_NOT_EXIST);
        }
        if (!loginRequest.getPassword().equals(password)) {
            throw new BizException(NacosServiceProviderReturnCode.LOGIN_PASSWORD_FAIL);
        }
        LoginResponse response = new LoginResponse();
        String token = UUID.randomUUID().toString().replaceAll("-", "");
        tokenMap.put(token, userName);
        response.setToken(token);
        response.setUname(userName);
        return response;
    }

    public boolean isLogined(String token) {
        return tokenMap.containsKey(token);
    }
}

接口:

@Slf4j
@Api(tags = "权限控制器")
@RestController
@RequestMapping(value = "/auth")
@RequiredArgsConstructor
public class AuthController {
    private final AuthService authService;


    @ApiOperation(value = "登录")
    @PostMapping(value = "/login")
    public JsonResult<LoginResponse> login(@RequestBody @Validated LoginRequest request) {
        return JsonResult.success(authService.login(request));
    }
}

通过JDK网络类Java.net.HttpURLConnection

比较原始的一种调用做法,这里把get请求和post请求都统一放在一个方法里面。

Post方式和Get方式使用步骤

(1)Post方式请求过程:

  1. 创建远程链接对象
  2. 设置请求方式(get,post,…)
  3. 设置连接超时时间
  4. 设置响应超时时间
  5. 发起连接
  6. 传入请求参数(可选)
  7. 获取请求响应
  8. 关闭连接

(2)Get方式请求过程:

  1. 拼接url参数,中文需要UrlEncode
  2. 创建远程链接对象
  3. 设置请求方式(get,post,…)
  4. 设置连接超时时间
  5. 设置响应读取时间
  6. 发起连接
  7. 获取请求响应
  8. 关闭连接

其实主要就是传递参数时不同

Post请求方式

测试程序:

根据步骤,新建doPost方法:
这里的param是已经被序列化后的JSON

 public <T, R> R doPost(String httpUrl, T params, Class<R> returnType,String token) throws IOException {
    StringBuilder result = new StringBuilder();
    //连接
    HttpURLConnection connection = null;
    OutputStream os = null;
    InputStream is = null;
    BufferedReader br = null;
    try {
        //创建连接对象
        URL url = new URL(httpUrl);
        //创建连接
        connection = (HttpURLConnection) url.openConnection();
        //设置请求方法
        connection.setRequestMethod("POST");
        //设置连接超时时间(必须)
        connection.setConnectTimeout(15000);
        //设置读取超时时间(必须)
        connection.setReadTimeout(15000);
        //DoOutput设置是否向httpUrlConnection输出,DoInput设置是否从httpUrlConnection读入,此外发送post请求必须设置这两个
        //设置是否可读取
        connection.setDoOutput(true);
        connection.setDoInput(true);
        //设置通用的请求属性
        connection.setRequestProperty("accept", "*/*");
        connection.setRequestProperty("connection", "Keep-Alive");
        connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
        connection.setRequestProperty("Content-Type", "application/json;charset=utf-8");
        //(可选)设置请求头鉴权信息
        if(StringUtils.isNotBlank(token)){
            connection.addRequestProperty("Authorization",token);
        }


        // 建立实际的连接
        connection.connect();
        //拼装参数
        if (null != params) {
            String param = objectMapper.writeValueAsString(params);
            //设置参数
            os = connection.getOutputStream();
            OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
            //拼装参数
            writer.write(param);
            writer.flush();

            //另一种方式
            //设置参数
            //os = connection.getOutputStream();
            //拼装参数
            //os.write(param.getBytes("UTF-8"));
        }
        //设置权限
        //设置请求头等
        //开启连接
        //connection.connect();
        //读取响应
        if (connection.getResponseCode() == 200) {
            is = connection.getInputStream();
            if (null != is) {
                br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                String temp = null;
                while (null != (temp = br.readLine())) {
                    result.append(temp);
                    result.append("\r\n");
                }
            }
        }

    } finally {
        //关闭连接
        if (br != null) {
            try {
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //关闭连接
        connection.disconnect();
    }
    R object = objectMapper.readValue(result.toString(), returnType);
    return object;

}

getToken方法:

/**
 * 获取第三方接口的token
 */
public String getToken() throws IOException {
    String token = null;
    Map<String, String> object = new HashMap<>();
    object.put("userName", "xxx");
    object.put("password", "xxx");

    String url = "http://localhost:8082/nacos-service-provider/auth/login";
    JsonResult jsonResult = doPost(url, object, JsonResult.class);


    if (Objects.isNull(jsonResult) || !ReturnCode.SUCCESS.getCode().equals(jsonResult.getCode())) {
        if (Objects.isNull(jsonResult)) {
            throw new BizException(ReturnCode.ERROR);
        } else {
            throw new BizException(jsonResult.getCode(), jsonResult.getMessage());
        }
    }
    LoginResponse loginResponse = objectMapper.convertValue(jsonResult.getData(), LoginResponse.class);
    token = loginResponse.getToken();
    return token;
}

Service方法:

/**
  * 使用HttpURLConnection发送Post请求
  */
 @SneakyThrows
 public TestHttpAccessResponse sendHttpRequestByJdk() {
     TestHttpAccessRequest request = new TestHttpAccessRequest();
     request.setAge(16);
     request.setName("刘伞");
     request.setAddress("佛山市");

     String httpUrl = "http://localhost:8082/nacos-service-provider/testHttpAccess";

     /**
      * 如果是List<T>这种带泛型的对象,则需要使用TypeReference<类型> typeRef = new TypeReference...
      * 注意日期的类型,需要事前设置类型转换器
      */
     String token = httpUrlConnectionService.getToken();
     JsonResult jsonResult = httpUrlConnectionService.doPost(httpUrl, request, JsonResult.class,token);
     if (Objects.isNull(jsonResult) || !ReturnCode.SUCCESS.getCode().equals(jsonResult.getCode())) {
         if (Objects.isNull(jsonResult)) {
             throw new BizException(ReturnCode.ERROR);
         } else {
             throw new BizException(jsonResult.getCode(), jsonResult.getMessage());
         }
     }


     /**
      * 由于我做了统一的返回体JsonResult,所以还需要再转一遍
      * 这里不封装进去是因为,除了JsonResult可能还会有其他的返回体
      */
     TestHttpAccessResponse response = objectMapper.convertValue(jsonResult.getData(), TestHttpAccessResponse.class);
     return response;
 }

调试结果(软件:apifox):
无授权:

{
    "code": "40026",
    "message": "token不存在或过期,请登录",
    "data": null
}

正常:

{
    "code": "40000",
    "message": "操作成功",
    "data": {
        "name": "刘伞",
        "age": 16,
        "address": "佛山市"
    }
}

Get请求方式

虽然正常工作中不太会使用到Get请求,但是还是要了解一下

测试程序:
根据步骤,新建doGet方法:

注意:
(1)注意这里不要把参数放到OutputStream里面,否则会自动变成POST请求
(2)String参数都用UrlEncode编码,否则有可能会提示Invalid Character

    /**
     * Http get请求
     *
     * @param httpUrl 连接
     * @return 响应数据
     */
    public String doGet(String httpUrl, String param,String token) {
        //链接
        HttpURLConnection connection = null;
        InputStream is = null;
        BufferedReader br = null;
        StringBuffer result = new StringBuffer();
        try {
            //创建连接
            if (StringUtils.isNotBlank(param)) {
                httpUrl = httpUrl + "?" + param;
            }
            URL url = new URL(httpUrl);
            connection = (HttpURLConnection) url.openConnection();
            //设置请求方式
            connection.setRequestMethod("GET");
            //设置连接超时时间
            connection.setReadTimeout(15000);
            //(可选)设置请求头鉴权信息
            if(StringUtils.isNotBlank(token)){
                connection.addRequestProperty("Authorization",token);
            }


            //开始连接
            connection.connect();

            //请求参数(这里不要把参数放到outputStream中,会自动变成post请求)
           /* if (StringUtils.isNotBlank(param)) {
                OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream(), "UTF-8");
                writer.write(param);
                writer.flush();
            }*/

            //获取响应数据
            if (connection.getResponseCode() == 200) {
                //获取返回的数据
                is = connection.getInputStream();
                if (null != is) {
                    br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                    String temp = null;
                    while (null != (temp = br.readLine())) {
                        result.append(temp);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //关闭远程连接
            connection.disconnect();
        }
        return result.toString();
    }

测试方法:

     /**
     * 使用HttpURLConnection发送Post请求
     */
    @SneakyThrows
    public TestHttpAccessResponse sendHttpGetRequestByJdk() {
        TestHttpAccessRequest request = new TestHttpAccessRequest();
        request.setAge(16);
        request.setName("刘伞");
        request.setAddress("佛山市");

        //拼接参数
        String httpUrl = "http://localhost:8082/nacos-service-provider/testHttpAccessGet";
        List<String> params = Lists.newArrayList();
        Class<TestHttpAccessRequest> clazz = TestHttpAccessRequest.class;
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            declaredField.setAccessible(true);
            Object o = declaredField.get(request);
            if (declaredField.getType().equals(String.class)) {
                String s = (String) declaredField.get(request);
                s = URLEncoder.encode(s);
                o = s;
            }
            params.add(declaredField.getName() + "=" + o);
        }
        String param = params.stream().collect(Collectors.joining("&"));
        //这里getToken方法沿用上面的
        String token = httpUrlConnectionService.getToken();
        String responseJson = httpUrlConnectionService.doGet(httpUrl, param,token);

        /**
         * 如果是List<T>这种带泛型的对象,则需要使用TypeReference<类型> typeRef = new TypeReference...
         * 注意日期的类型,需要事前设置类型转换器
         */
        JsonResult<Object> jsonResult = objectMapper.readValue(responseJson, JsonResult.class);
        /**
         * 由于我做了统一的返回体JsonResult,所以还需要再转一遍
         */
        if (Objects.isNull(jsonResult) || !ReturnCode.SUCCESS.getCode().equals(jsonResult.getCode())) {
            if (Objects.isNull(jsonResult)) {
                throw new BizException(ReturnCode.ERROR);
            } else {
                throw new BizException(jsonResult.getCode(), jsonResult.getMessage());
            }
        }
        TestHttpAccessResponse response = objectMapper.convertValue(jsonResult.getData(), TestHttpAccessResponse.class);
        return response;
    }

结果展示:
无授权:

{
    "code": "40026",
    "message": "token不存在或过期,请登录",
    "data": null
}

正常:

{
    "code": "40000",
    "message": "操作成功",
    "data": {
        "name": "刘伞",
        "age": 16,
        "address": "佛山市"
    }
}

相关阅读

Java实现HTTP请求的几种方式-Apache HttpClient(二)

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值