SpringMVC RedirectAttributes 实现重定向带参数 Controller接受参数

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/q11604316081/article/details/79393056

springmvc 在3.1版本后提供了 重定向带参数,之前不知道,使用的是session重定向后又清除这个机智而又粗鲁的方法。 在知道RedirectAttributes能做这件事后,这还能忍?立马回去把代码改了,顺便发个博客


/**
 * 页面跳转 至上传不成功excel 以及list页面
 *
 * @return upload_unsuccessful视图
 */
@GetMapping("/uploadUnsuccessful")

public String toUploadUnsuccessful(Model model,HttpServletRequest request) {
    Map<String, Object> modelMap = (Map<String, Object>) RequestContextUtils.getInputFlashMap(request);

    List<Scheme> year = schemeService.findDistinctYear();
    List<Plan> allTown = forestryService.getAllTown();
    List<Archive> byType = planService.findByType(MyConstant.UPLOAD_BANK_REBACK_FILE_TYPE);
    List<Map<String, String>> fileLists = new ArrayList<>();

    //archive的name 存放着这个文件的批次 年度 城镇 信息,所以遍历 逗号分隔
    byType.forEach(archive -> {
        String[] split = archive.getName().split(",");
        String substring = archive.getUrl().substring(archive.getUrl().lastIndexOf("/") + 1);

        Map<String, String> map = new HashMap<>(fileLists.size());
        map.put("id", archive.getId().toString());
        map.put("batch", split[1]);
        map.put("year", split[0]);
        map.put("town", split[2]);
        map.put("xlsName", substring);
        map.put("remark", "测试备注");

        fileLists.add(map);
    });


    allTown.stream().map(plan -> {
        Plan p = new Plan();
        p.setTown(plan.getTown());
        return p;
    });

    //sideBar
    model.addAttribute("sideBar_todo", true);
    model.addAttribute("sideBar_uploadUnsuccessful", true);


    //info
    model.addAttribute("scheme", year);
    model.addAttribute("towns", allTown);
    model.addAttribute("unsuccess", fileLists);

    if (modelMap!=null){
        model.addAttribute("archiveId",modelMap.get("archiveId"));
    }



    return "upload_unsuccessful";
}


@PostMapping("/unsuccessful_upload")
public String uploadUnsuccess(Model model,
                              MultipartFile upload,
                              String year,
                              String batch,
                              String town,
                              RedirectAttributes attributes

                              ) throws Exception {

    int archiveId = ImportReport.bankBackRead(upload, upload.getOriginalFilename(), year, batch, town, detailService, planService);
    /*
      导入数据title头的年度 批次 镇 与页面填写额不一致
     */

    if (archiveId == 0) {
        model.addAttribute("errorMessage", "上传失败,请检查Excel年度批次乡镇与页面填写是否一致");
        model.addAttribute("url", "uploadUnsuccessful");
        return "upload_unsuccessful_fail";
    }
    attributes.addFlashAttribute("archiveId",archiveId);

    return "redirect:/uploadUnsuccessful";
}

为什么RedirectAttribute能做到呢?原理如下

在重定向时,程序会把我们的请求参数添加到FlashMap中,然后通过flashMapManager来保存起来

	protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,    
	            HttpServletResponse response) throws IOException {    
	        //创建跳转链接    
	        String targetUrl = createTargetUrl(model, request);    
	        targetUrl = updateTargetUrl(targetUrl, model, request, response);    
	        //获取原请求所携带的数据    
	        FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);    
	        if (!CollectionUtils.isEmpty(flashMap)) {    
	            UriComponents uriComponents = UriComponentsBuilder.fromUriString(targetUrl).build();    
	            flashMap.setTargetRequestPath(uriComponents.getPath());    
	            flashMap.addTargetRequestParams(uriComponents.getQueryParams());    
	            FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request);    
	            if (flashMapManager == null) {    
	                throw new IllegalStateException("FlashMapManager not found despite output FlashMap having been set");    
	            }    
	            //将数据保存起来,作为跳转之后请求的数据使用    
	            flashMapManager.saveOutputFlashMap(flashMap, request, response);    
	        }    
	        //重定向操作    
	        sendRedirect(request, response, targetUrl, this.http10Compatible);    
	    }  


FlashMapManager是一个接口,定义了保存FlashMap和获取FlashMap的方法。

两个实现方法都在AbstractFlashMapManager抽象方法中:

saveOutputFlashMap方法实现如下

    
public final void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response) {
    if (!CollectionUtils.isEmpty(flashMap)) {
        String path = this.decodeAndNormalizePath(flashMap.getTargetRequestPath(), request);
        flashMap.setTargetRequestPath(path);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Saving FlashMap=" + flashMap);
        }

        flashMap.startExpirationPeriod(this.getFlashMapTimeout());
        Object mutex = this.getFlashMapsMutex(request);
        if (mutex != null) {
            synchronized(mutex) {
                List<FlashMap> allFlashMaps = this.retrieveFlashMaps(request);
                List<FlashMap> allFlashMaps = allFlashMaps != null ? allFlashMaps : new CopyOnWriteArrayList();
                ((List)allFlashMaps).add(flashMap);
                this.updateFlashMaps((List)allFlashMaps, request, response);
            }
        } else {
            List<FlashMap> allFlashMaps = this.retrieveFlashMaps(request);
            List<FlashMap> allFlashMaps = allFlashMaps != null ? allFlashMaps : new LinkedList();
            ((List)allFlashMaps).add(flashMap);
            this.updateFlashMaps((List)allFlashMaps, request, response);
        }

    }
}

updateFlashMaps的实现如下

protected void updateFlashMaps(List<FlashMap> flashMaps, HttpServletRequest request, HttpServletResponse response) {
    WebUtils.setSessionAttribute(request, FLASH_MAPS_SESSION_ATTRIBUTE, !flashMaps.isEmpty() ? flashMaps : null);
}


就是使用了session 


在dispatcher的doservice方法中   获得session暂存域的东西 保存至request中

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    if (this.logger.isDebugEnabled()) {
        String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
        this.logger.debug("DispatcherServlet with name '" + this.getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
    }

    Map<String, Object> attributesSnapshot = null;
    if (WebUtils.isIncludeRequest(request)) {
        attributesSnapshot = new HashMap();
        Enumeration attrNames = request.getAttributeNames();

        label112:
        while(true) {
            String attrName;
            do {
                if (!attrNames.hasMoreElements()) {
                    break label112;
                }

                attrName = (String)attrNames.nextElement();
            } while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));

            attributesSnapshot.put(attrName, request.getAttribute(attrName));
        }
    }

    request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
    request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
    request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
    request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.getThemeSource());
    if (this.flashMapManager != null) {
        FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
        if (inputFlashMap != null) {
            request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
        }

        request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
        request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
    }

    try {
        this.doDispatch(request, response);
    } finally {
        if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
            this.restoreAttributesAfterInclude(request, attributesSnapshot);
        }

    }

}


也就是说RedirectAttribute实现了  把值存入Session的FLASH_MAPS_SESSION_ATTRIBUTE中,然后在Dispatcher做doService的时候,就已经把Session的值复制到了Request中,然后Session的FLASH_MAPS_SESSION_ATTRIBUTE值会被清空,FLASH_MAPS_SESSION_ATTRIBUTE只是做一个暂存域(个人理解) 到了doService,会自动清空 ,所以Controller 中session看不到addAttribute值



下面的图是我debug的结果  可见request确实存着值  且key为    DispatcherServlet.INPUT_FLASH_MAP




controller获取值的两种方式



至此,介绍完,如果有什么错误,或者建议,可以在下方留言,共同讨论。

展开阅读全文

没有更多推荐了,返回首页