外卖点餐系统06
HttpClient
HttpClient 是 Java 中一个用于发送 HTTP 请求和接收 HTTP 响应的类库,最常见于与 Web 服务进行交互。
要在Java中使用HttpClient,只需要在Maven中导入HttpClien坐标:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
HttpClient核心Api
- HttpClient:
- HttpClient 是 Java 11 中引入的一个类,用于发送 HTTP 请求和处理 HTTP 响应。它支持同步和异步请求。
- 主要用于:发起 HTTP 请求,并接收来自服务器的响应。
- HttpClients:
- HttpClients 是 Apache HttpComponents 提供的工具类,用于创建 CloseableHttpClient 对象。它为 Java 8 和更早版本的开发者提供了一个类似的 HTTP 客户端。
- 主要用于:管理 CloseableHttpClient 对象的创建。
- CloseableHttpClient:
- CloseableHttpClient 是 Apache HttpClient 中的类,提供了一个 HTTP 客户端的实现,它是可关闭的,且常用于通过 execute 方法发送请求。
- 主要用于:发送 HTTP 请求并接收响应。
- HttpGet:
- HttpGet 是 Apache HttpClient 中的类,用于执行 HTTP GET 请求,常用于从服务器获取数据。
- 主要用于:向服务器请求数据。
- HttpPost:
- HttpPost 是 Apache HttpClient 中的类,用于执行 HTTP POST 请求,常用于向服务器发送数据(如表单提交、文件上传等)。
- 主要用于:向服务器发送数据。
发送请求步骤
- 创建HttpClient对象
- 创建Http请求对象
- 调用HttpClient的execute方法发送请求
- 解析返回结果
- 关闭资源
通过HttpClient发送Get方式请求
/**
* 测试通过Httpclient发送Get方式请求
*/
@Test
public void testGet() throws Exception {
// 创建HttpClient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
// 创建Http请求对象
HttpGet httpGet = new HttpGet("http://localhost:8080/user/shop/status");
// 调用HttpClient的execute方法发送请求, 接收响应结果
CloseableHttpResponse response = httpClient.execute(httpGet);
// 获取服务端返回的状态码,解析服务端返回的数据
int statusCode = response.getStatusLine().getStatusCode();
System.out.println(statusCode);
HttpEntity entity = response.getEntity();
String body = EntityUtils.toString(entity);
System.out.println("服务端返回的数据为:" + body);
// 关闭资源
response.close();
httpClient.close();
}
通过HttpClient发送Post方式请求,Post方式与Get方式类似,但是Post需要传递参数,中间部分代码都是为了设置Post请求体
/**
* 测试通过Httpclient发送Get方式请求
*/
@Test
public void testPost() throws Exception {
// 创建HttpClient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
// 下面的代码是构造一个请求,请求内容是json形式,用提供的类创造json请求
// 创建请求对象
HttpPost httpPost = new HttpPost("http://localhost:8080/admin/employee/login");
// Post 方式要提交请求参数,通过请求体以json方式提交
JSONObject jsonObject = new JSONObject();
jsonObject.put("username", "admin");
jsonObject.put("password", "123456");
StringEntity entity = new StringEntity(jsonObject.toString());
// 指定请求编码方式
entity.setContentEncoding("UTF-8");
// 指定数据格式
entity.setContentType("application/json");
httpPost.setEntity(entity);
// 发送请求
CloseableHttpResponse response = httpClient.execute(httpPost);
// 解析返回结果
int statusCode = response.getStatusLine().getStatusCode();
System.out.println("响应码为:" + statusCode);
HttpEntity entity1 = response.getEntity();
String body = EntityUtils.toString(entity1);
System.out.println("服务端返回的数据为:" + body);
// 关闭资源
response.close();
httpClient.close();
}
微信小程序开发
开发微信小程序之前需要做如下准备工作:
- 注册小程序
- 完善小程序信息
- 下载开发者工具
- 注册小程序
注册地址:https://mp.weixin.qq.com/wxopen/waregister?action=step1 - 完善小程序信息
完善小程序信息、小程序类目
记得保存微信小程序的AppID和密钥
- 下载开发者工具
下载地址: https://developers.weixin.qq.com/miniprogram/dev/devtools/stable.html
微信开发者工具界面
由于微信小程序开发主要是前端代码,所以我们直接导入提供的前端代码, 并需要将不校验合法域名勾选
微信登陆流程
微信小程序开发提供了微信登陆的官方文档,我们需要根据这个官方文档来开发微信登陆功能
微信官方文档: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
后端开发步骤
- 在小程序调用wx.login()获得code,这个code是授权码,然后将这个code通过wx.request()发送给Developer Service,也就是我们编写的后端服务
- 后端拿到了授权码code后,通过Httpclient调用Wechat Http Api,调用这个微信接口服务时,需要传参数(appid,appsecret,code),微信接口服务通过Httpclient返回session_key和openid,这个openid就是微信用户的唯一标识
- 后端获取到openid之后,后端可以通过这个openid自定义操作,譬如产生token记录当前用户,并将这个自定义登陆状态,返回给小程序
- 小程序通过后端返回的自定义登陆状态发起业务请求(业务请求中包含自定义登陆状态),后端收到业务请求后,会根据自定义登陆状态进行业务操作返回相应业务数据
总结
- 调用 wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
- 调用 auth.code2Session 接口,换取 用户唯一标识 OpenID 、 用户在微信开放平台账号下的唯一标识UnionID(若当前小程序已绑定到微信开放平台账号) 和 会话密钥 session_key。
之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。
代码开发
增加相关配置项
首先需要配置微信登陆配置项,包括
- 调用微信接口服务接口所需要的Appid和AppSecret
- 为微信用户生成jwt令牌时使用的配置项,管理端和用户端令牌分开配置
sky:
jwt:
#用户端令牌
user-secret-key: itheima
user-ttl: 7200000
user-token-name: authentication # 这个名称是与前端约定好的
wechat:
appid: ${sky.wechat.appid}
secret: ${sky.wechat.secret}
需求设计和具体代码实现
通过接口设计我们知道,这是一个Post请求方式,传递数据使用UserLoginDTO,该DTO只有一个code变量,对应接口设计中的请求体body中的code,返回数据封装成UserLoginVO,与接口设计的返回数据中的data变量一一对应。
后端根据前端传过来的code调用微信接口服务获取到当前登陆用户的信息,然后后端会根据用户信息生成jwt令牌,然后后续该用户的操作都会绑定该令牌,后端通过校验该令牌来确定前端请求的操作是合法的,校验令牌的操作是通过拦截器来实现的。
/**
* 微信登陆
* @param userLoginDTO
* @return
*/
@PostMapping("/login")
@ApiOperation("微信登陆")
public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO) { // @RequestBody 是将JSON对象反序列化为java对象
log.info("微信用户登陆:{}", userLoginDTO.getCode());
// 微信登陆
User user = userService.wxLogin(userLoginDTO);
// 为微信用户生成jwt令牌,调用工具类JwtUtil
Map<String, Object> map = new HashMap<>();
// 用户的唯一标识user.getId()
map.put(JwtClaimsConstant.USER_ID, user.getId());
String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), map);
// 最终需要返回VO,所以我们还需要封装VO
UserLoginVO userLoginVO = UserLoginVO.builder()
.id(user.getId())
.openid(user.getOpenid())
.token(token)
.build();
return Result.success(userLoginVO);
}
我们接收到前端传来的code,在后端我们需要通过Httpclient访问Wechat Http Api来获取session_key和Openid,这部分代码逻辑我们在userService.java中完成,并将获得的session_key和openid存储在实体User中。
在userServiceImpl.java中的wxlogin方法中,主要完成了通过HttpClient访问微信接口服务,并获得了当前登陆用户的openId,该openid是登陆用户的唯一标识,然后根据该openid在User表中查询当前用户的相关数据,并返回给UserController.java中的login方法,然后传递到前端小程序。具体代码开发看下面的代码块:
userServiceImpl.java
public class UserServiceImpl implements UserService {
public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";
@Autowired
private WeChatProperties weChatProperties; // 微信小程序的配置类,通过这个配置类读取微信小程序的AppID和Secret
@Autowired
private UserMapper userMapper;
/**
* 微信登陆
* @param userLoginDTO
* @return
*/
public User wxLogin(UserLoginDTO userLoginDTO) {
// 通过HttpClient调用微信接口服务,获取当前微信用户的openid,项目中有封装好了的HttpClient工具类,HttpClientUtil.java
// 通过配置属性类读出 配置好的微信小程序的appid和secret值
Map<String, String> map = new HashMap<>();
map.put("appid", weChatProperties.getAppid());
map.put("secret", weChatProperties.getSecret());
map.put("js_code", userLoginDTO.getCode());
map.put("grant_type", "authorization_code");
String json = HttpClientUtil.doGet(WX_LOGIN, map);
// 微信接口服务返回的是JSON类型的数据格式,我们需要将JSON格式转成Java对象,然后通过getter方法取出对象的属性
JSONObject jsonObject = JSONObject.parseObject(json);
String openid = jsonObject.getString("openid");
// 判断openid是否为空,如果空表示登陆失败,跑出业务异常
if(openid==null){
throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
}
// 我们需要判断当前用户是否为新用户,也就是openid是否在我们的User表中,需要使用UserMapper.java
// 如果不为空,说明当前微信用户的openid存在,不为新用户
// 如果是新用户,自动完成注册,注册需要创建一个user对象,并存储在User表中
User user = userMapper.getByOpenId(openid);
if(user==null){ // 新用户,完成注册
user = User.builder()
.openid(openid)
.createTime(LocalDateTime.now())
.build();
userMapper.insert(user); // 这里的插入操作,需要返回user表的id,因为在userController中使用到了id,
// 所以在mapper层编写MyBatis需要使用useGeneratedKeys="true" keyProperty="id
}
// 返回用户对象
return user;
}
}
导入商品浏览功能代码
这部分代码,与第四天的代码差别不大,在导入好代码之后,如果出现401报错,大概率是拦截器没有设置好,需要将拦截器中的admin全部换成user,检查一遍一般就能发现问题。