我们都知道SpringBoot的习惯优于配置简化了我们的开发,SpringBoot的Web起步依赖集成了SSM,而REST风格的设计也是当下互联网比较流行的设计风格。之前我做过SSM-REST的CRUD的Demo,最近学了SpringBoot,于是想把以前的项目来一次换血,但是遇到了一个比较坑的地方,现在来说一下。
关于REST风格详细:https://blog.csdn.net/belalds/article/details/80060296
先说下SpringMVC中REST的支持与实现;
我们以POST(增)、DELETE(删)、PUT(改)、GET(查)为例,但是http只支持get、post请求两种,那么扩展的话就得用点手段;
官方做法就是在前端设计隐藏表单,设置其中一个属性为 _method,其值可设为POST、DELETE等,表单提交的方式必须为post。那么表单提交的时候就多携带了 _method 参数,
<div>
<form method="post" action="/url">
<!-- “偷偷携带” -->
<input type="hidden" name="_method" value="PUT"/>
<!-- 其它正常提交的参数 -->
<input type="text" name="param1" value="从键盘输入"/><br>
<input type="text" name="param2" value="从键盘输入"/><br>
<input type="text" name="param3" value="从键盘输入"/><br>
</form>
</div>
在Ajax里如下,以获取user数据为例:
$.ajax({
'url':'/users',
'type':'post',
'data':{'_method':'GET'},
'dataType':'json',
'async':false,
'success':function(result){
// 得到结果 result
},
'error':function(xhr,textStatus,errorThrown) {
alert("getData|GG");
}
});
后端SpringMVC,返回所有user数据(json形式):
@Controller
@ResponseBody // 所有返回的数据都转成 json
public class AjaxRestUserController {
@RequestMapping(value = "/users",method = RequestMethod.GET) // 指定请求方式为 GET
public List findAllUser() {
return userService.findUsers();
}
}
还需要在web.xml配置一个过滤器,
<!--过滤器 支持 rest风格方法 要在前端控制器获取请求之前拦截-->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<servlet-name>springmvc</servlet-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
rest过滤器过滤部分的源码,到这里大致就能明白为什么前端要以post方式提交了吧。
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String paramValue = request.getParameter(this.methodParam);
if ("POST".equals(request.getMethod()) && StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
HttpServletRequest wrapper = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
filterChain.doFilter(wrapper, response);
} else {
filterChain.doFilter(request, response);
}
}
以上就是SpringMVC的REST实现,可以看到其核心就是:隐藏表单携带_method参数配合rest过滤器的使用。
下面再来看看SpringBoot里怎么做;
1、几个注解之间的关系,左边是SpringMVC中的,右边是SpringBoot-web中的,后者在前者之上扩展了:
@Controller + @ResponseBody == @RestController
@RequestMapping(method = RequestMethod.GET) == @GetMapping
其它rest的POST、DELETE也是一样。
2、所以后端如下,或者直接使用前面SpringMVC后端那样的注解也是一样的:
@RestController
public class RestUserController {
@Autowired
public IUserService userService;
@GetMapping(value = "/users")
public List findPage() {
return userService.findAllUsers();
}
3、重点来了!这时候前端依旧使用SpringMVC的前端方式设计,那么会出现后端只接收到post的情况,因为我们表单提交或者Ajax提交就是以post方式提交的,而其它如 GET、PUT、DELETE请求等于就是无法识别而不处理了。
正确的前端提交方式如下:
$.ajax({
'url':'/users',
'type':'GET',
// 'data':{'_method':'GET'},
'dataType':'json',
'async':false,
'success':function(result){
// 得到结果 result
},
'error':function(xhr,textStatus,errorThrown) {
alert("getData|GG");
}
});
在SpringBoot里,不再使用post提交与 _method参数结合,直接使用rest方式提交!!!
这个说实话有点搞人,后端一模一样的设计,前端却不一样。初步猜测可能是SpringBoot在请求处理上已经支持了rest方式的请求,过滤器那块不一定非得是post提交方式了。这也映衬了SpringBoot习惯优于配置的思想。