rest架构风格的http请求(含有解决后台不支持页面返回实现跳转的问题(JSP接口不允许接收来至于PUT,或者DELETE的访问))

什么是rest架构风格?

      rest不是什么新的技术,只是一种架构风格而已,在http协议中使用最广,用这种风格来使用http协议会更加的具有优势。

      REST是Representational State Transfer(在表示层上的状态传输)的缩写,REST是一种WEB应用的架构风格,它被定义为6个限制,满足这6个限制,能够获得诸多优势。

      先用一句话来概括RESTful API(具有REST风格的API): 用URL定位资源,用HTTP名词或形容词(GET,HEAD,POST,PUT,PATCH,DELETE)描述操作,用响应状态码表示操作结果。

      但是REST远远不仅是指API的风格,它是一种网络应用的架构风格。

      另外,需要注意的是,REST的原则不仅仅适用于HTTP协议。但是,由于REST的应用场景绝大部分是WEB应用,本篇文章将基于HTTP来讨论REST。

 

      在之前的开发中,我们通常使用get请求或者post请求来向后台发送请求来得到结果,平时我们在使用这两个请求的时候发现这两个请求对于所有的操作都可以,于是乎从此只存在了这两个请求方式,其他的请求方式也就被我们忽略了。

      但是这也导致了一些问题,就是我们对于每一个不同的请求都必须有一个与之对应的请求路径,这就导致了什么呢,与前端交流的时候他需要知道你写的所有路径,这就很烦人。同时呢,每个人对于请求路径的命名千奇百怪,比如:进行新增时用“insrt”、“add”、“save”......,这也就导致了没有统一的规范,为了解决这一系列的问题,于是rest架构的方式就非常有用了。

      我们用名词或形容词来代替动词,以前我们可能用的是add、update这系列的动词,但是现在我们用该用 /sys/user这种名词性的方式来指定资源,因为我们任何请求都是去获得资源。我们通常用的请求主要是,POST、DELETE、PUT、GET来对应增删改查。

 

怎么使用rest呢?

一、利用ajax传输

在利用ajax传输的时候,四种传输方式都可以用。

下面是例子:

  • 使用REST架构风格,完成对ID的查询
<button id="btn01">使用REST架构风格,完成对ID的查询</button>
$('#btn01').bind("click", function() {
	var arrs = new Array("userInfo", "adds");
	var url = "users/28";
	$.ajax({
		type : "GET",
		url : url,
		data : "arrs=" + arrs,
		success : function(msg) {
			console.log(msg);
		}
	});
});
@RequestMapping("/users")
@Controller
// @RestController 结合了@RequestMapping()和@ResponseBody()的作用
public class UserController {

	private Logger log = LoggerFactory.getLogger(this.getClass());

	@Resource
	private IUserService userServiceImpl;

	@ResponseBody
	@RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
	public UserBean getUserBeanById(@PathVariable("id") int id) {

		UserBean user = null;
		try {
			user = userServiceImpl.getUserBeanById(id);
		} catch (Exception e) {
			// TODO: handle exception
			log.error("UserController-------getUserBeanById()", e);
		}
		return user;
	}

}

       路径指定为“ {  } ” 的形式,那么前端向后台发送请求路径的时候,就可以随便写值了,比如这里 “users/28” ,只要是“users/*”的形式都可以获得,通过method的类型来指定到哪个具体方法上。所以@RequestMapping的method属性一定要指定。

       又比如:你在类上写的是“/user”,在方法上写成“/boy/{loginName}/{passWord}”,那么从前端传过来的路径就可以是“user/boy/*/*”,* 指的是随便写什么,不过你如果在后台要用这个{ }里面传下来的值的话,那么还是要和后台接收这个值的参数类型是一样的,那么在springMVC框架中它才能转换成功,前端的数据传到后台都是字符串,框架有内嵌转换器。

       举个例子:比如你用“/boy/{id}/{passWord}”,那么方法参数就是(@PathVariable("id")long id,@PathVariable("passWord ") String userPassWord),那么我就可以获得这个id和passWord的值,如果前台你传下来“/boy/3/tom”,那么我就可以得到id是3,userPassWord是tom了。

       后台的路径用“/{ }/{ }......”这种形式去定义路径,这样路径就没有固定死,{ }的作用是可以传值的,@PathVariable(" ") 这种形式可以去获得{ }里面的值,该注解里面填写对应的{ }里面的名字,就可以获得这个值。

       对于增删查改,他们的路径就可以写成一样了,比如我都写成 {id} 的形式,那么它会通过不同的请求类型定位到具体的方法上,所以这时候get、post、delete、get就派上了用场。

       即使后台的路径写死后,比如“/user/name”,那么你有增删改查四种请求在访问这个路径的时候,也可以通过method的区别来定位到指定的方法上。

 

  • 使用REST架构风格,完成对用户的新增
<button id="btn02">使用REST架构风格,完成对用户的新增</button>
$('#btn02').bind("click", function() {

	var url = "users/0";
	var data = {
		userName : "座山雕",
		loginName : "zsd",
		password : "123456"
	};

	$.ajax({
		type : "POST",
		url : url,
		data : data,
		success : function(msg) {
			console.log(msg);
		}
	});
});
@RequestMapping("/users")
@Controller
// @RestController 结合了@RequestMapping()和@ResponseBody()的作用
public class UserController {

	private Logger log = LoggerFactory.getLogger(this.getClass());

	@Resource
	private IUserService userServiceImpl;

	@ResponseBody
	@RequestMapping(value = "/{id}", method = RequestMethod.POST, produces = "application/json;charset=utf-8")
	public ResMessage saveUserBean(UserBean user) {
		ResMessage res = new ResMessage(true, "操作成功!");
		try {
			user.setId(null);
			userServiceImpl.saveUserBean(user);
		} catch (Exception e) {
			// TODO: handle exception
			log.error("UserController-------saveUserBean()", e);
			res.setStatus(false);
			res.setInformation("系统繁忙,请稍后重试!");
		}
		return res;
	}

}

 

  • 使用REST架构风格,完成对用户的更改
<button id="btn03">使用REST架构风格,完成对用户的更改</button>
$('#btn03').bind("click", function() {

	var url = "users/30";
	var data = {
		userName : "冯山雕",
		loginName : "fsd",
		password : "123456",
		version : "0"
	};

	$.ajax({
		type : "PUT",
		url : url,
		data : data,
		success : function(msg) {
			console.log(msg);
		}
	});
});
@RequestMapping("/users")
@Controller
// @RestController 结合了@RequestMapping()和@ResponseBody()的作用
public class UserController {

	private Logger log = LoggerFactory.getLogger(this.getClass());

	@Resource
	private IUserService userServiceImpl;

	@ResponseBody
	@RequestMapping(value = "/{id}", method = RequestMethod.PUT, produces = "application/json;charset=utf-8")
	public ResMessage updateUserBean(UserBean user) {

		log.info(user.toString());

		ResMessage res = new ResMessage(true, "操作成功!");
		try {
			userServiceImpl.updateUserBean(user);
		} catch (Exception e) {
			// TODO: handle exception
			log.error("UserController-------updateUserBean()", e);
			res.setStatus(false);
			res.setInformation("系统繁忙,请稍后重试!");
		}
		return res;
	}

}

 

  • 使用REST架构风格,完成对用户的删除
<button id="btn04">使用REST架构风格,完成对用户的删除</button>
$('#btn04').bind("click", function() {
	var url = "users/30/1";
	$.ajax({
		type : "DELETE",
		url : url,
		success : function(msg) {
			console.log(msg);
		}
	});
});
@RequestMapping("/users")
@Controller
// @RestController 结合了@RequestMapping()和@ResponseBody()的作用
public class UserController {

	private Logger log = LoggerFactory.getLogger(this.getClass());

	@Resource
	private IUserService userServiceImpl;

	@ResponseBody
	@RequestMapping(value = "/{id}/{version}", method = RequestMethod.DELETE, produces = "application/json;charset=utf-8")
	public ResMessage deleteUserBean(UserBean user) {

		log.info(user.toString());

		ResMessage res = new ResMessage(true, "操作成功!");
		try {
			userServiceImpl.deleteUserBean(user);
		} catch (Exception e) {
			// TODO: handle exception
			log.error("UserController-------deleteUserBean()", e);
			res.setStatus(false);
			res.setInformation("系统繁忙,请稍后重试!");
		}
		return res;
	}

}

 

  • 使用REST架构风格,完成对用户的分页查询
<button id="btn05">使用REST架构风格,完成对用户的分页查询</button>
$('#btn05').bind("click", function() {
	var url = "users/page";
	var data = {page:"1",rows:"5",gender:1};
		
	$.ajax({
		type : "GET",
		url : url,
		data :data,
		success : function(msg) {
			console.log(msg);
		}
	});
});
@RequestMapping("/users")
@Controller
// @RestController 结合了@RequestMapping()和@ResponseBody()的作用
public class UserController {

	private Logger log = LoggerFactory.getLogger(this.getClass());

	@Resource
	private IUserService userServiceImpl;

	@ResponseBody
	@RequestMapping(value = "/page", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
	public PageBean findUserBeanList2PageBean(PageBean page, UserBean user) {

		try {
			userServiceImpl.findUserBeanList2PageBean(page, user);
		} catch (Exception e) {
			// TODO: handle exception
			log.error("UserController-------findUserBeanList2PageBean()", e);
		}

		return page;
	}

}

 

  • 使用REST架构风格,完成路径传递中文参数
<button id="btn06">使用REST架构风格,完成路径传递中文参数</button>
$('#btn06').bind("click", function() {
	var url = "users/511/九眼桥";
	$.ajax({
		type : "GET",
		url : url,
		success : function(msg) {
			console.log(msg);
		}
	});
});
@RequestMapping("/users")
@Controller
// @RestController 结合了@RequestMapping()和@ResponseBody()的作用
public class UserController {

	private Logger log = LoggerFactory.getLogger(this.getClass());

	@Resource
	private IUserService userServiceImpl;

	@ResponseBody
	@RequestMapping(value = "/{idcard}/{address}", method = RequestMethod.GET, produces = "application/json;charset=utf-8")
	public List<?> findUserBeanByIdcardAndAddress(@PathVariable("idcard") String idcard,
			@PathVariable("address") String address) {
		List<?> datas = null;
		log.info(idcard);
		log.info(address);

		try {
			datas = userServiceImpl.findUserBeanByIdcardAndAddress(idcard, address);
		} catch (Exception e) {
			// TODO: handle exception
			log.error("UserController-------findUserBeanList2PageBean()", e);
		}

		return datas;
	}

}

 

二、REST风格的页面表单传输

页面表单传输只支持get和post请求,所以需要有过滤器,去把post转换成delet或put请求(因为spring自带的转换只支持post转换delete和put

spring提供了两个过滤器HttpPutFormContentFilter和HiddenHttpMethodFilter将请求转换put和delete

在web.xml中进行配置:

	<!-- 配置PUT支持表单提交过滤器 -->
	<filter>
		<filter-name>httpPutFormContentFilter</filter-name>
		<filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
	</filter>
	<!-- 配置表单支持PUT,DELETE提交的过滤器 -->
	<filter>
		<filter-name>hiddenHttpMethodFilter</filter-name>
		<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
	</filter>


	<filter-mapping>
		<filter-name>httpPutFormContentFilter</filter-name>
		<servlet-name>centerController</servlet-name>
	</filter-mapping>
	<filter-mapping>
		<filter-name>hiddenHttpMethodFilter</filter-name>
		<servlet-name>centerController</servlet-name>
	</filter-mapping>


<!-- 通过前端控制器,开启WEB层的spring容器 -->
	<servlet>
		<servlet-name>centerController</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring-mvc.xml</param-value>
		</init-param>
	</servlet>


具体配置看:https://blog.csdn.net/IT_CREATE/article/details/86592388

 

页面表单的 put 和 delete 提交

页面表单提交项中必须有个提交项叫做“_method”,用来指定请求方式

<fieldset>
	<label>HTTP的PUT方法,实现表单提交</label>
	<form action="<%=path %>/users/form" method="POST">
		
		<input type="hidden" name="_method" value="put"/>
		<input type="hidden" name="id" value="24"/>
		<input type="hidden" name="version" value="7"/>
			
		用户名:<input type="text" name="userName"/>
		<br/>
		登录名:<input type="text" name="loginName"/>
		<br/>
		<input type="submit" value="修改"/>
		
	</form>
</fieldset>

<fieldset>
	<label>HTTP的DELETE方法,实现表单提交</label>
	<form action="<%=path %>/users/form" method="POST">
		<input type="hidden" name="_method" value="delete"/>
		<input type="hidden" name="id" value="1"/>
		<input type="hidden" name="version" value="1"/>
		<input type="submit" value="删除"/>
	</form>
</fieldset>

可以看到虽然两个方法的请求路径相同,但是通过不同的请求类型mthod就可以定位到指定的方法上

在后台控制器端:为了实现接收数据后并进行页面的跳转有两种解决方案(因为tomcat8之后,JSP接口不允许接收来至于PUT,或者DELETE的访问;意思就是说我处理了之后,想跳转页面,但因为是put或者是delete的请求类型了,所以不支持页面跳转)

这两种解决方案我会分别用delete和put来举例,反正delete和put也没有什么区别,对于页面表单传输数据,这两个方法可以通用(只要后台是delete或put请求,都可以用这两种方法中的一种)

 

1、重定向

@RequestMapping("/users")
@Controller
// @RestController 结合了@RequestMapping()和@ResponseBody()的作用
public class UserController {

	private Logger log = LoggerFactory.getLogger(this.getClass());

	@Resource
	private IUserService userServiceImpl;

	@RequestMapping("/jump")
	public String toPage(String viewName) {
		//viewName要跳转页面路径
		return viewName;
	}
	
	@RequestMapping(value="/form",method=RequestMethod.PUT)
	public String updateFormUserBean(UserBean user) {
		log.info(user.toString());
		try {
			userServiceImpl.updateUserBean(user);
		} catch (Exception e) {
			// TODO: handle exception
			log.error("UserController-------updateFormUserBean()", e);
		}
		
		
		
		//无法直接使用return "index"; 完成页面的跳转,因为tomcat8以后,JSP接口不允许接收来至于PUT,或者DELETE的访问
		//解决方案一:重定向
		return "redirect:/users/jump?viewName=index";
	}
}

先重定向到一个空白页面上,这里我新建了一个“/jump”页面,说是页面,也就是一个作为中转站的请求路径;重定向的的请求路径后面一定要加上一个参数,也就是你最终要跳转的那个页面,这样就可以利用这个中转跳到你想要的页面了。

解释一下为什么,重定向到一个新页面后,这是第一次请求,因为重定向不是request中的,是response的,所以他不受你是delete请求还是put请求的影响;在跳转了之后,方法中又将你要跳转的最终页面进行了返回,这时会发送第二条请求进行跳转,但这个请求是从新页面发送的请求,所以是get请求了,也就实现了页面跳转。

 

2、请求转发  + 过滤器

首先需要你写一个过滤器,将put和delete请求在进行请求转发f(forward)的时候将put和delete转变成get请求

过滤器 HttpPUTOrDelete2GetFilter 代码:

import java.io.IOException;
import java.util.Locale;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

/**
 * 请求转发时,如果请求方法是PUT|DELETE,就将请求方法强制的转成GET
 * @author Administrator
 *
 */
public class HttpPUTOrDelete2GetFilter implements Filter {

	@Override
	public void destroy() {
		// TODO Auto-generated method stub

	}

	@Override
	public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain chain)
			throws IOException, ServletException {
		// TODO Auto-generated method stub
		HttpServletRequest req = (HttpServletRequest) arg0;
		String method = req.getMethod().toUpperCase();
		if("PUT".equals(method) || "DELETE".equals(method)) {
			method = "GET";
			HttpServletRequestWrapper wrapper = new HttpMethodRequestWrapper(req, method);
			chain.doFilter(wrapper, arg1);
		}
	}

	@Override
	public void init(FilterConfig arg0) throws ServletException {
		// TODO Auto-generated method stub

	}

	
	
	private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {

		private final String method;

		public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
			super(request);
			this.method = method.toUpperCase(Locale.ENGLISH);
		}

		@Override
		public String getMethod() {
			return this.method;
		}
	}
}

然后去xml.web中注册

	<filter>
		<filter-name>httpPUTOrDelete2GetFilter</filter-name>
		<filter-class>com.ge.ssh.filter.HttpPUTOrDelete2GetFilter</filter-class>
	</filter>

	<filter-mapping>
		<filter-name>httpPUTOrDelete2GetFilter</filter-name>
		<servlet-name>centerController</servlet-name>
		<dispatcher>FORWARD</dispatcher>
	</filter-mapping>

<!-- 通过前端控制器,开启WEB层的spring容器 -->
	<servlet>
		<servlet-name>centerController</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:spring-mvc.xml</param-value>
		</init-param>
	</servlet>

然后在控制器中这么写:

@RequestMapping("/users")
@Controller
// @RestController 结合了@RequestMapping()和@ResponseBody()的作用
public class UserController {

	private Logger log = LoggerFactory.getLogger(this.getClass());

	@Resource
	private IUserService userServiceImpl;

	@RequestMapping("/jump")
	public String toPage(String viewName) {
		//viewName要跳转页面路径
		return viewName;
	}
	
	@RequestMapping(value="/form",method=RequestMethod.DELETE)
	public String deleteFormUserBean(UserBean user) {
		log.info(user.toString());
		try {
			userServiceImpl.deleteUserBean(user);
		} catch (Exception e) {
			// TODO: handle exception
			log.error("UserController-------deleteFormUserBean()", e);
		}
		
		
		//解决方案二:请求转发  + 过滤器
		return "forward:/users/jump?viewName=index";
	}
}

先请求转发到一个空白页面上,这里我新建了一个“/jump”页面,说是页面,也就是一个作为中转站的请求路径;请求路径后面一定要加上一个参数viewName,也就是你最终要跳转的那个页面,这样就可以利用这个中转跳到你想要的页面了。

解释一下为什么,请求转发到一个新页面的过程中就将put和delete请求变成了get请求,所以再进行页面返回的时候就是get请求了,不会受到这个限制。

 

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值