[JWeb系列4] SpringBoot注册登录小测试

2018-01-05  本文已影响2343人  做梦枯岛醒

SpringBoot+MyBatis+Thymeleaf注册登录

好吧我承认,我用Php Web的时候也是做的注册登录,现在写javaweb还是注册登录,如果是仅仅做一个简单的注册登录不考虑安全性等问题,那其实拿来做练手是很不错的。

下面要完成的是这几项。

1.准备

用JavaWeb来写的话比Php肯定是复杂的多,特别是对于我第一次接触JavaWeb的小白,所以我们这篇文章不得不长篇大论的写,如果你喜欢研究的话,不如耐心的看下来,如果您是大神的话,还请您能够指出其中的错误。

准备就是我们打算接着使用先前建立好的工程,我们新建几个控制器来做注册登录。

那么在写代码之前我们要学会一些基本的知识。

2.View

MVC想必大家应该很熟悉,特别是我们用的SpringBoot框架,更应该知道它是基于MVC架构的。
上面我们已经写过C,这次我们来研究一下View。

先贴一段Controller的代码

package com.example.demo.Web;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
@RestController
@RequestMapping("/My")
public class MyController {
    @RequestMapping(value = "/index")
    public ModelAndView hello(Model model) {
        model.addAttribute("name", "苏凌");
        return new ModelAndView("hello");
    }
    @RequestMapping("/")
    public String normal()
    {
        return "hello";
    }
}

这里就是写好了两个接口,访问http://localhost:8080/My/的时候是打印hello,访问http://localhost:8080/My/index时候截图如下

运行

那么这个界面从哪来的?

你可能已经知道了

new ModelAndView("hello")

这行代码应该就是打印的内容吧。
对,没错,通过ModelAndView方法,我们可以找到hello代表的网页,然后把网页里面的信息展示出来,同时我们可以通过model传递数据,那么我们此时就做到了模型视图分离。

那么我们用到的就是模板技术(Jsp开发应该不属于模板系列)。

Tip: 目前Spring官方已经不推荐使用JSP来开发WEB了,而是推荐使用如下几种模板引擎来开发:

Thymeleaf

那么这篇文章里,我们使用官方推荐模板,关于这款模板的好处大家可以自行搜索,在这里不占用篇幅。

第一步:下载

通过修改pom.xml添加Thymeleaf的依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
Note:注意加在<dependencies></dependencies>标签里

下载需要挺长时间,如果等不及或者多次不成功可以试试修改mvn源。
我们下载完最好检验一下有没有成功(我踩了一天的坑,就是因为没有下载成功,pom写的没毛病,就是lib下载不完整……坑的一批),打开mvn project,看看有没有Thymeleaf。


显示

有的话继续,没有的话慢慢下载……暂时没啥好方法。

第二步,小小配置

在application.properties中添加一行,小小的配置关闭缓存。

#关闭缓存
spring.thymeleaf.cache=false
第三步,编写模板文件
Note:文件需要放在src/main/resouces/templates/目录下,文件后缀.html

上面的例子中我写的模板文件是hello.html

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>hello</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<!--${name}可以打印一个叫name的变量-->
<p th:text="'你好!' + ${name} + '!'" ></p>
</body>
</html>

模板语法不在这里多介绍,暂时先Copy代码,我们这篇文章理解一下流程。

第四步,编写Controller

那么Controller就是我们上面贴的那个,我们只看所需要的接口

package com.example.demo.Web;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
@RestController
@RequestMapping("/My")
public class MyController {
    @RequestMapping(value = "/index")
    public ModelAndView hello(Model model) {
        model.addAttribute("name", "苏凌");
        return new ModelAndView("hello");
    }
}

我们使用model传入了name变量的值,然后用ModelAndView显示了页面。

这里还有一种操作,就是下面这种直接返回html名字的写法,但是要求必须使用@Controller注解。


image.png

Thymeleaf先说到这里,在后面我们可能会尝试一下Jsp原始写法。

3.get/post

1.准备

那么研究Web,必须先来研究get/post请求。
【文章参考:http://blog.csdn.net/u010399316/article/details/52913299

我们最常用的两种方式。

比如说我设计这样一个链接。
http://localhost:8080/register?name=surine&pwd=123 通过get请求,传递用户名和密码来完成注册,
然后设计这样一个链接。
http://localhost:8080/login 通过post请求,传递用户名和密码来完成登录。

2.Get

在SpringBoot中,我们的Get这样实现,通过method来设置方法

/**
     * 通过get请求去注册
     * @param name
     * @param pwd
     * @return 状态信息
     */
    //method :设置一下get或者post
    //@RequestParam的注解已经很清楚了,接受名字为name的参数,required 设置是否必须。
    @RequestMapping(value = "/register", method = RequestMethod.GET)
    public String register(@RequestParam(value = "name", required = true) String name,
                             @RequestParam(value = "pwd", required = true) String pwd) {
        //register_check是自定义的一个注册方法我们可以在里面写注册操作,返回状态码
        return register_check(name, pwd);
    }
//先写一个简单的register_check方法
private String register_check(String name, String pwd) {
        if(name.equals("surine")&&pwd.equals("123456")){
            return "注册成功";
        }else{
            return "注册失败";
        }
    }

我们浏览器测试一下
首先我们不给参数看啥情况,下面报错很明显,缺少参数,这是因为我们设置了必须要参数的原因


image.png

账号密码错误


image.png

账号密码正确


image.png

细心的小朋友站出来:哪家的注册还判断你的账号密码……
我:测试嘛!这么认真干嘛!

3.Post

我们先写一个Post的Controller,额,竟然跟Get一样,除了改了下method

/**
     * 通过get请求去登陆
     *
     * @param name
     * @param pwd
     * @return
     */
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public String login(@RequestParam(value = "name", required = true) String name,
                             @RequestParam(value = "pwd", required = true) String pwd) {
        return login_check(name, pwd);
    }

    private String login_check(String name, String pwd) {
        if(name.equals("surine")&&pwd.equals("123456")){
            return "登录成功";
        }else{
            return "登录失败";
        }
    }

那么我们跑起来用Postman来测试一下。


image.png

输入地址,表单,最后得到结果。
看样子没有什么异常,暂时先掌握这些,为我们后面的开发做铺垫。

其余的方法感兴趣可以自行研究。

4.注册登录分析

Note:注意,我们在下面写的代码可能与上面有所不同,具体为什么这么写我会在下面做出解释,但是上面的练习也应该跑一遍,参考:https://www.jianshu.com/p/212f2682c10b
这里面提到的一些生词,在一会都会详细解释,现在就记住我们要写的项目有这么些东西就行了。

1.原型分析

预期要做成下面这个样子,


首页.png

打开首页有登录注册两个链接,点击登录链接,跳转到登录,登录成功失败会有相应提示,同理注册也是。


注册.png
密码错误.png
2.流程分析

最终的文件结构如下图所示。


image.png

这里我们有5个重要部分
Controller上面我们讲了,作为接受页面数据的工具,Dao是操作数据库的工具,Domin是放的实体类,Service主要是操作数据检查合法性,通过Service来发出指令,templates也就是我们的页面模板了。

注册(登录与其相同):

3.设计原则

可能你会觉得我一个模板,一个Controller,然后用Mapper(上篇文章讲过)就可以做一个注册登录,搞得这么复杂干嘛……
这样写体现了MVC模型的特点,通过对业务的一些分层,减少层次之间耦合,使每一个类的功能单一化,方便以后维护,但对于新手来说,一下子接受不了,

4.数据库设计

数据库不多说了,自己建立就行了,不会搞数据库的童鞋,还是先去搞搞数据库吧。


数据表

5.注册登录实现

下面我们开始Coding,在这期间遇到了不少问题,但是又没做什么解决自己就好了……真的是要人命,然后我会在遇到问题或者容易出错的地方打Tips,童靴们自己注意下。

1.配置项

首先我们做一下配置,把这之前的一起说了。
Thymeleaf模板:

#修改pom.xml文件
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
#修改application.properties文件
#为了保证项目的运行,增加这些
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.servlet.content-type=text/html

#关闭缓存
spring.thymeleaf.cache=false

Application:作为SpringBoot的入口文件也有需要配置的地方
添加mapper扫描的包路径,为数据库连接做准备

@SpringBootApplication
//Mapper扫描(这里的值就是dao目录的值,按照我刚贴的目录结构来做就行)
@MapperScan("com.example.demo.dao")
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
2.实体类

刚才说过,domin目录下面放实体类,我们第一步先把实体类做出来,为整体的流程做协调,根据我们的数据库设计字段,来设计实体类。

Tip: 没有什么特殊需求,一定要保持数据库字段跟domin实体类字段一致,甚至是数据类型,省的出现各种各样的问题。
我的User.class

package com.example.demo.domin;
public class User {
    private int id;
    private String username;
    private String password;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}
3.数据库操作

前面也说过,Dao目录下面放的各种dao文件是我们的数据库操作接口(抽象数据类),例如我们的UserDao内容如下,两个操作,增加用户,查询用户。
所以在以后的项目中,如果需求大的话,我们可以编写更多功能。

package com.example.demo.dao;

//这个注解代表这是一个mybatis的操作数据库的类
@Repository

public interface UserDao {
    // 根据username获得一个User类
    @Select("select * from user where username=#{name}")
    User getOneUser(String name);


    //插入一个User
    @Insert("insert into user (username,password) values(#{username},#{password})")
    boolean setOneUser(User user);

}

Tip:注意字段对应 比如#{name}最后会被String name 的name所替换

4.API接口(Controller)

这里是重点,前端后端从这里划分(不知道这个说法正不正确,个人见解),我们根据需求分析,发现需要实现的功能主要有三个,注册登录注销,那么我们的Controller如下。(UserService 先让他标红,我们将在下面建立)

package com.example.demo.controller;
@Controller
@EnableAutoConfiguration
public class IndexController {
    //自动注入userService,用来处理业务
    @Autowired
    private UserService userService;
     /**
      * 域名访问重定向
      * 作用:输入域名后重定向到index(首页)
      * */
    @RequestMapping("")
    public String index(HttpServletResponse response) {
        //重定向到 /index
        return response.encodeRedirectURL("/index");
    }
    /**
     * 首页API
     * 作用:显示首页
     * */
    @RequestMapping("/index")
    public String home(Model model) {
        //对应到templates文件夹下面的index
        return "index";
    }
    /**
     * 注册API
     * @method:post
     * @param user(从View层传回来的user对象)
     * @return 重定向
     * */
    @RequestMapping(value = "/register", method = RequestMethod.POST)
    public String registerPost(Model model,
                               //这里和模板中的th:object="${user}"对应起来
                               @ModelAttribute(value = "user") User user,
                               HttpServletResponse response) {
        //我们可以用Sout这种最原始打印方式来检查数据的传输
        System.out.println("Controller信息:"+user.getUsername());
        System.out.println("Controller密码:"+user.getPassword());
        //使用userService处理业务
        String result = userService.register(user);
        //将结果放入model中,在模板中可以取到model中的值
        //这里就是交互的一个重要地方,我们可以在模板中通过这些属性值访问到数据
        model.addAttribute("result", result);
        //开始重定向,携带数据过去了。
        return response.encodeRedirectURL("/index");
    }
    /**
     * 登录API
     * @method:post
     * @param user(从View层传回来的user对象)
     * @return 重定向
     * */
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public String loginPost(Model model,
                            @ModelAttribute(value = "user") User user,
                            HttpServletResponse response,
                            HttpSession session) {
        String result = userService.login(user);
        if (result.equals("登陆成功")) {
           //session是作为用户登录信息保存的存在
            session.setAttribute("user",user);
        }
        model.addAttribute("result", result);
        return response.encodeRedirectURL("/index");
    }
    /**
     * 注销API
     * @method:get
     * @return 首页
     * */
    @RequestMapping(value = "/loginOut", method = RequestMethod.GET)
    public String loginOut(HttpSession session) {
        //从session中删除user属性,用户退出登录
        session.removeAttribute("user");
        return "index";
    }
}

相信就算是小白,我们在前面的学习中也接触到接口的写法了,这里仅仅增加了几个重定向的方法,还有post的写法有一点点不太一样(运用了模板之后)

Tip:不会使用单元测试或调试的话可以用System.out.print这种最原始的方法来检查数据哦

5.页面模板

通过需求分析我们一共要设计三个页面,源码分别如下。

<!--index.html-->
<!--模板源码应该很好理解,不理解的话去看看教程就OK-->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<!--这里result会报错,不要担心这是idea的bug,不影响你的项目->
<h1> <span th:text="${result}"></span></h1>
<h1>欢迎 : <span  th:text="${session.user}?${session.user.getUsername()}:'(您未登录)'" ></span> </h1>
<a th:href="@{/register}">点击注册</a>
<a th:href="@{/login}" th:if="${session.user==null}">点击登录</a>
<a th:href="@{/loginOut}" th:unless="${session.user==null}">退出登陆</a>
</body>
</html>
<!--login.html-->

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登陆</title>
</head>
<body>
<h1>欢迎登陆</h1>
 <!--这个地方user也会报错,不用担心-->
<!--注意下面name,和id都写上就OK-->
<form th:action="@{/login}" th:object="${user}" method="post">
    <label for="username">username:</label>
    <input type="text" name="username"  id="username" />
    <label for="password">password:</label>
    <input type="text" name="password" id="password" />
    <input type="submit" value="submit">
</form>
</body>
</html>
<!--register.html-->

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>欢迎注册</title>
</head>
<body>
<h1>欢迎注册</h1>
<form th:action="@{/register}" th:object="${user}" method="post">
    <label for="username">username:</label>
    <input type="text" id="username" name="username"/>
    <label for="password">password:</label>
    <input type="text" id="password" name="password"/>
    <input type="submit" value="submit">
</form>
</body>
</html>

TIp:页面的重点放在input的一些属性上,我们输入的值最终会被映射到user(domin)里面

6.Service

写了好长时间,算是把页面,数据库,接口都写了,那么剩下最后一个纽带就是service了。

package com.example.demo.service;

import com.example.demo.dao.UserDao;
import com.example.demo.domin.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    //自动注入一个userDao
    @Autowired
    private UserDao userDao;

    //用户注册逻辑
    public String  register(User user) {
        System.out.println(user.getUsername());
        User x = userDao.getOneUser(user.getUsername());
        //判断用户是否存在
        if (x == null) {
            userDao.setOneUser(user);
            return "注册成功";
        }
        else {
            return x.getUsername()+"已被使用";
        }
    }
    //用户登陆逻辑
    public String login(User user) {
        //通过用户名获取用户
        User dbUser = userDao.getOneUser(user.getUsername());

        //若获取失败
        if (dbUser == null) {
            return "该用户不存在";
        }
        //获取成功后,将获取用户的密码和传入密码对比
        else if (!dbUser.getPassword().equals(user.getPassword())){
            return "密码错误";
        }
        else {
            //若密码也相同则登陆成功
            //让传入用户的属性和数据库保持一致
            user.setId(dbUser.getId());
            return "登陆成功";
        }
    }
}

仔细阅读service的源码,你就会发现,它其实就是与dao联系起来做数据合法性检验的。

Tip:这里也是可以使用sout来测试数据

7.运行
到这里我们的所有代码都已经写完了,如果你的工程还缺了什么跑不起来,可以在下面评论哦。
运行的时候浏览器输入localhost:8080就可以到首页,经我这边测试注册登录注销都没问题,而且登录了之后下次再访问就直接是登录状态了(session没有设置过期时间,所以直到下一个用户登录一直有效)


image.png

Tip:善于观察浏览器报错信息或者Idea里也有,通过检查报错来解决问题,不要遇到问题就去对照别人的源码看哪里不一样,同样的代码可能不适合你。

6.总结

感觉还是Php写起来简单一些,毕竟脚本语言本身就是轻巧。
那么JavaWeb用了SpringBoot框架后,也从一定程度上减少了代码的复杂度,逻辑还算清楚。
唯一不爽的是数据库竟然要自己写sql语句……
不过自由定制毕竟是可以满足大量需求的,那么如果你真的不喜欢sql语句,可以试试用 Hibernate来替换MyBatis

我们目前实现的是一个注册登录功能,那么其实你再做别的接口也是一样的,只是在它的业务上有一些不同,那么希望这篇文章能对你有所帮助。

个人刚接触JavaWeb,有好多东西也是现查现学,因为为了实习才准备的JavaWeb,所以现在连本书都没有,好尴尬。

最后,代码虐我千百遍,我对代码如初恋。

这里特意加出一个部分,我们来讨论一下Jsp的写法和Css/js静态页面(仅讨论JSP与Css/js,不讨论thymeleaf)的解决方案。

SpringBoot与Jsp

前面我们说到Spring官方非常推荐开发者使用ThymeLeaf来开发,而不推荐使用Jsp。那我们还是要了解一下Jsp是怎么写的。

1.导入依赖

再pom.xml里面导入依赖

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
</dependency>

记得导完看看有没有哦,这里我先前一直不知道要导入这个,说实话我真的没找到一个像样的文章,也不知道是我理解能力太差了还是大家都写得很烂,自我感觉良好…… 我不知道我写的是不是很烂,但是我把我做的每一步都说出来了。如果还有问题你可以私信或留言。
参考:https://stackoverflow.com/questions/29782915/spring-boot-jsp-404
如果不导入就会出现这位小哥遇到的错误,也就是我一直遇到的错误

image.png
我是参考第一个红框和第三个红框来解决的,也就是上面的代码。

Tip:遇到问题不要百度,要么google要么直接StackOverFlow,百度出来的?要是中国程序员都这么糟糕的话……简直不敢想象。

2.配置application.properties

文件中添加这两行

# 页面默认前缀目录
spring.mvc.view.prefix=/WEB-INF/jsp/
# 响应页面默认后缀
spring.mvc.view.suffix=.jsp
3.配置Application

主要增加一个继承


增加
4.新建文件夹,创建jsp

webapp/WEB-INF/jsp/文件


webapp

举个例子,indexjsp.jsp内容

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Spring Boot Sample</title>
</head>
<body>
Time: ${time}
<br>
Message: ${message}
</body>
</html>
5.Controller
 @RequestMapping(value = "/jsp/index",method = RequestMethod.GET)
    public String jsp_index(Map<String, Object> model){
        model.put("time", new Date());
        model.put("message", "你好");
        return "indexjsp";
    }

6.访问

访问之前有个小Tip

Tip:注意删掉Thymeleaf在工程中的依赖和配置,这两样就行。

要注意Thymeleaf和jsp是两种并列的技术,为了解决同一个问题,不要试图去融合使用他们,那样只会让你的项目更乱,所以我们使用的时候按照需求选一个就行,我不确定两个共存会不会出现致命错误,但是为了增加系统的稳定性,尽量不要两个一起使用。
地址:localhost:8080/jsp/index


image.png

Jsp与CSS

在Jsp中使用静态资源也没什么难处。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Spring Boot Sample</title>
    <!--Import Google Icon Font-->
    <link href="http://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <!--Import materialize.css-->
    <link type="text/css" rel="stylesheet" href="../../static/css/materialize.css"  media="screen,projection"/>
</head>
<body>
<script type="text/javascript" src="../../static/js/jquery-3.2.1.js"></script>
<script type="text/javascript" src="../../static/js/materialize.js"></script>
Time: ${time}
<br>
Message: ${message}
<button class="btn waves-effect waves-light" type="submit" name="action">提交
    <i class="material-icons right">send</i>
</button>
</body>
</html>

上面这个代码还是indexjsp.jsp,我们在其中引入了materialize和jquery(materialize需要),然后使用了materialize中的一个组件。
重点在我们建立的文件夹,把materialize放在static下面



效果:


image.png

不会用materialize的童鞋参考中文网http://www.materializecss.cn/buttons.html。哈哈不罗嗦了,好长一篇文章,最后给一个jsp的注册登录版本源码。

https://github.com/Surine/demo

上一篇下一篇

猜你喜欢

热点阅读