【苍穹外卖】Day 6 HttpClient、wx小程序
1 HttpClient
HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议
HttpClient 是一个用于发送 HTTP 请求并接收响应的类或库,在不同的编程语言中有不同的实现。它通常用于与 Web 服务进行交互,尤其是在 RESTful API 的场景下。HttpClient 能够处理各种类型的 HTTP 请求,如 GET
、POST
、PUT
、DELETE
等,还可以设置请求头、超时、身份验证等
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
核心API:
- HttpClient
- HttpClients
- CloseableHttpClient
- HttpGet
- HttpPost
发送请求步骤:
- 创建 HttpClient 对象
- 创建 Http 请求对象(如GET、POST)
- 调用 HttpClient 的 execute 方法发送请求
1.1 GET
首先运行springboot,然后开启redis server
运行测试类:
如果改成访问 admin,就会失败,因为没有 jwt 令牌
1.2 POST
2 wx小程序
使用 微信开发者工具
2.1 入门案例
小程序目录结构:
包含一个描述整体程序的 app 和多个描述各自页面的 page
一个小程序 主体部分 由三个文件组成,必须放在项目的根目录:
文件 | 是否必需 | 作用 |
---|---|---|
app.js | 是 | 小程序逻辑 |
app.json | 是 | 小程序公共配置 |
app.wxss | 否 | 小程序公共样式表 |
wxss:wx Style Sheets
css:Cascading Style Sheets 层叠样式表,CSS 用于控制网页的外观和布局,通过为 HTML 元素设置样式属性(如颜色、字体、排版和布局等),从而实现网页的美化与响应式设计
一个小程序 页面 由四个文件组成:
文件类型 | 是否必需 | 作用 |
---|---|---|
js | 是 | 页面逻辑 |
wxml | 是 | 页面结构 |
json | 否 | 页面配置 |
wxss | 否 | 页面样式表 |
页面逻辑
// index.js
Page({
data: {
msg: 'hello',
nickName: '',
img: '',
authorizationCode: '',
status: ''
},
//获取用户头像和昵称
getUserInfo() {
wx.getUserProfile({
desc: '获取用户头像和昵称',
success: (res) => {
console.info(res.userInfo)
this.setData({
nickName: res.userInfo.nickName,
img: res.userInfo.avatarUrl
})
}
})
},
login() {
wx.login({
success: (res) => {
this.setData({
authorizationCode: res.code
})
},
})
},
sendRequest() {
wx.request({
url: 'http://localhost:8080/user/shop/status',
method: 'GET',
success: (res) => {
console.log(res.data)
this.setData({
status: res.data
})
}
})
}
})
页面结构
<!--index.wxml-->
<navigation-bar title="Weixin" back="{{false}}" color="black" background="#FFF"></navigation-bar>
<views>
<view class="container">
{{msg}}
</view>
<view>
<button type="primary" bind:tap="getUserInfo">get user info</button>
nickname:{{nickName}}
<image style="width: 30px ;height: 30px;" src="{{img}}"></image>
</view>
<view>
<button bind:tap="login" type="default">login</button>
authorizationCode: {{authorizationCode}}
</view>
<view>
<button bind:tap="sendRequest" type="warn">send request to get status</button>
status is [{{status}}]
</view>
</views>
效果:
审核中
2.2 微信登录 接口设计
时序图
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
接口设计:
第一个 user 代表 用户端
第二个 user 代表 用户模块
实现:
配置wx
配置客户端的 jwt 校验
在配置属性类里面已经规定好了
数据封装
DTO
要传输的数据
VO
要展示、要返回的数据
2.3 实现
小程序登录 / 小程序登录 (qq.com)
controller
package com.sky.controller.user;
import com.sky.constant.JwtClaimsConstant;
import com.sky.dto.UserLoginDTO;
import com.sky.entity.User;
import com.sky.properties.JwtProperties;
import com.sky.result.Result;
import com.sky.service.UserService;
import com.sky.utils.JwtUtil;
import com.sky.vo.UserLoginVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
@RestController
@RequestMapping("/user/user")
@Api(tags = "用户相关接口")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private JwtProperties jwtProperties;
/**
* 用户微信登录
*
* @return
*/
@PostMapping("/login")
@ApiOperation("用户微信登录")
public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO) {
// 微信登录
User user = userService.wxLogin(userLoginDTO);
// 为微信用户生成 jwt 令牌
HashMap<String, Object> claims = new HashMap<>();
claims.put(JwtClaimsConstant.USER_ID, user.getId());
String token = JwtUtil.createJWT(
jwtProperties.getUserSecretKey(),
jwtProperties.getUserTtl(),
claims
);
UserLoginVO userLoginVO = UserLoginVO.builder()
.id(user.getId())
.openid(user.getOpenid())
.token(token)
.build();
return Result.success(userLoginVO);
}
}
service
package com.sky.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.sky.constant.MessageConstant;
import com.sky.dto.UserLoginDTO;
import com.sky.entity.User;
import com.sky.exception.LoginFailedException;
import com.sky.mapper.UserMapper;
import com.sky.properties.WeChatProperties;
import com.sky.service.UserService;
import com.sky.utils.HttpClientUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@Service
public class UserServiceImpl implements UserService {
// 微信服务接口地址
public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";
@Autowired
private WeChatProperties weChatProperties;
@Autowired
private UserMapper userMapper;
/**
* 微信登录
*
* @param userLoginDTO
* @return
*/
public User wxLogin(UserLoginDTO userLoginDTO) {
//调用微信接口服务,获得当前微信用户的 openid
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 string = HttpClientUtil.doGet(WX_LOGIN, map);
// fastJSON 解析 JSON 对象
JSONObject jsonObject = JSONObject.parseObject(string);
// 通过 key,访问 openid
String openid = jsonObject.getString("openid");
//判断 openid 是否为空,如果为空,要抛出登录失败异常
if (openid == null) {
throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
}
//判断当前用户是否为新用户(数据库查询是否包含openid)
User user = userMapper.getByOpenid(openid);
//新用户要注册
if (user == null) {
user = User.builder()
.openid(openid)
.createTime(LocalDateTime.now())
.build();
userMapper.add(user);
}
return user;
}
}
mapper
package com.sky.mapper;
import com.sky.entity.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UserMapper {
/**
* 根据 openid 查询用户
*
* @param openid
* @return
*/
@Select("select * from sky_take_out.user where openid = #{openid}")
User getByOpenid(String openid);
/**
* 新增用户
* 这里要注意要返回主键
*
* @param newUser
*/
void add(User newUser);
}
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.UserMapper">
<insert id="add" useGeneratedKeys="true" keyProperty="id">
insert into sky_take_out.user
(openid, name, phone, sex, id_number, avatar, create_time)
values (#{openid}, #{name}, #{phone}, #{sex}, #{idNumber}, #{avatar}, #{createTime})
</insert>
</mapper>
2.4 测试
3 商品浏览
注意这里,名称要 与前端保持一致