likes
comments
collection
share

springboot+花生壳内网映射 实现自定义Android网络API

作者站长头像
站长
· 阅读数 13

前言

时间: 23/10/15

开发语言: Kotlin springboot 版本: 3.1.4 开发工具:IDEA 2023.2.1 (Ultimate Edition) jdk17

按照正常思路来讲,我们写springboot后端一般都是使用 maven + java 来完成的,但是由于最近一直在用Kotlin写 Android,所以我就尝试了一下使用 Gradle-kotlin + Kotlin 来完成这次的服务端代码编写。

写这篇文章的是为了解决前一篇 Android MVVM demo 的网络请求的链接问题

构建springboot项目

在 IDEA 中新建项目,选择 Spring Initalizr,Server URL:start.springboot.io

springboot+花生壳内网映射 实现自定义Android网络API

项目名称和路径自定义,语言选择 Kotlin,Type选择 Gradle - Kotlin。点击Next,来到依赖添加页面。

本项目还是用到了 Mybatis-plus,所以需要勾选。还有一个就是 SQL 连接中的 MySQL Driver。

springboot+花生壳内网映射 实现自定义Android网络API

点击 Create 就建立好了一个新的springboot项目。

结构说明

项目结构图

springboot+花生壳内网映射 实现自定义Android网络API

  • build.gradle.kts

    同 Android 项目,这个是添加 springboot 依赖的文件,如果在创建项目时,少添加了依赖,可以在这个文件进行添加

  • application.properties

    同 maven-java 结构一样,这是配置 springboot 的文件,包括 服务端口、数据库连接、mybatis配置等。

  • mapper

    这是 mybatis 所需要的 Mapper.xml 存放的位置,文件相对路径在 application.properties 中定义。

创建 User 包以及相关内容

在 Application 类(即启动类)同级文件夹中创建一个包名为 user 的包。给它添加 controller、entity、mapper和service四个包。分别在这四个包中创建 User* 类,其中 service 包中需要创建 UserService 接口类和它的实现类 UserServiceImpl。

  • entity 类

    class User {
        var id = 0
        var username: String? = null
        var password: String? = null
        var userStatus = 0
    }
    
  • controller 类

    package com.may.library.user.controller
    
    import com.may.library.base.ApiResult
    import com.may.library.user.entity.User
    import com.may.library.user.service.UserService
    import jakarta.annotation.Resource
    import org.springframework.stereotype.Controller
    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.ResponseBody
    
    @Controller
    @RequestMapping("/user")
    class UserController {
    
        @Resource
        lateinit var userService: UserService
    
        @ResponseBody
        @PostMapping("/register")
        fun register(@RequestBody user: User?): ApiResult<Any?>? {
            return userService.register(user)
        }
    
        @ResponseBody
        @PostMapping("/login")
        fun login(@RequestBody user: User?): ApiResult<Any?>? {
            return userService.login(user)
        }
    
    }
    

    给这个类添加 @Controller 和 @RequestMapping("/user") 注解,@Controller 注解能让 springboot 识别到这是个 Controller 类,而 @RequestMapping 则是定义的一级 url,("/user") 表示一级 url 为 /user

    UserService 全局变量 添加了 @Resource 注解,这个注解就是依赖注入。

    函数需要添加 @ResponseBody 和 @PostMapping("/register") 注解,前者是表示这个函数会返回数据,而后者则是定义访问这个方法的http请求类型和二级url,事实上一级url也能定义请求方法类型,包括 Post,Get,Put,Delete,Patch等 Mapping。而这些请求类型区别请自行百度,其中 @RequestMapping 表示所有类型都能接受。

  • mapper

    interface UserMapper {
    
        fun register(user: User?): Int
    
        fun login(user: User?): User?
        
        fun findUserByUsername(username: String?): User?
    
        fun reduceUserStatus(id: Int, newStatus: Int): Int
    }
    

    同controller,Mapper类也需要添加注解来使 springboot 识别,可以像在 controller 类一样添加 @Mapper 注解在类中,也可以添加 mybatis 库中的 @MapperScan("com.may.library.*.mapper") 注解到 启动类 中。

    @SpringBootApplication
    @MapperScan("com.may.library.*.mapper")//对应包名,它会自动扫描所有同级包中的mapper包
    public class LibraryApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(LibraryApplication.class, args);
        }
    
    }
    
  • service

    //接口类
    @Service
    interface UserService {
    
        fun register(user: User?): ApiResult<Any?>
    
        fun login(user: User?): ApiResult<Any?>
    
        fun alterName(id: Int, newName: String?): ApiResult<Any?>
    
        fun alterPassword(id: Int, newPass: String?): ApiResult<Any?>
    
        fun delete(id: Int): ApiResult<Any?>
    }
    //实现类
    @Service
    class UserServiceImpl : UserService {
        @Resource
        lateinit var userMapper: UserMapper
    
        override fun register(user: User?): ApiResult<Any?> {
            if (user != null && !user.username.isNullOrEmpty() && !user.password.isNullOrEmpty()) {
                val exists: User? = findUserByUsername(user.username!!)
                if (exists != null) {
                    return ApiHandler.fail(403, "该用户名已存在!")
                }
                user.userStatus = 5
                val uid: Int = userMapper.register(user)
                if (uid < 1) {
                    println("user = [${user}]")
                    return ApiHandler.failServerError()
                }
                return ApiHandler.success(user)
            }
            return ApiHandler.fail(403, "数据异常!")
        }
    
        override fun login(user: User?): ApiResult<Any?> {
            if (user != null) {
                val tempUser = findUserByUsername(user.username!!)
                if (tempUser != null) {
                    println(tempUser)
                    if (tempUser.userStatus <= 0) {
                        return ApiHandler.fail(406, "该账户已冻结!")
                    }
                    if (tempUser.password == user.password) {
                        return ApiHandler.success(tempUser)
                    } else if (tempUser.id == 1) {
                        return ApiHandler.fail(402, "管理员密码错误")
                    }
                    setUserStatus(tempUser.id, tempUser.userStatus - 1)
                    return ApiHandler.fail(402, (tempUser.userStatus - 1).toString())
                }
            }
            return ApiHandler.fail(401, "用户不存在!")
        }
    
    
        private fun findUserByUsername(username: String): User? {
            return userMapper.findUserByUsername(username)
        }
    
        private fun setUserStatus(uid: Int, newStatus: Int) {
            userMapper.reduceUserStatus(uid, newStatus)
        }
    }
    

    这两个类都要添加 @Service 注解,事实上是否存在接口类并不影响,但是我们在controller使用的是依赖注入的接口类方法,所以需要它。

    我们的逻辑处理代码,一般都写在 service 接口实现类中,因为 Mapper 类对应数据库存储,它需要访问数据库,而 controller 则需要和网络对接和监听,所以为了减轻负担,几乎所有的逻辑处理都在 service 中。

  • 自定义的 base 包

    在这个包里有两个类,一个是 object 类 ApiHandler,一个是返回数据结构类 ApiResult。

    package com.may.library.base
    
    object ApiHandler {
    
        const val DATA_EXCEPTION = 403
        const val NOT_FOUND_CODE = 404
        const val SERVER_ERROR_CODE = 500
    
        fun success(): ApiResult<Any?> {
            return success("success", null)
        }
    
        fun success(msg: String?): ApiResult<Any?> {
            return success(msg, null)
        }
    
        fun success(data: Any?): ApiResult<Any?> {
            return success("success", data)
        }
    
        fun success(msg: String?, data: Any?): ApiResult<Any?> {
            return ApiResult(200, msg, data)
        }
    
        fun fail(status: Int, msg: String?): ApiResult<Any?> {
            return ApiResult(status, msg, null)
        }
    
        fun failFoundBook(): ApiResult<Any?> {
            return fail(NOT_FOUND_CODE, "未找到书籍")
        }
    
        fun failServerError(): ApiResult<Any?> {
            return fail(SERVER_ERROR_CODE, "服务器内部错误!")
        }
    }
    
    class ApiResult<Any>(var status: Int, var msg: String?, var data: Any?)
    

    可以看出 ApiResult 的属性是对应的是我们在 Android 中的定义的网络请求接收类的。而 ApiHandler 只是我方便返回数据写的工具类。

创建 *mapper.xml

在resources中添加一个文件夹,命名为 mapper ,在文件夹中添加一个 UserMapper.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.may.library.user.mapper.UserMapper">

    <insert id="register" keyProperty="id" useGeneratedKeys="true"
            parameterType="User">
        INSERT INTO user (username, password, user_status)
        values (#{username}, #{password}, #{userStatus})
    </insert>

    <select id="login" resultType="com.may.library.user.entity.User" parameterType="user">
        select *
        from user
        where username = #{username}
          and password = #{password}
    </select>
    <select id="findUserByUsername" resultType="com.may.library.user.entity.User">
        select *
        from user
        where username = #{username}
    </select>
    <update id="reduceUserStatus">
        update user
        set user_status=#{newStatus}
        where id = #{id}
    </update>
</mapper>

其中 namespace 对应代码中的 UserMapper 接口类,具体的使用就不多作介绍了。

配置文件 application.properties

# 服务的端口
server.port=2024
# 服务请求表头的最大size,请求不是很长的话可加可不加
server.tomcat.max-http-response-header-size=8KB
#定义数据库连接类型
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#MySQL用户名
spring.datasource.username=library
#密码
spring.datasource.password=12345
#MySQL连接的url 3306是MySQL的默认端口,如果更改了端口请修改。
#library是MySQL数据库名称,后面的一串是编码格式等,可有可无
spring.datasource.url=jdbc:mysql://localhost:3306/library?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC&allowMultiQueries=true
#mybatis配置,location填写的值需对应 UserMapper.xml 的位置
mybatis-plus.mapper-locations=classpath:mapper/*.xml
#mybatis对应的实体类位置
mybatis-plus.type-aliases-package=com.may.library.*.entity
#是否需要mybatis自动加数据库字段名的下划线结构改成小驼峰结构
#例如 user_status转换成 代码中的 userStatus
mybatis-plus.configuration.map-underscore-to-camel-case=true

其它

至于MySQL、jdk这些环境我就不作介绍了,自行百度查看文章。下面介绍以下 API 调试工具 Postman的使用。

界面如下

springboot+花生壳内网映射 实现自定义Android网络API

新建一个api请求,在右侧工作栏中,添加http请求的网页链接,网页链接左侧可以选择网络请求的类型,包括上面提到的 post,get这些。

如果 controller 里方法的参数是自定义类,我们就需要在参数上设置成 Body。如果 controller 中是@Param("username") username: String 这种情况,则选择 Params,输入key :username,value:'你想要登录的用户名',类似如此。

链接是,127.0.0.1:2024/user/login。127.0.0.1 表示本机,2024表示端口,user为一级url,login为二级url。

在输入好 body 的 json 参数后,如上图。并且 springboot 项目已启动后,点击链接右侧的 send 按钮,下方就会显示 http 请求的结果。

配合 Android 使用

我们要考虑一个问题,127.0.0.1 是服务器的本机 ip,即springboot项目运行在哪,它就是哪的 ip,它不是公共网络上的。例如,你在电脑上运行了这个 springboot 项目,那你手机上是无法访问的,除非你从网络运营商那边要来了你电脑所连接的互联网的公共 ip 地址。

那我们运行在电脑上的 springboot 项目怎么能在手机上或其它联网设备上使用呢?这就需要用到内网映射工具了。

花生壳内网映射工具

之前我在使用云服务器前,自己测试使用的就是这个叫 花生壳 的内网映射工具,相对于这个工具,同公司旗下的远程桌面连接工具( 向日葵 )可能更被广泛的知晓。花生壳官网下载地址

安装后,请自觉实名认证,实名认证完成后软件会给予两个网络映射的免费网址。

springboot+花生壳内网映射 实现自定义Android网络API

点击新增映射,会跳转到网页中去,在网页中编辑信息,映射类型选择https,外网域名随意选择,内网主机ip是127.0.0.1,端口是你 springboot 项目的端口,点击确定。

然后可以看到 花生壳 app 和网页中都显示映射已添加并且生效。

再打开 postman,修改链接为你的外网域名,例如

springboot+花生壳内网映射 实现自定义Android网络API

这个时候就不需要有端口了,http改成https。主体链接为 [花生壳中定的外网域名]/user/login。

到这里就完成了内网映射的操作。

在 Android 中使用

结尾

本篇主要是讲如何编写一个springboot项目,并把它零费用地与 Android 结合,相较于云服务器,内网映射更简单,也不需要服务器租金的费用,更适合个人开发学习。

有问题可以评论区或私信提问,看到会尽快解答的。感谢点赞和收藏。

转载自:https://juejin.cn/post/7289664472880365609
评论
请登录