苍穹外卖Day06

HttpClient

介绍

HttpClient 是Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。

  • 作用

发送HTTP请求

接收响应数据

  • 应用场景

扫描支付、查看地图、获取验证码、查看天气等功能时

应用程序本身并未实现这些功能,都是在应用程序里访问提供这些功能的服务,访问这些服务需要发送HTTP请求,并且接收响应数据

  • maven坐标
<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpclient</artifactId>
	<version>4.5.13</version>
</dependency>
  • 核心API

HttpClient:Http客户端对象类型,使用该类型对象可发起Http请求。

HttpClients:可认为是构建器,可创建HttpClient对象。

CloseableHttpClient:实现类,实现了HttpClient接口。

HttpGet:Get方式请求类型。

HttpPost:Post方式请求类型。

  • 发送请求步骤

创建HttpClient对象

创建Http请求对象

调用HttpClient的execute方法发送请求

项目入门

阿里云OSS的maven里面依赖了HttpClient,就不用导入相关的API

GET请求

  1. 创建HttpClient对象
  2. 创建请求对象
  3. 发送请求,接受响应结果
  4. 解析结果
  5. 关闭资源
@SpringBootTest(classes = SkyApplication.class)
public class HttpClientTest {

    /**
     * 测试通过httpclient发送GET方式的请求
     */
    @Test
    public void testGET() throws Exception{
        //创建httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //创建请求对象
        HttpGet httpGet = new HttpGet("http://localhost:8080/admin/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();
    }
}

POST请求

  1. 创建HttpClient对象
  2. 创建请求对象
  3. 发送请求,接收响应结果
  4. 解析响应结果
  5. 关闭资源
	public void testPOST() throws Exception{
        // 创建httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //创建请求对象
        HttpPost httpPost = new HttpPost("http://localhost:8080/admin/employee/login");
        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();
    }

微信小程序开发

官方网址:微信小程序

注册地址:小程序

登录小程序后台:微信公众平台/

完善小程序信息、小程序类目,查看小程序的 AppID

下载开发者工具:

下载地址: 微信开发者工具(稳定版 Stable Build)下载地址与更新日志 | 微信开放文档

微信登录

登录流程

用户进入到小程序的时候,微信授权登录之后才能点餐。需要获取当前微信用户的相关信息,比如昵称、头像等,这样才能够进入到小程序进行下单操作。是基于微信登录来实现小程序的登录功能,没有采用传统账户密码登录的方式。若第一次使用小程序来点餐,就是一个新用户,需要把这个新的用户保存到数据库当中完成自动注册。

步骤分析:

  1. 小程序端,调用wx.login()获取code,就是授权码。
  2. 小程序端,调用wx.request()发送请求并携带code,请求开发者服务器(自己编写的后端服务)。
  3. 开发者服务端,通过HttpClient向微信接口服务发送请求,并携带appId+appsecret+code三个参数。
  4. 开发者服务端,接收微信接口服务返回的数据,session_key+opendId等。opendId是微信用户的唯一标识。
  5. 开发者服务端,自定义登录态,生成令牌(token)和openid等数据返回给小程序端,方便后绪请求身份校验。
  6. 小程序端,收到自定义登录态,存储storage。
  7. 小程序端,后绪通过wx.request()发起业务请求时,携带token。
  8. 开发者服务端,收到请求后,通过携带的token,解析当前登录用户的id。
  9. 开发者服务端,身份校验通过后,继续相关的业务逻辑处理,最终返回业务数据。

说明:

  1. 调用 wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
  2. 调用 auth.code2Session 接口,换取 用户唯一标识 OpenID 、 用户在微信开放平台帐号下的唯一标识UnionID(若当前小程序已绑定到微信开放平台帐号) 和 会话密钥 session_key

之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。

注:开发阶段,小程序发出请求到后端的Tomcat服务器,若不勾选,请求发送失败。

相关配置

  • application-dev.yml
sky:
  wechat:
    appid: wxffb3637a228223b8
    secret: 84311df9199ecacdf4f12d27b6b9522d
  • application.yml
sky:
  wechat:
    appid: ${sky.wechat.appid}
    secret: ${sky.wechat.secret}
  • 配置为微信用户生成jwt令牌
sky:
  jwt:
    # 设置jwt签名加密时使用的秘钥
    admin-secret-key: guslegend
    # 设置jwt过期时间
    admin-ttl: 7200000
    # 设置前端传递过来的令牌名称
    admin-token-name: token
    user-secret-key: guslegend
    user-ttl: 7200000
    user-token-name: authentication

代码开发

  • 定义VO,DTO
//DTO
public class UserLoginDTO implements Serializable {

    private String code;

}

//VO
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserLoginVO implements Serializable {

    private Long id;
    private String openid;
    private String token;

}
  • controller
 public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){
        log.info("微信用户登录:{}",userLoginDTO.getCode());
        //微信登录
        User user = userService.wxLogin(userLoginDTO);
        //为微信用户生成jwt令牌
        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
/**
 * jwt令牌校验的拦截器
 */
@Component
@Slf4j
public class JwtTokenUserInterceptor implements HandlerInterceptor {

    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 校验jwt
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //判断当前拦截到的是Controller的方法还是其他资源
        if (!(handler instanceof HandlerMethod)) {
            //当前拦截到的不是动态方法,直接放行
            return true;
        }

        //1、从请求头中获取令牌
        String token = request.getHeader(jwtProperties.getUserTokenName());

        //2、校验令牌
        try {
            log.info("jwt校验:{}", token);
            Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);
            Long userId = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());
            log.info("当前用户的id:", userId);
            BaseContext.setCurrentId(userId);
            //3、通过,放行
            return true;
        } catch (Exception ex) {
            //4、不通过,响应401状态码
            response.setStatus(401);
            return false;
        }
    }
}
  • mapper
<select id="getByOpenid" resultType="com.guslegend.entity.User">
        select * from user where openid = #{openid}
</select>

    <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>

拦截器

编写拦截器JwtTokenUserInterceptor:统一拦截用户端发送的请求并进行jwt校验

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{
        //判断当前拦截到的是Controller的方法还是其他资源
        if (!(handler instanceof HandlerMethod)) {
            //当前拦截到的不是动态方法,直接放行
            return true;
        }
        //1、从请求头中获取令牌
        String token = request.getHeader(jwtProperties.getUserTokenName());
        //2、校验令牌
        try {
            log.info("jwt校验:{}", token);
            Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);
            Long userId = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());
            log.info("当前用户的id:", userId);
            BaseContext.setCurrentId(userId);
            //3、通过,放行
            return true;
        } catch (Exception ex) {
            //4、不通过,响应401状态码
            response.setStatus(401);
            return false;
        }
    }
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","/user/shop/status");
    }

导入商品浏览模块

### 关于苍穹外卖 Day06 的教程或资料 目前,关于苍穹外卖项目的官方文档和公开资源主要集中在前五天的内容上[^2]。然而,在实际开发过程中,后续的课程通常会涉及更复杂的业务逻辑和技术细节,例如分布式事务处理、微服务架构优化以及性能调优等内容。 如果需要了解 **苍穹外卖 Day06** 的相关内容,可以尝试以下几个方向: #### 1. 微服务框架扩展 在第六天的学习中,可能会深入讲解如何通过 Spring Cloud 或 Dubbo 实现微服务之间的通信机制。这部分内容可能包括但不限于: - 使用 Feign 进行声明式 REST 调用。 - 配置负载均衡策略以提高系统的可用性和稳定性。 ```java @FeignClient(name = "order-service") public interface OrderServiceClient { @GetMapping("/orders/{id}") public ResponseEntity<Order> getOrderById(@PathVariable Long id); } ``` 上述代码片段展示了如何定义一个简单的 Feign 客户端来调用远程订单服务。 --- #### 2. 数据库分片与读写分离 随着项目规模的增长,数据库的压力也会逐渐增大。因此,Day06 可能会引入 ShardingSphere 或 MyCat 来实现数据分片和读写分离的功能。以下是配置文件的一个简单示例: ```yaml spring: shardingsphere: datasource: names: ds_0,ds_1 ds_0: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://localhost:3306/db_0?serverTimezone=UTC&useSSL=false username: root password: 123456 ds_1: type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://localhost:3306/db_1?serverTimezone=UTC&useSSL=false username: root password: 123456 ``` 此部分重点在于提升数据库访问效率并降低单点故障风险。 --- #### 3. 缓存设计优化 缓存作为高性能系统的重要组成部分,其合理应用能够显著减少数据库查询次数。在 Day06 中,预计会对 Redis 缓存的设计进一步深化,比如利用 Redis Stream 处理实时消息队列或者采用布隆过滤器防止缓存穿透等问题。 以下是一个基于 RedisConfiguration 类的基础设置实例: ```java @Configuration public class RedisConfiguration { @Bean public LettuceConnectionFactory redisConnectionFactory() { return new LettuceConnectionFactory(); } @Bean public StringRedisTemplate stringRedisTemplate(LettuceConnectionFactory factory) { StringRedisTemplate template = new StringRedisTemplate(factory); return template; } } ``` 这段代码用于初始化 Redis 连接池及相关模板工具类。 --- #### 4. 日志监控体系搭建 为了更好地追踪线上问题,构建完善的日志收集与分析平台显得尤为重要。ELK (Elasticsearch, Logstash, Kibana) 堆栈可能是该阶段的重点之一,帮助开发者快速定位异常情况并提供可视化报表支持。 --- 尽管当前未找到明确标注为“苍穹外卖 Day06”的具体材料[^1],但从整体技术路线推测以上几个方面均有可能成为教学的核心主题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值