HttpClient
介绍
HttpClient 是 Apache 提供的一个开源的 Java HTTP 客户端库,用于发送 HTTP 请求和处理 HTTP 响应。它提供了一种更简便的方式来执行 HTTP 请求,并支持多种协议,如 HTTP、HTTPS、FTP 等。
使用 HttpClient 可以方便地与远程服务器进行通信,发送 HTTP 请求并处理响应。在实际应用中,HttpClient 常被用于与 RESTful API 交互、爬虫开发、测试等场景。
入门案例
新建一个test类进行测试
package com.sky.test;import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import java.io.IOException;
import java.io.UnsupportedEncodingException;@SpringBootTest
public class HttpClientTest {/*** 测试通过HttpClientTest发送GET请求*/@Testpublic void testGet() throws Exception{//创建HTTP对象CloseableHttpClient httpClient = HttpClients.createDefault();//创建请求对象HttpGet httpGet = new HttpGet("http://localhost:8080/user/shop/status");//发送请求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();}/*** 测试通过HttpClientTest发送POST请求*/@Testpublic void testPost() throws Exception {//创建HTTP对象CloseableHttpClient httpClient = HttpClients.createDefault();//创建请求对象HttpPost httpPost = new HttpPost("http://localhost:8080/admin/employee/login");//构造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();}
}
微信小程序开发
介绍
准备工作
注册小程序
登录微信小程序平台按照提示进行注册
完善小程序信息
登录后进入开发管理和管理,根据页面提示信息补充小程序各项基本信息并生成密钥。
下载开发者工具
根据提示创建小程序
入门案例
获取用户信息
<!--index.wxml-->
<navigation-bar title="Weixin" back="{{false}}" color="black" background="#FFF"></navigation-bar>
<scroll-view class="scrollarea" scroll-y type="list"><view class="container"><!-- 展示动态信息 --><view>{{meg}}</view><button type="primary" bind:tap="getUserInfo">获取用户信息</button>昵称: {{nickName}}<image src="{{url}}" style="width: 100px;height: 100px;"></image></view>
</scroll-view>
// index.js
Page({data:{meg:'hello world',nickName:'',url:''},//获取微信用户头像和昵称getUserInfo(){wx.getUserProfile({desc: '获取用户信息',success: (res) =>{console.log(res.userInfo)//为数据赋值this.setData({nickName: res.userInfo.nickName,url: res.userInfo.avatarUrl})}})}})
其中需要调整数据库到2.7以下版本。具体路径为右上角详情->本地设置->调试基础库。
获取用户授权码
data:{meg:'hello world',nickName:'',url:'',code:''},//微信登录,获取微信登录的授权码wxlogin(){wx.login({success: (res) => {console.log(res.code)this.setData({code: res.code})},})},
<view><button type="warn" bind:tap="wxlogin">微信登录</button>授权码:{{code}}</view>
发送请求
//发送请求sendRequest(){wx.request({url: 'http://localhost:8080/user/shop/status',method:'GET',success: (res)=>{//代表后端响应的整个JSON数据console.log(res.data)}})}
<view><button type="default" bind:tap="sendRequest">发送请求</button></view>
微信登录
微信登录流程
需求分析
代码开发
openid是微信用户的唯一标识
新建Controller层
@RestController
@RequestMapping("/user/user")
@Api(tags = "C端用户相关接口")
@Slf4j
public class UserController {@AutowiredUserService userService;@Autowiredprivate JwtProperties jwtProperties;/*** 微信登录* @param userLoginDTO* @return*/@PostMapping("/login")@ApiOperation("微信登录")public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){log.info("微信用户登录:{}",userLoginDTO.getCode());//微信登录User user = userService.wxLogin(userLoginDTO);//为微信用户生成令牌Map<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实现类
@Service
@Slf4j
public class UserServiceImpl implements UserService {//微信服务接口登录地址public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";@Autowiredprivate WeChatProperties weChatProperties;@Autowiredprivate UserMapper userMapper;/*** 微信登录* @param userLoginDTO* @return*/@Overridepublic User wxLogin(UserLoginDTO userLoginDTO) {String openid = getOpenid(userLoginDTO.getCode());//判断openid是否为空,如果为空表示登录失败,抛出业务异常if(openid == null){throw new LoginFailedException(MessageConstant.LOGIN_FAILED);}//是否是新用户User user = userMapper.getByOpenID(openid);//是新用户,自动完成注册if (user==null){user = User.builder().openid(openid).createTime(LocalDateTime.now()).build();}userMapper.insert(user);//返回用户对象return user;}private String getOpenid(String code){//调用微信服务器接口服务,获得当前微信服务的openidMap<String,String> map = new HashMap<>();map.put("appid",weChatProperties.getAppid());map.put("secret", weChatProperties.getSecret());map.put("js_code", code);map.put("grant_type", "authorization_code");String json = HttpClientUtil.doGet(WX_LOGIN, map);JSONObject jsonObject = JSON.parseObject(json);String openid = jsonObject.getString("openid");return openid;}
}
Mapper接口
@Mapper
public interface UserMapper {@Select("select * from user where openid= #{openid}")User getByOpenID(String openid);/*** 插入数据* @param user*/void insert(User user);
}
后端对应的xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sky.mapper.UserMapper"><insert id="insert" useGeneratedKeys="true" keyProperty="id">insert into user (openid, name, phone, sex, id_number, avatar, create_time) VALUES(#{openid},#{name},#{phone},#{sex},#{idNumber},#{avatar},#{createTime})</insert>
</mapper>
配置web层、拦截器组件学习
对注射web层中WebMvcConfiguration的理解
/*** 设置静态资源映射* 当访问 /doc.html路径时,Spring MVC将会去 classpath:/META-INF/resources/目录下寻找对应的静态资源* 而访问 /webjars/** 路径时,将会去 classpath:/META-INF/resources/webjars/目录下寻找对应的静态资源。* 这样做的好处是可以将一些静态资源集中存放在指定的目录,而不需要暴露给外部直接访问项目的文件结构。* 这有助于更好地组织项目结构,同时提供对静态资源的有效管理和映射。* @param registry*/protected void addResourceHandlers(ResourceHandlerRegistry registry) {log.info("开始设置静态资源映射");registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");/*** 扩展Spring MVC框架的消息转换器* 将 Java 对象转换为 JSON 数据:当服务器端需要将 Java对象转换为 JSON数据* 以便于发送给客户端时,消息转换器负责将 Java对象序列化为JSON格式的数据。* 将 JSON数据转换为 Java对象:当客户端发送包含JSON数据的请求体给服务器时* 消息转换器负责将接收到的JSON数据反序列化为对应的 Java对象。* @param converters*/@Overrideprotected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {log.info("扩展消息转换器..");//创建一个消息转换器对象MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();//需要为消息转换器设置一个对象转换器,可以将Java对象序列化为json数据converter.setObjectMapper(new JacksonObjectMapper());//将自己的消息转换器加入容器中converters.add(0,converter);}/*** 注册自定义拦截器* 第一个拦截器 jwtTokenAdminInterceptor 注册在路径 /admin/**下,但排除了路径/admin/employee/login。* 第二个拦截器 jwtTokenUserInterceptor 注册在路径 /user/**下,但排除了路径/user/user/login和/user/shop/status。* 这样配置的效果是,当请求路径匹配拦截路径时,会触发相应的拦截器执行相应的逻辑。拦截器可以用于处理请求前的预处理、日志记录、权限验证等工作。* 通过拦截器来验证 JWT 令牌,确保请求的合法性,这在安全性要求较高的 Web 应用中是常见的做法。* @param registry*/protected void addInterceptors(InterceptorRegistry registry) {log.info("开始注册自定义拦截器...");registry.addInterceptor(jwtTokenAdminInterceptor).addPathPatterns("/admin/**").excludePathPatterns("/admin/employee/login");registry.addInterceptor(jwtTokenUserInterceptor).addPathPatterns("/user/**").excludePathPatterns("/user/user/login").excludePathPatterns("/user/shop/status");}
对jwt拦截器的理解
/*** jwt令牌校验的拦截器*/
@Component
@Slf4j
public class JwtTokenUserInterceptor implements HandlerInterceptor {@Autowiredprivate JwtProperties jwtProperties;/*** 校验jwt** @param request* @param response* @param handler* @return* @throws Exception*/public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//获取当前线程idSystem.out.println("当前线程id"+Thread.currentThread().getId());//判断当前拦截到的是Controller的方法还是其他资源if (!(handler instanceof HandlerMethod)) {//当前拦截到的不是动态方法,直接放行return true;}//1、从请求头中获取令牌String token = request.getHeader(jwtProperties.getUserTokenName());//2、校验令牌try {log.info("jwt校验:{}", token);//这个方法是自定义的 JWT 解析方法,接受两个参数,第一个是用于解密的密钥//第二个是待解析的 JWT 令牌 (token)。该方法返回一个 Claims 对象,其中包含了 JWT 中的声明信息。Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);//这一行代码从 Claims 对象中获取用户ID的声明,并将其转换成Long类型。在JWT的声明中,通常会包含一些声明(比如过期时间、签发者等)// 同时也会包含自定义的声明。在这里,通过 JwtClaimsConstant.USER_ID 来获取用户ID的声明。Long userId = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());log.info("当前用户id:", userId);//在拦截器中存入//这行代码的作用是将用户ID存入线程上下文。线程上下文是一个与线程关联的数据存储区域,可以在整个线程的生命周期内共享数据。//在这个具体的场景中,当用户的 JWT 令牌验证通过后,将用户的ID存入线程上下文,以便在后续的业务逻辑中能够方便地获取当前用户的ID,//而不必在每个方法参数中传递用户ID或者从其他地方再次获取。这样做的好处是简化了代码,提高了代码的可读性和可维护性。//举例来说,如果有其他地方需要使用当前用户的ID,可以通过 BaseContext.getCurrentId() 获取,//而不必传递用户ID的参数。这在涉及多个方法、类之间需要传递用户ID的情况下,可以减少重复代码,提高开发效率。BaseContext.setCurrentId(userId);//3、通过,放行return true;} catch (Exception ex) {//4、不通过,响应401状态码response.setStatus(401);return false;}}
}
导入商品浏览功能
需求分析
代码实现
C端-分类接口
Controller层注意对注释进行命名用来区别admin的相同请求,具体实现方法可以通用。
@RestController("userCategoryController")
@Api(tags = "C端-分类接口")
@RequestMapping("/user/category")
@Slf4j
public class CategoryController {@Autowiredprivate CategoryService categoryService;// Spring MVC 默认会按照参数名字和请求中的参数名字进行匹配。在你的情况下,接口路径为 /list// 而请求参数为 type,由于参数名字和请求中的参数名字一致,Spring MVC 可能会自动将参数值绑定到方法的参数上。//这是Spring MVC 的一种简化规则,适用于一些简单的情况。@GetMapping("/list")@ApiOperation("条件查询")/*** 条件查询*/public Result<List<Category>> list(Integer type){List<Category> list = categoryService.list(type);return Result.success(list);}
}
C端-菜品浏览接口
Controller层
@RestController("userDishController")
@Api(tags = "C端-菜品浏览接口")
@RequestMapping("/user/dish")
@Slf4j
public class DishController {@Autowiredprivate DishService dishService;/*** 根据分类id查询菜品* @param categoryId* @return*/@GetMapping("/list")@ApiOperation("根据分类id查询菜品")private Result<List<DishVO>> list(Long categoryId){List<DishVO> dishVOS = dishService.listWithFlavor(categoryId);return Result.success(dishVOS);}
Service实现层
/*** 根据分类id查询菜品以及口味* @param categoryId* @return*/@Overridepublic List<DishVO> listWithFlavor(Long categoryId) {Dish dish = Dish.builder().categoryId(categoryId).status(StatusConstant.ENABLE).build();List<Dish> dishList = dishMapper.list(dish);List<DishVO> dishVOList = new ArrayList<>();for (Dish d : dishList){DishVO dishVO = new DishVO();BeanUtils.copyProperties(d, dishVO);List<DishFlavor> dishFlavorList = dishFlavorMapper.getByDishId(d.getId());dishVO.setFlavors(dishFlavorList);dishVOList.add(dishVO);}return dishVOList;}
C端-套餐浏览接口
Controller层
@RestController("userSetmealController")
@Api(tags = "C端-套餐浏览接口")
@RequestMapping("/user/setmeal")
@Slf4j
public class SetmealController {@Autowiredprivate SetmealService setmealService;/*** 套餐浏览接口* @param categoryId* @return*/@GetMapping("/list")@ApiOperation("根据分类id查询套餐")public Result<List<Setmeal>> list(Long categoryId){List<Setmeal> setmeals = setmealService.list(categoryId);return Result.success(setmeals);}/*** 根据套餐id查询包含的菜品* @param id* @return*/@ApiOperation("根据套餐id查询包含的菜品")@GetMapping("/dish/{id}")public Result<List<DishItemVO>> dishList(@PathVariable("id") Long id){List<DishItemVO> dishItemVOS = setmealService.dishList(id);return Result.success(dishItemVOS);}
Service层实现类
/*** 根据分类id查询菜品* @param categoryId* @return*/@Overridepublic List<Setmeal> list(Long categoryId) {Setmeal setmeal = new Setmeal();setmeal.setCategoryId(categoryId);setmeal.setStatus(StatusConstant.ENABLE);List<Setmeal> setmeals = setmealMapper.list(setmeal);return setmeals;}/*** 根据套餐id查询包含的菜品* @param id* @return*/@Overridepublic List<DishItemVO> dishList(Long id) {return setmealMapper.dishList(id);}
Mapper接口
/*** 根据分类id查询菜品* @param Setmeal* @return*/List<Setmeal> list(Setmeal setmeal);/*** 根据套餐id查询包含的菜品* @param setmealId* @return*/@Select("select sd.name,sd.copies,d.image,d.description from setmeal_dish sd left join dish d on sd.dish_id = d.id " +"where sd.setmeal_id = #{setmealId}")List<DishItemVO> dishList(Long setmealId);
XML底层
<select id="list" resultType="com.sky.entity.Setmeal" parameterType="Setmeal">select * from setmeal<where><if test="name != null">and name like concat('%',#{name},'%')</if><if test="categoryId != null">and category_id = #{categoryId}</if><if test="status != null">and status = #{status}</if></where></select>