day32(通过V14和V15两个版本完成用户注册业务:V14(form表单,进一步解析uri)、V15(UserController:reg()方法,uri换成requestURI))
day32(通过V14和V15两个版本完成用户注册业务)
1.V14(form表单,进一步解析uri)
1.主要内容
将注册页面中表单提交的数据解析完毕并存入到HttpServletRequest对应属性
1.用户注册业务的大致流程:
1:用户访问注册页面,并在页面上输入注册信息后点击注册按钮
2:数据提交发到服务端,服务端解析页面提交上来的数据
3:根据解析出来的数据进行响应的注册处理
4:给用户回复一个注册处理结果的页面(注册成功或失败)
2.这里涉及的知识点:
1:页面如何将用户输入的信息提交给服务端
表单form的使用
2:服务端如何通过解析请求得到表单数据
3:DispatcherServlet如何区分请求是处理注册还是请求一个静态资源(页面,图片等)
本版本完成表单的提交以及请求的解析工作。这个工作是通用操作,无论将来处理何种业务,解析表单
数据的方式都是相同的。
3.实现:
1:在static/myweb下新建用户注册页面reg.html
在这个页面上我们学习form表单的使用
2:重构HttpServletRequest的解析工作,添加对表单数据的解析。
2.reg.html(form表单)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body >
<center>
<h1>用户注册</h1>
<form action="/myweb/reg" method="get">
<table border="1">
<tr>
<td>用户名</td>
<td><input name="username" type="text"></td>
</tr>
<tr>
<td>密码</td>
<td><input name="password" type="password"></td>
</tr>
<tr>
<td>昵称</td>
<td><input name="nickname" type="text"></td>
</tr>
<tr>
<td>年龄</td>
<td><input name="age" type="number"></td>
</tr>
<tr>
<td align="center" colspan="2"><input type="submit" value="注册" ></td>
</tr>
</table>
</form>
</center>
<style>
</style>
</body>
</html>
3.HttpServletRequest(进一步解析uri)
package com.webserver.http;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
/**
* HTTP请求对象
* 该类的每一个实例用于表示一个HTTP请求内容
* 每个请求由三部分构成:
* 请求行,消息头,消息正文
*/
public class HttpServletRequest {
private Socket socket;
//请求行相关信息
private String method;//请求方式
private String uri;//抽象路径
private String protocol;//协议版本
//保存uri中"?"左侧的内容。如果uri中没有"?"则内容与uri一致
private String requestURI;
//保存uri中"?"右侧内容,即:参数部分
private String queryString;
//用来保存客户端传递过来的每一组参数
private Map<String,String> parameters = new HashMap<>();
//消息头相关信息
private Map<String,String> headers = new HashMap<>();
public HttpServletRequest(Socket socket) throws IOException, EmptyRequestException {
this.socket = socket;
//1.1解析请求行
parseRequestLine();
//1.2解析消息头
parseHeaders();
//1.3解析消息正文
parseContent();
}
/**
* 解析请求行
* parse:解析
*/
private void parseRequestLine() throws IOException, EmptyRequestException {
String line = readLine();
if(line.isEmpty()){//如果请求行为空字符串,则说明为空请求
throw new EmptyRequestException();
}
System.out.println(line);
String[] array = line.split("\\s");
method = array[0];
uri = array[1];
protocol = array[2];
//进一步解析uri
parseUri();
System.out.println("method:"+method); //GET
System.out.println("uri:"+uri); // /myweb/index.html
System.out.println("protocol:"+protocol);//HTTP/1.1
}
/**
* 进一步解析uri
*/
private void parseUri(){
/*
uri是有两种情况的,1:不含有参数的 2:含有参数的
例如:
不含有参数的:/myweb/reg.html
含有参数的:/myweb/reg?username=fanchuanqi&password=123456&nickname=chuanqi&age=22
处理方式:
1:若不含有参数,则直接将uri的值赋值给requestURI
2:若含有参数
2.1:先将uri按照"?"拆分为请求部分和参数部分
将请求部分赋值给requestURI
将参数部分赋值给queryString
2.2:再将参数部分按照"&"拆分出每一组参数
每组参数再按照"="拆分为参数名和参数值
并将参数名作为key,参数值作为value保存到parameters这个Map中
允许页面输入框空着,这种情况该参数的值为null存入parameters即可
*/
String[] data = uri.split("\\?");
requestURI = data[0];
if(data.length>1){//说明?后面有参数部分
//username=fanchuanqi&password=123456&nickname=chuanqi&age=22
queryString = data[1];
//进一步拆分参数部分
//[username=xxxx, password=xxxx, nickname=xxxx, age=xxx]
data = queryString.split("&");
//遍历数组,再进一步拆分每一个参数
for(String para : data){
//[username, xxxx]
String[] paras = para.split("=");
parameters.put(paras[0],paras.length>1?paras[1]:null);
}
}
System.out.println("requestURI:"+requestURI);
System.out.println("queryString:"+queryString);
System.out.println("parameters:"+parameters);
}
/**
* 解析消息头
*/
private void parseHeaders() throws IOException {
while(true) {
String line = readLine();
if(line.isEmpty()){
break;
}
System.out.println("消息头:" + line);
//将消息头按照"冒号空格"拆分为消息头的名字和值并以key,value存入headers
String[] array = line.split(":\\s");
headers.put(array[0],array[1]);
}
System.out.println("headers:"+headers);
}
/**
* 解析消息正文
*/
private void parseContent(){}
/**
* 通过socket获取的输入流读取客户端发送过来的一行字符串
* @return
*/
private String readLine() throws IOException {
InputStream in = socket.getInputStream();
char pre='a',cur='a';//pre上次读取的字符,cur本次读取的字符
StringBuilder builder = new StringBuilder();
int d;
while((d = in.read())!=-1){
cur = (char)d;//本次读取到的字符
if(pre==13&&cur==10){//判断是否连续读取到了回车和换行符
break;
}
builder.append(cur);
pre = cur;//在进行下次读取字符前将本次读取的字符记作上次读取的字符
}
return builder.toString().trim();
}
public String getMethod() {
return method;
}
public String getUri() {
return uri;
}
public String getProtocol() {
return protocol;
}
/*
根据消息头的名字获取对应消息头的值
*/
public String getHeader(String name) {
return headers.get(name);
}
public String getRequestURI() {
return requestURI;
}
public String getQueryString() {
return queryString;
}
/**
* 根据参数名获取对应的参数值
* @param name
* @return
*/
public String getParameter(String name){
return parameters.get(name);
}
}
此版本完成用户注册业务操作
上一个版本我们已经将注册页面中表单提交的数据解析完毕并存入到HttpServletRequest对应属性中了
此版本我们。
4.webserver流程图
2.V15(UserController:reg()方法,uri换成requestURI)
1.主要内容:
-
完成DispatcherServlet处理请求的环节,增加对业务的处理
-
实现:
1:新建一个包:com.webserver.controller这个包中保存所有将来用于处理业务的类
2:在controller包中新建处理与用户数据相关的业务类:UserController
3:在UserController中添加reg()方法,用于处理用户注册逻辑
4:在DispatcherServlet处理请求的环节中,首先我们将原来判断路径使用的请求对象中的uri换成requestURI.
原因:uri中可能表示的路径中含有参数,而不是纯请求部分了。
5:如果请求路径是/myweb/reg,则说明这次请求是reg.html页面form表单提交的请求(action决定)
那么这个请求就是要处理注册业务,因此我们实例化UserController并调用reg方法进行处理即可。
2.UserController(处理业务的类:UserController:reg()方法)
package com.webserver.controller;
import com.webserver.core.DispatcherServlet;
import com.webserver.entity.User;
import com.webserver.http.HttpServletRequest;
import com.webserver.http.HttpServletResponse;
import java.io.*;
import java.net.URISyntaxException;
/*处理与用户相关的操作
* */
public class UserController {
private static File userDir;
private static File root;
private static File staticDir;
static {
try {
root = new File(DispatcherServlet.class.getClassLoader().getResource(".").toURI());
staticDir = new File(root, "static");
} catch (URISyntaxException e) {
e.printStackTrace();
}
userDir = new File("./users");
if (!userDir.exists()) {
userDir.mkdirs();
}
}
public void reg(HttpServletRequest request, HttpServletResponse response) {
//1.获取用户注册页面上输入的注册信息,获取form表单上提交的内容
String username = request.getParameter("username");
String password = request.getParameter("password");
String nickname = request.getParameter("nickname");
String ageStr = request.getParameter("age");
int age = Integer.parseInt(ageStr);
System.out.println(username + "," + password + "," + nickname + "," + ageStr);
//2.将用户信息保存
File userFile = new File(userDir, username + ".obj");
try (
FileOutputStream fos = new FileOutputStream(userFile);
ObjectOutputStream oos = new ObjectOutputStream(fos);
) {
User user = new User(username, password, nickname, age);
oos.writeObject(user);//保存到硬盘上
/*注册成功了*/
File file=new File(staticDir,"/myweb/reg_success.html");
response.setContentFile(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//3 给用户响应一个注册结果页面(注册成功或注册失败)
}
}
3.DispatcherServlet(uri换成requestURI.)
package com.webserver.core;
import com.webserver.controller.UserController;
import com.webserver.http.HttpServletRequest;
import com.webserver.http.HttpServletResponse;
import java.io.File;
import java.net.URISyntaxException;
/**
* 用于处理请求
*/
public class DispatcherServlet {
private static File root;
private static File staticDir;
static {
try {
root = new File(DispatcherServlet.class.getClassLoader().getResource(".").toURI());
staticDir = new File(root, "static");
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
public void service(HttpServletRequest request, HttpServletResponse response) {
String path = request.getRequestURI();
// 首先判断是否为请求一个业务
if ("/myweb/reg".equals(path)) {
System.out.println("开始处理注册!!!!!!!!!!");
UserController controller=new UserController();
controller.reg(request,response);
} else if ("/myweb/login".equals(path)) {
System.out.println("开始处理登陆!!!!!!!!!!");
} else {
File file = new File(staticDir, path);
System.out.println("资源是否存在:" + file.exists());
if (file.isFile()) {//当file表示的文件真实存在且是一个文件时返回true
response.setContentFile(file);
} else {//要么file表示的是一个目录,要么不存在
response.setStatusCode(404);
response.setStatusReason("NotFound");
file = new File(staticDir, "root/404.html");
response.setContentFile(file);
}
}
//测试添加一个额外的响应头
response.addHeader("Server", "WebServer");
}
}