赞
踩
在实际开发过程中,我们经常需要调用对方提供的接口或测试自己写的接口是否合适。很多项目都会封装规定好本身项目的接口规范,所以大多数需要去调用对方提供的接口或第三方接口(短信、天气等)。
准备两个项目:
项目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)); } }
比较原始的一种调用做法,这里把get请求和post请求都统一放在一个方法里面。
(1)Post方式请求过程:
(2)Get方式请求过程:
其实主要就是传递参数时不同
测试程序:
根据步骤,新建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请求,但是还是要了解一下
测试程序:
根据步骤,新建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": "佛山市"
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。