1、RestFul风格
概念
Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
功能
资源:互联网所有的事物都可以被抽象为资源
资源操作:使用POST、DELETE、PUT、GET,使用不同方法对资源进行操作。
分别对应 添加、 删除、修改、查询。
传统方式操作资源 :通过不同的参数来实现不同的效果,方法单一,post 和 get
http://127.0.0.1/item/queryItem.action?id=1 查询,GET
http://127.0.0.1/item/saveItem.action 新增,POST
http://127.0.0.1/item/updateItem.action 更新,POST
http://127.0.0.1/item/deleteItem.action?id=1 删除,GET或POST 使用RESTful操作资源 :可以通过不同的请求方式来实现不同的效果,如下:请求地址一样,但是功能可以不同!
http://127.0.0.1/item/1 查询,GET
http://127.0.0.1/item 新增,POST
http://127.0.0.1/item 更新,PUT
http://127.0.0.1/item/1 删除,DELETE
实际运用
在Spring MVC中可以使用 @PathVariable 注解,让方法参数的值对应绑定到一个URI模板变量
下面这个例子也就是说参数a,b绑定到了URL上
package com.adie.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
@Controller
public class RestFulController {
@RequestMapping(value = "/add/{a}/{b}")
public String test1(@PathVariable int a,@PathVariable int b, Model model){
int res=a+b;
model.addAttribute("msg","结果1为"+res);
return "test";
}
}
我们使用RestFul风格
- 使路径变得更加简洁;
- 获得参数更加方便,框架会自动进行类型转换。
- 通过路径变量的类型可以约束访问参数,如果类型不一样,则访问不到对应的请求方法,如这里访问是的路径是/add/1/“10”,则路径与方法不匹配,而不会是参数转换失败。
使用method属性指定请求类型
用于约束请求的类型,可以收窄请求范围。指定请求谓词的类型如GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE等
修改我们刚刚的函数为
package com.adie.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
@Controller
public class RestFulController {
@RequestMapping(value = "/add/{a}/{b}",method = RequestMethod.POST)
public String test1(@PathVariable int a,@PathVariable int b, Model model){
int res=a+b;
model.addAttribute("msg","结果1为"+res);
return "test";
}
}
结果
总结:
Spring MVC 的 @RequestMapping 注解能够处理 HTTP 请求的方法, 比如 GET, PUT, POST, DELETE 以及 PATCH。所有的地址栏请求默认都会是 HTTP GET 类型的。
方法级别的注解变体有如下几个:组合注解
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping
@GetMapping就是我们前面所用的@RequestMapping(method =RequestMethod.GET) 的组合注解。
@RequestMapping(value = “/add/{a}/{b}”,method = RequestMethod.POST)换成组合注解就是@GetMapping("/add/{a}/{b}")
这样简洁方便了许多
2、数据处理及跳转
2.1、结果跳转方式
1.ModelAndView
1.先配置视图解析器 返回的页面是{视图解析器前缀} + viewName +{视图解析器后缀}
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
2.写对应的Controller类
package com.adie.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
String result="HelloSpringMVC";
mv.addObject("msg",result);
mv.setViewName("test");
return mv;
}
}
2.通过SpringMVC注解来实现转发和重定向
不需要视图解析器
请求转发
@Controller
public class ModelTest1 {
@RequestMapping("/m1/t1")
public String test1(Model model){
model.addAttribute("msg","进来了");
return "forward:/WEB-INF/jsp/test.jsp";
}
@RequestMapping("/m1/t2")
public String test2(Model model){
return "/WEB-INF/jsp/test.jsp";
}
}
重定向
@Controller
public class ModelTest1 {
@RequestMapping("/m1/t1")
public String test1(Model model){
return "redirect:/index.jsp";
}
}
加上视图解析器
重定向有无视图解析器的写法都是一样的
@Controller
public class ModelTest1 {
@RequestMapping("/m1/t2")
public String test2(Model model){
return "test";
}
@RequestMapping("/m1/t1")
public String test1(Model model){
return "redirect:/index.jsp";
}
}
2.2、数据处理
1、提交的域名称和处理方法的参数名一致
@GetMapping("t1")
public String test1(String name, Model model){
System.out.println("接收到前端的参数为"+name);
model.addAttribute("msg",name);
return "test";
}
2、提交的域名称和处理方法的参数名不一致
这时候需要使用@RequestParam注解来统一名称
@GetMapping("t1")
public String test1(@RequestParam("username") String name, Model model){
System.out.println("接收到前端的参数为"+name);
model.addAttribute("msg",name);
return "test";
}
只要是前端的参数都加上@RequestParam(),除了可以起别名还可以用于区分是否为前端传来的参数 不用@RequestParam()时,不管前端传递过来了什么这里都能会进入这个方法,但是用了之后前端传递名称不对的参数会报错
3、提交的是一个对象
1.我们先定义一个实体类
package com.adie.pojo;
import lombok.Data;
@Data
public class User {
private int id;
private String name;
private int age;
}
2.Controller处理方法
http://localhost:8080/user/t2?id=1&name=a碟&age=19
@GetMapping("/t2")
public String test2(User user){
System.out.println(user);
return "test";
}
如果使用对象的话,前端传递的参数名和对象名必须一致,否则就是null。
2.3、数据显示到前端
1.ModelAndView
Controller使用接口定义时一般使用ModelAndView,用来封装对象和封装视图
package com.adie.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mv = new ModelAndView();
String result="HelloSpringMVC";
mv.addObject("msg",result);
mv.setViewName("test");
return mv;
}
}
2.Model
注解实现的SpringMVC,返回的视图就是return的值经过视图解析器后解析出来的视图。所以只需要封装模型就是封装对象
@GetMapping("t1")
public String test1(@RequestParam("username") String name, Model model){
System.out.println("接收到前端的参数为"+name);
model.addAttribute("msg",name);
return "test";
}
3. 通过ModelMap
这种使用的比较少,简单来说,Model就是简单版本的ModelMap,ModelMap继承了 LinkedMap,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性
这三种的区别简单来说
-
Model 只有几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解; -
ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性; -
ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。
2.4、乱码问题处理
1.先写一个简单的表单
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/e/t1" method="post">
<input type="text" name="name">
<input type="submit">
</form>
</body>
</html>
2.后台处理Controller
package com.adie.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
@Controller
public class EncodingController {
@PostMapping("/e/t1")
public String test1(String name, Model model){
System.out.println(name);
model.addAttribute("msg",name);
return "test";
}
}
输入中文测试一般都会出现乱码问题
乱码在我们的做项目过程中经常会出现,这里介绍几种处理的方法
1.自己写一个过滤器处理乱码
package com.adie.filter;
import javax.servlet.*;
import java.io.IOException;
public class EncodingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
filterChain.doFilter(request,response);
}
@Override
public void destroy() {
}
}
接着需要在web.xml中注册过滤器
<filter>
<filter-name>encoding</filter-name>
<filter-class>com.adie.filter.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.使用SpringMVC提供的过滤器
<filter>
<filter-name>encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
但是这个过滤器在有些极端情况下对get请求的乱码支持不好
3.修改Tomcat的配置文件
在server.xml中配置
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
4.参考网上大佬的万能自定义过滤器的解决方法
这个过滤器很好的处理了get和post请求
package com.kuang.filter;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
public class GenericEncodingFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse myResponse=(HttpServletResponse) response;
myResponse.setContentType("text/html;charset=UTF-8");
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletRequest myrequest = new MyRequest(httpServletRequest);
chain.doFilter(myrequest, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
private boolean hasEncode;
public MyRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public Map getParameterMap() {
String method = request.getMethod();
if (method.equalsIgnoreCase("post")) {
try {
request.setCharacterEncoding("utf-8");
return request.getParameterMap();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else if (method.equalsIgnoreCase("get")) {
Map<String, String[]> parameterMap = request.getParameterMap();
if (!hasEncode) {
for (String parameterName : parameterMap.keySet()) {
String[] values = parameterMap.get(parameterName);
if (values != null) {
for (int i = 0; i < values.length; i++) {
try {
values[i] = new String(values[i]
.getBytes("ISO-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
hasEncode = true;
}
return parameterMap;
}
return super.getParameterMap();
}
@Override
public String getParameter(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
if (values == null) {
return null;
}
return values[0];
}
@Override
public String[] getParameterValues(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
return values;
}
}
然后同样要在web.xml中配置
乱码问题,需要平时多注意,在尽可能能设置编码的地方,都设置为统一编码 UTF-8
注意:前面在web.xml中配置过滤器的时候
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这一部分,/*,我们一定要配置过滤器处理的请求为**/ ***,而不是/,因为/是不会处理jsp页面的。
2.5、JSON数据的处理
1.jackson
Jackson应该是目前比较好的json解析工具了
使用Jackson,需要导入它的jar包;
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.4</version>
</dependency>
编写一个User的实体类
package com.adie.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int age;
private String sex;
}
编写Controller
使用@ResponseBody //它就不会走视图解析器,会直接返回一个字符串
或者使用@RestController,就不用在每一个方法前加@ResponseBody了,在前后端分离中我们经常这样配置
@Controller
public class UserController {
@RequestMapping(value = "/j1")
@ResponseBody
public String json1() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
User user=new User("a碟",18,"男");
String str = mapper.writeValueAsString(user);
return str;
}
}
测试
现了乱码问题,我们需要设置一下他的编码格式为utf-8,以及它返回的类型;
0通过配置@RequestMapping中的produces属性解决乱码问题
@RequestMapping(value = "/json1",produces = "application/json;charset=utf-8")
还有一种乱码统一解决的方法
我们可以在springmvc的配置文件上添加一段消息StringHttpMessageConverter转换配置
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
处理多个对象
接着我们增加一个方法
@RequestMapping(value = "/j2")
@ResponseBody
public String json2() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
List<User> userList=new ArrayList<User>();
User user1=new User("a碟1",18,"男");
User user2=new User("a碟2",18,"男");
User user3=new User("a碟3",18,"男");
User user4=new User("a碟4",18,"男");
userList.add(user1);
userList.add(user2);
userList.add(user3);
userList.add(user4);
String str = mapper.writeValueAsString(userList);
return str;
}
也是能够很好的处理的
输出时间对象
@RequestMapping(value = "/j3")
@ResponseBody
public String json3() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Date date = new Date();
return mapper.writeValueAsString(date);
}
- 默认日期格式会变成一个数字,是1970年1月1日到当前日期的毫秒数!
- Jackson 默认是会把时间转成timestamps形式 时间戳
解决方案:取消timestamps形式 , 自定义时间格式
@RequestMapping(value = "/j3")
@ResponseBody
public String json3() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS,false);
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
mapper.setDateFormat(sdf);
return mapper.writeValueAsString(sdf.format(date));
}
进一步优化,如果经常使用,我们可以抽取为工具类
将代码封装为工具类
package com.adie.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.text.SimpleDateFormat;
import java.util.Date;
public class JsonUtils {
public static String getjson(Object object) throws JsonProcessingException {
return getjson(object,"yyyy-MM-dd HH:mm:ss");
}
public static String getjson(Object object,String dateFormat) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS,false);
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
mapper.setDateFormat(sdf);
return mapper.writeValueAsString(object);
}
}
那么我们的代码就更加简洁了
@RequestMapping(value = "/j3")
@ResponseBody
public String json3() throws JsonProcessingException {
Date date = new Date();
return JsonUtils.getjson(date,"yyyy-MM-dd HH:mm:ss");
}
2.FastJson
fastjson.jar是阿里开发的一款专门用于Java开发的包,可以方便的实现json对象与JavaBean对象的转换,实现JavaBean对象与json字符串的转换,实现json对象与json字符串的转换。实现json的转换方法很多,最后的实现结果都是一样的。
导入jar包
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.60</version>
</dependency>
fastjson 三个主要的类:
JSONObject 代表 json 对象
-
JSONObject实现了Map接口, 猜想 JSONObject底层操作是由Map实现的。 -
JSONObject对应json对象,通过各种形式的get()方法可以获取json对象中的数据,也可利用诸如size(),isEmpty()等方法获取"键:值"对的个数和判断是否为空。其本质是通过实现Map接口并调用接口中的方法完成的。
JSONArray 代表 json 对象数组
JSON代表 JSONObject和JSONArray的转化
测试
@RequestMapping(value = "/j4")
@ResponseBody
public String json4() throws JsonProcessingException {
Date date = new Date();
List<User> userList=new ArrayList<User>();
User user1=new User("a碟1",18,"男");
User user2=new User("a碟2",18,"男");
User user3=new User("a碟3",18,"男");
User user4=new User("a碟4",18,"男");
userList.add(user1);
userList.add(user2);
userList.add(user3);
userList.add(user4);
String s = JSON.toJSONString(userList);
return s;
}
|