前言

        今天的内容主要是简单了解在java中使用HttpClient发送请求,了解微信小程序开发及完成项目用户端的微信登录功能,导入商品浏览数据。

  • HttpClient

  • 微信小程序开发

  • 微信登录

  • 导入商品浏览功能代码

《苍穹外卖day04》:学生党学习笔记——黑马程序员Java项目实战《苍穹外卖》——Day05 redis&店铺状态-CSDN博客


视频学习链接黑马程序员Java项目实战《苍穹外卖》,最适合新手的SpringBoot+SSM的企业级Java项目实战_哔哩哔哩_bilibili

资料网盘链接1、黑马程序员Java项目《苍穹外卖》企业级开发实战_免费高速下载|百度网盘-分享无限制

本人项目远程仓库sky-take-out: 黑马程序员Java项目实战《苍穹外卖》,最适合新手的SpringBoot+SSM的企业级Java项目实战


HttpClient

介绍

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

HttpClient作用

  • 发送HTTP请求

  • 接收响应数据

HttpClient应用场景:

当我们在使用扫描支付、查看地图、获取验证码、查看天气等功能时(调用第三方接口)

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

HttpClient的maven坐标:

<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpclient</artifactId>
	<version>4.5.13</version>
</dependency>

HttpClient的核心API:

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

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

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

  • HttpGet:Get方式请求类型。

  • HttpPost:Post方式请求类型。

HttpClient发送请求步骤:

  • 创建HttpClient对象

  • 创建Http请求对象

  • 调用HttpClient的execute方法发送请求

入门案例

        对HttpClient编程工具包有了一定了解后,那么,我们使用HttpClient在Java程序当中来构造Http的请求,并且把请求发送出去,接下来,就通过入门案例分别发送GET请求POST请求,具体来学习一下它的使用方法。

GET请求方式

        项目中由于导入"aliyun-sdk-oss ",该依赖底层包含HttpClient相关依赖

故选择导入或者不导入均可。

实现步骤

  1. 创建HttpClient对象

  2. 创建请求对象

  3. 发送请求,接受响应结果

  4. 解析结果

  5. 关闭资源

@SpringBootTest
public class HttpClientTest {

    /**
     * 测试通过httpclient发送GET方式的请求
     */
    @Test
    public void testGET() throws Exception{
        //创建httpclient对象
        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();
    }
}

注意:在访问http://localhost:8080/user/shop/status请求时,需要提前启动项目。

测试结果:


POST方式请求

相比GET请求来说,POST请求若携带参数需要封装请求体对象,并将该对象设置在请求对象中。

实现步骤:

  1. 创建HttpClient对象

  2. 创建请求对象

  3. 发送请求,接收响应结果

  4. 解析响应结果

  5. 关闭资源

/**
     * 测试通过httpclient发送POST方式的请求
     */
    @Test
    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();
    }

测试结果:


微信小程序开发

介绍

        小程序是一种新的开放能力,开发者可以快速地开发一个小程序。可以在微信内被便捷地获取和传播,同时具有出色的使用体验。

官方网址:https://mp.weixin.qq.com/cgi-bin/wx?token=&lang=zh_CN

小程序主要运行微信内部,可通过上述网站来整体了解微信小程序的开发。


准备工作

开发微信小程序之前需要做如下准备工作:

  • 注册小程序

  • 完善小程序信息

  • 下载开发者工具

1). 注册小程序

注册地址:https://mp.weixin.qq.com/wxopen/waregister?action=step1

2). 完善小程序信息

登录小程序后台:https://mp.weixin.qq.com/

两种登录方式选其一即可

完善小程序信息、小程序类目

3). 下载开发者工具

资料中已提供,无需下载,熟悉下载步骤即可。

下载地址: https://developers.weixin.qq.com/miniprogram/dev/devtools/stable.html

建议从地址下载,使用资源中的安装包会有bug

关键设置

设置不校验合法域名

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

微信登录

代码导入

        在资料day06中解压后的文件夹

AppID:使用自己的AppID

后端服务选择不使用云服务

修改配置

        因为小程序要请求后端服务,需要修改为自己后端服务的ip地址和端口号(默认不需要修改)

微信登录流程

   微信登录:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html

步骤分析:

  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. 开发者服务端,身份校验通过后,继续相关的业务逻辑处理,最终返回业务数据。


需求分析和设计

产品原型

业务规则:

  • 基于微信登录实现小程序的登录功能

  • 如果是新用户需要自动完成注册

接口设计

        通过微信登录的流程,如果要完成微信登录的话,最终就要获得微信用户的openid。在小程序端获取授权码后,向后端服务发送请求,并携带授权码,这样后端服务在收到授权码后,就可以去请求微信接口服务。最终,后端向小程序返回openid和token等数据。

说明:请求路径/user/user/login,第一个user代表用户端,第二个user代表用户模块。


代码开发

配置微信登录所需配置项:

application-dev.yml
sky:
  wechat:
    appid: wxffb3637a228223b8
    secret: 84311df9199ecacdf4f12d27b6b9522d


application.yml
sky:
  wechat:
    appid: ${sky.wechat.appid}
    secret: ${sky.wechat.secret}

配置为微信用户生成jwt令牌时使用的配置项:

application.yml
sky:
  jwt:
    user-secret-key: itheima
    user-ttl: 7200000
    user-token-name: authentication

//Controller

@RestController
@RequestMapping("/user/user")
@Api(tags = "C端用户相关接口")
@Slf4j
public class UserController {

    @Autowired
    private UserService userService;
    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 微信登录
     * @param userLoginDTO
     * @return
     */
    @PostMapping("/login")
    @ApiOperation("微信登录")
    public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){
        log.info("微信用户登录:{}",userLoginDTO.getCode());

        //微信登录
        User user = userService.wxLogin(userLoginDTO);//后绪步骤实现

        //为微信用户生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        //其中,JwtClaimsConstant.USER_ID常量已定义。
        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
public interface UserService {

    /**
     * 微信登录
     * @param userLoginDTO
     * @return
     */
    User wxLogin(UserLoginDTO userLoginDTO);
}

//ServiceImpl
@Service
@Slf4j
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;

    /**    ServiceImpl
     * 微信登录
     * @param userLoginDTO
     * @return
     */
    public 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;
    }

    /**    ServiceImpl抽取方法
     * 调用微信接口服务,获取微信用户的openid
     * @param code
     * @return
     */
    private String getOpenid(String code){
        //调用微信接口服务,获得当前微信用户的openid
        //构建请求参数
        Map<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);
        //解析返回json数据
        JSONObject jsonObject = JSON.parseObject(json);
        //获取微信用户openid
        String openid = jsonObject.getString("openid");
        return openid;
    }
}

//mapper
@Mapper
public interface UserMapper {

    /**
     * 根据openid查询用户
     * @param openid
     * @return
     */
    @Select("select * from user where openid = #{openid}")
    User getByOpenid(String openid);

    /**
     * 插入数据
     * @param user
     */
    void insert(User user);
}
<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校验

/**
 * 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;
        }
    }
}

在WebMvcConfiguration配置类中注册拦截器:

    @Autowired
    private JwtTokenUserInterceptor jwtTokenUserInterceptor;

    /**
     * 注册自定义拦截器
     * @param registry
     */
    protected void addInterceptors(InterceptorRegistry registry) {
        log.info("开始注册自定义拦截器...");
        //.........

        registry.addInterceptor(jwtTokenUserInterceptor)
                .addPathPatterns("/user/**")
                .excludePathPatterns("/user/user/login")
                .excludePathPatterns("/user/shop/status");
    }

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐