前面我们介绍的都是关于SprintBoot的基础知识,涉及到的知识也都是关于Spring的Core项目,还没有和网络的请求建立连接并交互
但是我们先来看一下MVC是什么,MVC就是描述了我们和客户端如何交互的思想。也就是我们说的MVC三层结构
- 用户的输入最先到的地方就是控制器
- 控制器接收到请求之后就向数据库中读取数据
- 控制器的操作接收到来自数据库中的数据之后就将内容展示到View中
- 用户最后读取View信息
我们上面明白了MVC,但是SpringMVC是什么呢?
其实,SpringMVC是Spring的web项目中的一个,并且Spring和SpringMVC的年龄都是比SpringBoot的年龄要大的
也就是使用Spring创建项目的时候,我们要勾选Spring Web 表明这是一个web项目
主要经过一下的步骤:
@RequestMapping是一个路由选择,里面的参数是用户访问的网址
如果@RequestMapping直接修饰在方法上面,那么它的参数就是用户直接访问的参数
@ResponseBody
@org.springframework.stereotype.Controller
public class Controller {
//直接写在方法上面
@RequestMapping("/printMVC")
public String print(){
return "hello,SpringMVC";
}
}
上面的代码中,@RequestMapping("/printMVC")
直接修饰了方法,所以我们访问的时候,直接http://localhost:8080/printMVC
即可
@RequestMapping还可以同时修饰类和方法
这个时候,如果我们要访问一个方法的话,访问的网址应当是类网址/方法网址
package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@RequestMapping("/test")
@ResponseBody
@org.springframework.stereotype.Controller
public class Controller {
@RequestMapping("/printMVC")
public String print(){
return "hello,SpringMVC";
}
@RequestMapping("/printHello")
public String print2(){
return "hello,Hello";
}
}
如果我们要打印hello,SpringMVC
就访问http://localhost:8080/test/printMVC
如果我们要打印hello,hello
就访问http://localhost:8080/test/printhello
相当于是多加了一层目录结构
@RequestMapping 可以支持GET请求和POST请求
get请求:
GET http://localhost:8080/test/printMVC HTTP/1.1
Host: localhost:8080
post请求:
POST http://localhost:8080/test/printMVC HTTP/1.1
User-Agent: PostmanRuntime/7.29.0
默认情况下Get请求和Post请求都是可以的
我们可以访问某一个方法的时候,只能接收一种类型的请求
通过指定method,指定RequestMehtod的POST请求,那么这个方法就只可以接收POST请求
@RequestMapping(value = "/printHello",method = RequestMethod.POST)
RequestMethod类的请求有以下这些:
GET,
HEAD,
POST,
PUT,
PATCH,
DELETE,
OPTIONS,
TRACE;
但是常用的只有GET和POST
只可以接收Post请求
@PostMapping("/printMVC")
public String print(){
return "hello,SpringMVC";
}
只可以接收Get请求
@GetMapping("/printHello")
public String print2(){
return "hello,Hello";
}
直接使用方法的参数就可以接收到传递的参数了
@RequestMapping("/onePara")
public String getOnePara(String name){
return name+" 你好";
}
访问的时候:
http://localhost:8080/para/onePara?name=java
访问的参数名要和方法的参数名相同
另外,传递数字的时候,接收的参数最好还是使用包装类,因为如果是int类型的话,如果没有接收到参数,就直接报错.
但是如果是Integer类型,没有接收到参数,id的值就是null
不会再参数接收的时候就报错
@RequestMapping("/object")//参数的接收都应该是包装类
public People getObject(Integer id){
People people=new People();
people.setId(id==null?0:id);
people.setName("cx");
people.setPassword("1234");
return people;
}
如果前端的Key和后端的key不一样是不可以的,为了解决这个问题,我们就可以重命名参数
如前端传递的name.经过重命名之后,后端可以为newName
使用**@RequestParam(“前端的名字”)在方法的参数前面标记**
@RequestMapping("/para")
@Controller
@ResponseBody
public class ControllerPara {
@RequestMapping("/onePara")
public String getOnePara(@RequestParam("name") String newName){
return newName+" 你好";
}
}
如果我们使用了@RequestParam注解的话,所以如果前端没有传递参数的话就会报错,
但是如果不加@RequestParam的话,就不会报错,原因是
下面RequestParam类的实现:
public @interface RequestParam {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
//这里的required默认是true的,表示一定要写上
boolean required() default true;
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}
所以我们就可以让RequestParam的required里面改为false.这样的话我们就算缺省也可以
@RequestMapping("/para")
@Controller
@ResponseBody
public class ControllerPara {
@RequestMapping("/onePara")
public String getOnePara(@RequestParam(value = "name",required = false) String newName){
return newName+" 你好";
}
}
方法里面有多个参数,其中前端在传递参数的时候,不用考虑参数的位置问题,而是根据前端的名字和后端进行匹配
@RequestMapping("/para")
@Controller
@ResponseBody
public class ControllerPara {
@RequestMapping("/onePara")
public String getOnePara(@RequestParam(value = "name",required = false) String newName,
@RequestParam(value = "password",required = false) String newPassword){
return newName+" 你好 "+"密码是 "+newPassword;
}
}
localhost:8080/para/onePara?password=1111&name=lisi
如果要传递的参数过多,我们就可以将参数都放到一个类里面,这样就比较方便了
@RequestMapping("/getbyobject")
public People getByBoject(People people){return people;}
如果没有传递参数:
localhost:8080/para/getbyobject
{
"id": 0,
"name": null,
"password": null
}
传递参数了:
同样,我们传递对象的参数的时候也是使用的键值对进行匹配到类的属性中
请求url:
localhost:8080/para/getbyobject?id=1&name=lllll&password=1234
返回结果值:
{
"id": 1,
"name": "lllll",
"password": "1234"
}
如果我们使用JSON传递对象的话,因为默认它是一个对象,所以我们接收的时候,在方法的参数前面加上@RequestBody
@RequestMapping("/getbyobject")
//加上RequestBody就可以使用JSON传递了
public People getByBoject(@RequestBody People people){return people;}
有一种另类的URL的写法,就是文件路径部分正常写,但是参数部分只写参数的value部分,value之间使用/隔开
这个就是从URL中获取属性,前面是属于从URL的参数部分获取属性
@RequestMapping("/getbyurl/{name}/{password}")
public String getByURL(@PathVariable String name,@PathVariable String password){
return "name: "+name+" password: "+password;
}
前端的URL:
http://localhost:8080/para/getbyurl/cx/123
public boolean upFile(Integer id , @RequestPart("img")MultipartFile file){
boolean result=false;
try {
file.transferTo(new File("D:/桌面/hhh.png"));
result=true;
} catch (IOException e) {
log.error(e.getMessage());
}
return result;
}
但是我们最好是指定好文件的目录和名字,要不然就会发生文件的覆盖的情况.
我们可以根据环境的不同设置不同的目录,对于开发环境和生产环境指定不同的路径
# 生产环境
img:
path: /root/img
# 开发环境
img:
path: D:/桌面
# 主配置文件
# 描述程序运行的平台的配置
spring:
profiles:
active: prod
这样我们就根据配置文件读取信息
@Value("${img.path}")
private String path;
@RequestMapping("/upfile")
/**
* id:上传的是图片的id
* file必须是MutipartFile的,表示是一个
*/
public boolean upFile(Integer id , @RequestPart("img")MultipartFile file){
boolean result=false;
try {
//先获取整个传递过来的图片的名字
String fileName=file.getOriginalFilename();
//取图片的后缀
fileName=fileName.substring(fileName.lastIndexOf("."));
//将随机名和后缀组合成为一个新的图片名
fileName= UUID.randomUUID().toString()+fileName;
//目录名+图片名一起构成文件路径
file.transferTo(new File(path+fileName));
result=true;
} catch (IOException e) {
log.error(e.getMessage());
}
return result;
}
因为SpringMVC是基于Servlet的,所以我们还是可以通过Servlet进行获取
得到Cookie数组之后遍历cookie
@RequestMapping("/getcookie")
public void getCookie(HttpServletRequest request){
Cookie[] cookies =request.getCookies();
for (Cookie cookie:cookies) {
log.info(cookie.getName()+" "+cookie.getValue());
}
}
注解里面是cookie的key,方法里面是value
@RequestMapping("/getcookie2")
//@CookieValue里面是要查找的key值,方法的参数是对应的参数
public void getCookie2(@CookieValue("bite") String value){
log.info("bite "+value);
}
使用HttpServletRequest的getHeader函数
@RequestMapping("/getheader")
public void getHeader(HttpServletRequest request){
log.info("header: "+request.getHeader("User-Agent"));
}
使用@RequestHeader注解,里面是header的key.接收的是value
@RequestMapping("/getheader2")
public void getHeader2(@RequestHeader("User-Agent") String value){
log.info(value);
}
存储session只有一个方法,就是使用HttpServletRequest
1.通过HttpServletRequest的getSession得到一个HttpSession对象,布尔值设置为true
2.通过HttpSession的setAttribute来设置session
@RequestMapping("/setsession")
public void setSession(HttpServletRequest request){
HttpSession session=request.getSession(true);
if(session!=null) {
session.setAttribute("学习","概率论");
}
}
@RequestMapping("/getsession")
public String getSession(HttpServletRequest request){
HttpSession session=request.getSession(false);
if(session!=null&&session.getAttribute("学习")!=null){
return (String)session.getAttribute("学习");
}
return "暂无";
}
SessionAttribute注解(value=Session的key的值,required=false) session的value值
required表示可以没有这个Session对象
@RequestMapping("/getsession2")
public String getSession2(@SessionAttribute(value = "学习",required = false) String value){
return value;
}
将后端的数据返回给前端,
当没有@ResponseBody注解的时候,默认返回的是静态页面
@RequestMapping("/returnstatic")
public Object returnStatic(){
return "/index.html";
}
返回对象
@RequestMapping("/returnbody")
@ResponseBody
public String returnBody(){return "hello body
";}
@RestController同时包含了@Controller和@ResponseBody
@Controller
@ResponseBody
public @interface RestController {
所以只要写一个注解就可以了,更加的方便
@RestController
public class ControllerRest {
@RequestMapping("/print")
public String print(){return "Hello RestController";}
}
前端就是使用form传输数据,
input的name属性要和后端的方法的参数相同
前端:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<form action="cal">
<input type="text" name="num1">
<input type="text" name="num2">
<input type="submit" value="提交">
form>
body>
html>
后端:
@RestController
public class CalController {
@RequestMapping("/cal")
public Integer cal(Integer num1,Integer num2){return num1+num2;}
}
直接返回HashMap对象就是返回的JSON对象
@RequestMapping("/json")
public HashMap<String,String> returnJson(){
HashMap<String,String> hashMap=new HashMap<>();
hashMap.put("java","1");
hashMap.put("MySQL","2");
hashMap.put("Redis","3");
return hashMap;
}
返回报文:
HTTP/1.1 200
Content-Type: application/json
前端使用ajax发送JSON对象字符串到服务器,然后服务器再对这个JSON对象进行接收.
前端:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
<script src="jquery.min.js">script>
head>
<body>
<input type="text" class="name">
<input type="text" class="password">
<input type="button" value="提交" class="submit">
<script>
let name=document.querySelector(".name");
let password=document.querySelector(".password");
let submit=document.querySelector(".submit");
if(jQuery.trim(name.value)==""){
alert("输入为空,请重新输入");
name.focus();
return;
}
if(jQuery.trim(password.value)==""){
alert("输入为空,请重新输入");
password.focus();
return;
}
submit.onclick=function login(){
$.ajax({
type:'post',
url:'login',
contentType:'application/json',
data:JSON.stringify({
name:jQuery.trim(name.value),
password:jQuery.trim(password.value)
}),
success:function(body){
if(body.succ==200){
alert("body.name:"+body.name+" body.password:"+body.password);
}else{
alert("输入不完整");
}
}
})
}
script>
body>
html>
后端:
@RequestMapping("/login")
public HashMap<String,String> login(@RequestBody People people){
System.out.println(people.getName());
System.out.println(people.getPassword());
HashMap<String,String> hashMap=new HashMap<>();
int success=200;
if(people.getPassword()!=null&&people.getName()!=null){
hashMap.put("name",people.getName());
hashMap.put("password",people.getPassword());
hashMap.put("succ",success+"");
}else{
hashMap.put("succ","输入不完全");
}
return hashMap;
}
上面我们是将JSON转为字符串了进行传递的,后端要使用@RequestBody People people进行接收
但是如果我们只传输JSON对象,不将它转为字符串,那么后端接收的时候直接按照参数进行接收就可以
前端:
直接传递JSON对象
data:{
name:jQuery.trim(name.value),
password:jQuery.trim(password.value)
},
后端:
前端传递多少参数,就使用多少参数接收
@RequestMapping("/login2")
public HashMap<String,String> login2(String name,String password){
}
转发是服务器直接将要访问的内容返回
// 转发
@RequestMapping("/forward")
public String forward(){return "forward:/login.html";}
如果省略forword也可以,直接写网址也是可以的
@RequestMapping("/forward2")
public void forward2(HttpServletRequest request, HttpServletResponse response){
try {
request.getRequestDispatcher("/cal.html").forward(request,response);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
重定向相当于是只给了一个网址,还需要我们手动的在去访问
也就是说服务器只是返回了一个新的网址,客户端进行新的网址的访问
和forward一样,都是使用redirect开头
@RequestMapping("/redirect")
public String redirect(){
return "redirect:/cal.html";
}
使用HttpServletResponse的sendRedirect直接进行转发
@RequestMapping("/redirect2")
public void redirect2(HttpServletResponse response){
try {
response.sendRedirect("/cal.html");
} catch (IOException e) {
e.printStackTrace();
}
}