spring mvc如何封装请求参数


使用spring mvc,可以直接把页面提交的参数转为对象,方便开发。但有时候会出现传不进参数的情况,本文通过调试spring mvc源码来分析几种传参方法.

1.不带标注,原生pojo对象传参

2.@RequestParam标注

3.@RequestBody标注

4.@PathVariable("username")标注
这个是取@RequestMapping(value="/login/{username}")中的值,也就是url中的值
5.request.getParameter取参

后面两种比较清晰,都是返回一个String,这个String是什么格式需要我们自己处理。前2种,我们都可以在Controller方法添加任意类型的参数。简单类型可以直接解析,String、Integer等都能正常解析.但复杂类型却不一定能解析成功。
1.先看下客户端用jquery的基本例子
$(document).ready(function(){
	let url=location.href;
	let param=new Object();
	param["url"]=url;
	$.ajax({
		type: "POST",
		url: "/xx/xx.action",
		dataType: "json",
		data:param,
		success: function(obj){
		}
	});
);	

2.服务端spring mvc Controller方法分别定义如下三种方式:
public Result test(String entryWay) {
public Result test(@RequestParam("entryWay") Integer entryWay) {
public Result test(@RequestBody Integer entryWay) {
前两种可以用上面的客户端参数都可正常取到值11,而第三种不行。每三种可以把Integer改String得到原始的String,再自己解析,但这样就失去了用这个标注的意义。

3.分析源码,看三种方式都是怎么实现的。

在以下代码处打断点:
org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.getArgumentResolver(MethodParameter) line: 79
可以得到这三种情况的实现类,都实现了HandlerMethodArgumentResolver接口。

标注
实现类
不带标注
org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor
RequestParam
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver
RequestBody
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor

a.不带标注注入参数堆栈如下:

可以看出,最后还调用Method.invoke方法来调用set方法来实现的注入。

b.RequestParam堆栈如下:
这个可以看出,这种方式用的是request.getParameterValues获得参数,传入name就是标注里的填的值.

@RequestParam其实可不带参数,那么不带参数时,这个name取什么值呢?答案是取@RequestParam修饰参数的的变量名。比如:public Result test(@RequestParam String key1) 这里的name就是key1.
通过asm获得,堆栈如下:

ClassReader的包名为:org.springframework.asm.ClassReader
c.RequestBody要与请求头的contentType参数配合使用
最常用的contentType值就是application/json,在jquery中有两种方法.
1.
$.ajax({
beforeSend: function (xhr) {  
          debugger;
          xhr.setRequestHeader("Content-Type","application/json");  
      }
});

2.
$.ajax({
      		contentType:’application/json’,
});

注意上面的代码有跨域问题,与请求url同域才没有问题。
后台获取这个值来找对应的HttpMessageConverter来读取传入的值,如下:



最后来讨论下,如何传递自定义类参数.
定义类:
public class EntryWay{
	private String name;
 	public void setName(String name) {
		this.name = name;
	}
}


后台分别用三种方式传值:
public Result test(EntryWay entryWay) {
public Result test(@RequestParam("entryWay") EntryWay entryWay) {
public Result test(@RequestBody EntryWay entryWay) {

第一种用这种方式传值,
$.ajax({
data:"name=abc",
})

第二种没有找到可以传值的方法。

第三种用下面方式传值:
$.ajax({
    contentType:’application/json’,
    data: JSON.stringify(paramObj)
});


上面写的并不是完整的代码,但都是各种方式必须的代码,并且这些代码都不一样。

再记录一点,Controller的用法:
一般我把把注解直接写在Controller类上面就行了,比如:
@RequestMapping("/xx")
@RestController
public class TestController {
	@RequestMapping(value = "/doRequest")
	public String doRequest(@RequestParam("violationId") Integer id) {
		return "";
	}
}

但我也看过这样的代码,先定义一个接口,再去实现它:
@RequestMapping("/xx")
@RestController
public interface ViolationApi {
     @RequestMapping(value = "/doRequest") 
     public String doRequest(@RequestParam("violationId")Integer violationId);
}

实现:
@RestController
public class ViolationApiImpl implements ViolationApi { 
      public String doRequest(Integer id){
      //do something
      } 
} 

那么请问,这种接口加实现时。参数用"xx/doRequest?violationId=123",还是参数用"xx/doRequest?id=123" ?
答案是id才是正确的,那么这个接口中方法里的RequestParam根本没有用。有趣的是,接口中的RequestMapping等注解却用了。

spring mvc的数组参数怎么传?
Controller 参数代码:@RequestParam("schoolIds")List schoolIds
这里一定要有RequestParam这个标注,这是与其本类型的参数不一样的地方。
前端传参以","隔开:schoolIds=3000000000000313,111
如果是复杂对象数组,可以把数组在前端转为json,后端再解析回来。如:
//前端:
var paramObj=new Object();
let cols=new Array();
let trs=$("#colTab tbody tr");
for(let i=0;i<trs.size();i++){
	let tr=trs.get(i);
	let colObj=new Object();
	colObj["colName"]=$(tr).find("input[name='colName']").val();
	colObj["colType"]=$(tr).find("select[name='colType']").val();
	cols.push(colObj);
}
paramObj["cols"]=JSON.stringify(cols);
//后端:
public CommonResult addDataSource(HttpServletRequest req, String cols) {
	List<ColReqParam> colObjs=JSONObject.parseObject(cols,new TypeReference<ArrayList<ColReqParam>>(){});
}


SpringMVC Controller那种参数写法,不允许为空?
  1. @RequestParam(value = "code")
  2. String code
答案是1,这个会抛异常.

对于异常参数的处理,捕获异常参数,并打印出请求地址:
@RestControllerAdvice
public class BaseExceptionHandler {
	@ExceptionHandler(MethodArgumentTypeMismatchException.class)
	public JsonResult<Object> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e,HttpServletRequest req){
		log.error(e.getMessage()+" 请求地址:"+req.getServletPath());
		return JsonResult.buildErrorResult(ResultEnum.PARAMETER_ERROR.msg());
	}

	@ExceptionHandler(MissingServletRequestParameterException.class)
	public JsonResult<Object> handleMissingServletRequestParameterException(MissingServletRequestParameterException e,HttpServletRequest req) {
		log.error(e.getMessage()+" 请求地址:"+req.getServletPath());
		return JsonResult.buildErrorResult(ResultEnum.PARAMETER_ERROR.msg());
	}
}


文/程忠 浏览次数:0次   2018-05-06 22:11:28

相关阅读

微信扫描-捐赠支持
加入QQ群-技术交流

评论:
点击刷新

↓ 广告开始-头部带绿为生活 ↓
↑ 广告结束-尾部支持多点击 ↑