簡體   English   中英

下載或重定向錯誤消息到 Spring Web MVC 中的另一個控制器操作

[英]Download or redirect with error message to another controller action in Spring web MVC

想法:我有一個 Spring web MVC 操作應該完成以下一項或兩項任務:

  • 從遠程服務器下載文件並將輸入流寫入響應輸出流
  • 或者捕獲下載異常,設置多個錯誤消息之一並重定向到 /addresses 頁面。 地址頁面會顯示錯誤

問題:Spring 無法下載文件並在出現問題時重定向 - 不知何故 flash 屬性不起作用,因為在重定向中丟失了:

@ResponseBody
@RequestMapping(value = "/download/{fileaddress}", method = RequestMethod.GET)
public void download(HttpServletRequest request, HttpServletResponse response, @PathVariable(value = "fileaddress") String fileaddress) throws Exception
{
    if(fileaddress != null && fileaddress.length() > 0)
    {
        try
        {
            // Get the remove file based on the fileaddress
            RemoteFile remotefile = new RemoteFile(fileaddress);

            // Set the input stream
            InputStream inputstream = remotefile.getInputStream();

            // Write the input stream to the output stream or throw an exception
            Utils.writeTo(inputstream, response.getOutputStream());
        }
        catch(MyExceptionA)
        {
            // TODO: Define error message a and pass it to /addresses
            // PROBLEM: Flash attributes that contain all critical error information don't work
            response.sendRedirect(request.getContextPath() + "/addresses");
        }
        catch(MyExceptionB)
        {
            // TODO: Add another error message and redirect
            response.sendRedirect(request.getContextPath() + "/addresses");
        }
        catch(MyExceptionC)
        {
            // TODO: Add another error message and redirect
            response.sendRedirect(request.getContextPath() + "/addresses");
        }
        catch(MyExceptionN)
        {
            // TODO: Add another error message and redirect
            response.sendRedirect(request.getContextPath() + "/addresses");
        }
    }
    else
    {
        // TODO: Add error message
        response.sendRedirect(request.getContextPath() + "/addresses");
    }
}

/addresses的JSP頁面:

<%@ page pageEncoding="UTF-8" %>
<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
<%@ taglib prefix="core" uri="http://java.sun.com/jsp/jstl/core" %>

<tags:index>
    <jsp:attribute name="content">
        <core:if test="${not empty error}">
            <div class="alert alert-danger">
                <p>${error}</p>
            </div>
        </core:if>
        <p>Page under construction!</p>
    </jsp:attribute>
</tags:index>

問題:我如何能夠在 /addresses 站點中顯示錯誤消息(例如簡單字符串)? 使用不同的 URL 參數(error=errora、error=errorb ...)是一個巨大的痛苦,如果有多種錯誤類型並將錯誤消息作為 GET 參數傳遞看起來不專業,並且是編碼問題的根源。

您需要的是RedirectAttributes Model的特殊化,控制器可以使用它來選擇重定向場景的屬性。 因此,對於一個工作示例,請參見下面的代碼:

@ResponseBody
@RequestMapping(value = "/download/{fileaddress}", method = RequestMethod.GET)
public Object download(@PathVariable(value = "fileaddress") String fileaddress, RedirectAttributes redirectAttrs) throws Exception {
    if(StringUtils.hasText(fileaddress)){
        try{
            // Get the remove file based on the fileaddress
            RemoteFile remotefile = new RemoteFile(fileaddress);

            // Set the input stream
            InputStream inputstream = remotefile.getInputStream();
            // asume that it was a PDF file
            HttpHeaders responseHeaders = new HttpHeaders();
            InputStreamResource inputStreamResource = new InputStreamResource(inputStream);
            responseHeaders.setContentLength(contentLengthOfStream);
            responseHeaders.setContentType(MediaType.valueOf("application/pdf"));
            return new ResponseEntity<InputStreamResource> (inputStreamResource,
                                       responseHeaders,
                                       HttpStatus.OK);
         } catch (MyExceptionA | MyExceptionB | MyExceptionC | MyExceptionD ex) {
           redirectAttrs.addFlashAttribute("error", ex.getMessage());
         }        
    } else {
        redirectAttrs.addFlashAttribute("error", "File name is required");
    }
    return "redirect:/addresses";
}

更新:我認為問題是關於RedirectAttributes不可用的情況,否則,解決方案很明顯(使用RedirectAttributes )。

原答案:

在 Spring 不支持RedirectAttributes情況下(例如在ExceptionHandler方法中),我使用以下代碼將消息綁定到閃存映射:

public static Feedback getInstance(HttpServletRequest request, HttpServletResponse response) throws IllegalArgumentException {
    FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);
    Object o = flashMap.get(KEY);
    if (o != null) {
        if (o instanceof Feedback) {
            return Feedback.class.cast(o);
        } else {
            throw new IllegalArgumentException(...);
        }
    } else {
        FeedbackContainer feedbackContainer = new FeedbackContainer();
        flashMap.put(KEY, feedbackContainer);
        FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request);
        flashMapManager.saveOutputFlashMap(flashMap, request, response);
        return feedbackContainer;
    }

其中Feedback / FeedbackContainer是消息的容器,然后通過 JSON 序列化在 JPS 中訪問這些消息。 在您的情況下,您可以僅使用帶有鍵“錯誤”的字符串並直接在 JSP 中訪問它:

void storeErrorMsg(HttpServletRequest request, HttpServletResponse response, String message) {
   FlashMap flashMap = RequestContextUtils.getOutputFlashMap(request);
   flashMap.put("error", message);
   FlashMapManager flashMapManager = RequestContextUtils.getFlashMapManager(request);
   flashMapManager.saveOutputFlashMap(flashMap, request, response);
}

使用我自己的消息容器的主要原因是可能有多個不同級別的消息以及RedirectAttributesModelModelMap附加getInstance方法,所以我不必關心重復的反饋綁定和/或不同的綁定代碼。

我無法解釋為什么它不是從服務器下載文件而是進入您的第二個查詢,您可以通過兩種方式重定向,或者將您的方法返回類型作為 ModelAndView 並這樣做

ModelAndView mav = new ModelAndView("write here your jsp page name even is will work for remote server");
        try {
            mav.addObject("some key","here you can write any message that will you can show in your jsp page..");

然后返回 mav。 像這樣,您可以使用 if 條件重定向所需的頁面數量。

第二種方法可以采用方法返回類型字符串,並且可以直接重定向到所需的jsp。

我建議采用以下方法:由於您有一些遠程文件作為輸入流,因此您無法從發生異常的位置讀取它。 因此,您可以通過首先將文件下載到本地主機上的臨時文件夾來減少出錯的機會。

在這里,如果出現錯誤,您可以按照您的建議進行重定向,也可以再次嘗試並在多次嘗試失敗后重定向。 這樣,在確定文件下載成功之前,您不會向流寫入任何內容。

將文件下載到服務器后,您可以將其傳遞給客戶端。 例如,您可以讀取字節並保存當前位置。 假設在寫入流的過程中發生了異常。 您可以從中間讀取失敗的點恢復寫入。

一個小例子

...
    final String localFileName = saveFileLocally(fileaddress)
    if (localFileName == null) {
        response.sendRedirect(request.getContextPath() + "/addresses");
    } else {
        safeWrite(new BufferedInputStream(new FileInputStream(new File(localFileName))), response.getOutputStream());
    }
...

safeWrite應該支持從文件中間讀取的本地文件讀取異常處理。 對於此類操作,應該有標准的 java api。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM