前后端传参范例
Survive by day and develop by night.
talk for import biz , show your perfect code,full busy,skip hardness,make a better result,wait for change,challenge Survive.
happy for hardess to solve denpendies.
目录
概述
接口通信范例是一个常见的任务和功能,在前后台交互中有着非常重要的作用。
上个系列中谈到了几个范例,我们今天来实现部分。如下:
1.基本参数传值:
2.框架层:
单个基本变量用:
集合对象变量:
单个类对象:
多个集合对象:
3.复杂组合传值用:
基本实现还是借助框架的@RequestParam @RequestBody@PathVariable)3个要素实现。
需求:
1.1.基本传值:
2. 框架层:
3. 复杂组合传值用:
设计思路
实现思路分析
1.基本传值:
基本传值传送的是HttpServletRequest 对象,接收的对象是HttpServletResponse ,获取对应的参数是getParamter.(“XXX”)
前台传送的XXX组件的数值即可
public R list(Pageable pageable, HttpServletRequest request) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("pageable", pageable);
params.put("notCategoryId", 1);
Page<Map<String, Object>> page = articleService.findPageByParams(params);
Iterator<Map<String, Object>> iterator = page.getContent().iterator();
while (iterator.hasNext()) {
Map<String, Object> mp = iterator.next();
mp.put("createDate", DateUtils.dateToString((Date) mp.get("createDate"), "yyyy-MM-dd"));
}
Map responseData = new Hashtable();
responseData.put("page", page);
Map<String, Object> data = new HashMap<String, Object>();
// data.put("type", 2);
// data.put("grade", 1);
List<HashMap<String, Object>> articleCategoryPage = articleCategoryService.findListByParams(data);
Iterator<HashMap<String, Object>> iterator2 = articleCategoryPage.iterator();
while (iterator2.hasNext()) {
Map<String, Object> mp = iterator2.next();
mp.put("createDate", DateUtils.dateToString((Date) mp.get("createDate"), "yyyy-MM-dd"));
}
responseData.put("articleCategoryPage", articleCategoryPage);
/*按钮权限*/
//List<String> permsList = (List<String>) request.getSession().getAttribute(SysAdmin.ADMIN_PERMS);
//model.addAttribute("permsList",permsList);
// model.addAttribute("setting",sysConfigService.get());
return R.ok(responseData);
}
2.框架层
2.1 @RequestParam(name = “column”, required = false)
@RequestMapping(value = "/list", method = RequestMethod.POST)
public R list(String startDate, String endDate, String name, String date, Long adLocationId, Integer isShow, Integer pageNo) {
Map<String, Object> params = new HashMap<String, Object>();
params.put("pageable", new Pageable(pageNo, 20));
params.put("startDate", startDate);
params.put("endDate", endDate);
params.put("name", name);
params.put("adLocationId", adLocationId);
params.put("isShow", isShow);
// int[] adLocationIds = {1, 2};
//params.put("netAdLocationIds", adLocationIds);
params.put("netAdLocationIds", null);
Page<Map<String, Object>> page = adService.findPageByParams(params);
for (Map<String, Object> ss : page.getContent()) {
ss.put("startDate", DateUtils.dateToString((Date) ss.get("startDate"), DateUtils.patternD));
ss.put("endDate", DateUtils.dateToString((Date) ss.get("endDate"), DateUtils.patternD));
}
R r = R.ok();
r.put("page", page);
r.put("total", page.getTotal());
return r;
2.2 集合对象变量:
这里常用的是@RequestBody List companyIds,等集合对象接收。
/**
* 文本编辑器上传
*/
@PostMapping(value = "/editor_upload")
public Object editorUpload(@RequestParam("file[]") MultipartFile file[]) {
FileType ft = FileType.image;
List<Object> dataList = new ArrayList<>();
for (MultipartFile imgFile : file) {
if (!this.filesService.isValid(ft, imgFile)) {
throw new RuntimeException("上传失败");
}
Files f = this.filesService.upload(ft, imgFile, 0);
if (f == null) {
throw new RuntimeException("上传失败");
}
Map<String, Object> data = new HashMap<>();
data.put("url", f.getAbsolute());
data.put("name", f.getName());
data.put("error", 0);
dataList.add(data);
}
return dataList;
}
2.3.单个类对象: @RequestBody @Valid MembersAssessmentConfigDto dto
@PostMapping(value = "/editor")
public Object editorUpload(@RequestBody @Valid MembersAssessmentConfigDto dto) MultipartFile file[]) {
FileType ft = FileType.image;
List<Object> dataList = new ArrayList<>();
for (MultipartFile imgFile : file) {
if (!this.filesService.isValid(ft, imgFile)) {
throw new RuntimeException("上传失败");
}
Files f = this.filesService.upload(ft, imgFile, 0);
if (f == null) {
throw new RuntimeException("上传失败");
}
Map<String, Object> data = new HashMap<>();
data.put("url", f.getAbsolute());
data.put("name", f.getName());
data.put("error", 0);
dataList.add(data);
}
return dataList;
}
2.4.多个集合对象:@RequestBody Map<String, PsDeptCostDto> map
这里前台可以使用对应的list 传值。或者map传值通常用于批处理。
public R save(@RequestBody Map<String, PsDeptCostDto> map) {
if (StringUtils.isBlank(name)) {
return R.error("角色名称不能为空");
}
String[] idsArr = null;
if (StringUtils.isNotBlank(ids)) {
idsArr = ids.split(",");
}
Long[] menuIds = null;
if (idsArr != null && idsArr.length > 0) {
menuIds = new Long[idsArr.length];
for (int i = 0; i < idsArr.length; i++) {
menuIds[i] = Long.parseLong(idsArr[i]);
}
}
SysAdmin sa = ShiroUtils.getSysAdmin();
sysRoleService.saveSupplierRole(name, description, menuIds, sa.getSupplierId());
return R.ok("添加成功");
}
3.复杂组合传值用:
这里集中了最复杂的组合式传参的方法:
代码如下:
public R del(@RequestBody MembersAssessmentConfigDto dto,
@RequestParam(name = “column”, required = false) String sortColumn,
@RequestParam(name = “order”, defaultValue = “ASC”, required = false) String sortType,
HttpServletRequest req
)) {
if (ids == null || ids.length == 0) {
return R.error("请选择角色");
}
sysRoleService.deleteByPrimaryKeys(ids);
ShiroUtils.clearCachedAuthorizationInfo();
return R.ok("删除成功");
}
拓展实现
1.request
无论是的jsp的方法,还是html得ajax方法,本质上是servlet的方法,springmvc,nutz都是对其的封装,
request.getParameter()方法:1.获取通过http协议提交过来的数据. 通过容器的实现来取得通过get或者post方式提交过来的数据
request.getParameter()方法传递的数据,会从web客户端传到web服务器端,代表HTTP请求数据,该方法返回String类型的数据
request.setAttribute()和getAttribute()只是在web容器内部流转,仅仅是请求处理阶段
request.getAttribute()方法返回request范围内存在的对象
request.setAttribute() 和 getAttribute() 方法传递的数据只会存在于Web容器内部
HttpServletRequest 类有 setAttribute() 方法,而没有setParameter() 方法
一般通过表单和链接传递的参数使用getParameter.
@param
springMVC的实现类是RequestParamMethodArgumentResolver。
该实现类支持处理有@RequestParam注解的参数:
实现逻辑如下:
根据方法入参获取参数配置信息(调用抽象方法createNamedValueInfo获取,这里就是拿到参数的@RequestParam注解属性)
根据参数配置的name获取真正的参数name(有的子类在1中返回的name可能是占位符或表达式,所以需要处理,这里就是@RequestParam的name值)
根据参数name得到参数值(调用模板方法resolveName),若为null或空但配有默认值,则参数值为resolveStringValue方法(即2中方法)返回值;若为null且必填,调用handleMissingValue方法抛出异常;若为null但非必填,则参数值为handleNullValue方法返回值(若参数是Boolean类型,返回false,否则抛异常)
再调用空方法handleResolvedValue(留给子类@Override从而实现特殊逻辑,这里未覆盖)
返回参数值
@PathVariable
RequestParam几乎一样,简单的提取注解属性。
RequestMappingInfoHandlerMapping在lookupHandlerMethod方法中解析请求路径并匹配到处理器后,调用handleMatch方法解析请求路径和RequestMappingInfo里的路径后设置的
@RequestBody
找出能处理此请求contentType的实现类,在解析前后又插入了RequestBodyAdvice增强类的逻辑。
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
MediaType contentType;
boolean noContentType = false;
try {
//1.调用HttpServletRequest相关方法获取ContentType
contentType = inputMessage.getHeaders().getContentType();
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
if (contentType == null) {
noContentType = true;
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
Class<?> contextClass = parameter.getContainingClass();
Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
if (targetClass == null) {
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
targetClass = (Class<T>) resolvableType.resolve();
}
HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
Object body = NO_VALUE;
EmptyBodyCheckingHttpInputMessage message;
try {
message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
//2.遍历messageConverters
for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
GenericHttpMessageConverter<?> genericConverter =
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
//如果HttpMessageConverter#canRead返回true,即支持处理该contentType类型
if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
(targetClass != null && converter.canRead(targetClass, contentType))) {
//这里的body就是servletRequest.getInputStream()返回的InputStream
if (message.hasBody()) {
//遍历匹配该参数的RequestBodyAdvice,调用其beforeBodyRead方法处理HttpInputMessage
HttpInputMessage msgToUse =
getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
//执行HttpMessageConverter#read返回解析后的参数值
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
//遍历匹配该参数的RequestBodyAdvice,调用其afterBodyRead方法处理body
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
}
break;
}
}
}
catch (IOException ex) {
throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
}
if (body == NO_VALUE) {
if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
(noContentType && !message.hasBody())) {
return null;
}
throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
}
MediaType selectedContentType = contentType;
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
});
return body;
}
还有最基本的一个
RequestParamMethodArgumentResolver解析的。
兜底逻辑支持处理文件类型参数(如MultipartFile),和一些简单类型(如基本类型、String、Date等)。如上图对文件上传等。
而解析逻辑其实和有@RequestParam注解时差不多,只是有注解时从注解name属性取参数名,没注解时取反射的参数名。
父类AbstractNamedValueMethodArgumentResolver调用getNamedValueInfo方法获取参数信息时,没有提到会调用updateNamedValueInfo方法更新参数配置信息
如果从子类createNamedValueInfo方法得到的配置信息里没有参数名,会使用反射参数名。
参考资料和推荐阅读
欢迎阅读,各位老铁,如果对你有帮助,点个赞加个关注呗!~