Java 如何防止 Spring 3.0 MVC @ModelAttribute 变量出现在 URL 中?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2163517/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 04:17:40  来源:igfitidea点击:

How do I prevent Spring 3.0 MVC @ModelAttribute variables from appearing in URL?

javaspringspring-mvc

提问by Christian

Using Spring MVC 3.0.0.RELEASE, I have the following Controller:

使用 Spring MVC 3.0.0.RELEASE,我有以下控制器:

@Controller
@RequestMapping("/addIntake.htm")
public class AddIntakeController{

  private final Collection<String> users;

  public AddIntakeController(){
    users = new ArrayList<String>();
    users.add("user1");
    users.add("user2");
    // ...
    users.add("userN");
  }

  @ModelAttribute("users")
  public Collection<String> getUsers(){
    return this.users;
  }

  @RequestMapping(method=RequestMethod.GET)
  public String setupForm(ModelMap model){

    // Set up command object
    Intake intake = new Intake();
    intake.setIntakeDate(new Date());
    model.addAttribute("intake", intake);

    return "addIntake";
  }

  @RequestMapping(method=RequestMethod.POST)
  public String addIntake(@ModelAttribute("intake")Intake intake, BindingResult result){

    // Validate Intake command object and persist to database
    // ...

    String caseNumber = assignIntakeACaseNumber();

    return "redirect:intakeDetails.htm?caseNumber=" + caseNumber;

  }

}

The Controller reads Intake information from a command object populated from an HTML form, validates the command object, persists the information to the database, and returns a case number.

控制器从从 HTML 表单填充的命令对象中读取 Intake 信息,验证命令对象,将信息保存到数据库中,并返回案例编号。

Everything works great, except for when I redirect to the intakeDetails.htm page, I get a URL that looks like this:

一切正常,除了当我重定向到 IntakeDetails.htm 页面时,我得到一个如下所示的 URL:

http://localhost:8080/project/intakeDetails.htm?caseNumber=1&users=user1&users=user2&users=user3&users=user4...

http://localhost:8080/project/intakeDetails.htm?caseNumber=1&users=user1&users=user2&users=user3&users=user4...

How do I prevent the user Collection from showing up in the URL?

如何防止用户集合显示在 URL 中?

采纳答案by elaich

Since spring 3.1 the RequestMappingHandlerAdapterprovides a flag called ignoreDefaultModelOnRedirectthat you can use to prevent using the content of the defautl model if the controller redirects.

从 spring 3.1 开始,RequestMappingHandlerAdapter它提供了一个名为的标志ignoreDefaultModelOnRedirect,您可以使用它来防止在控制器重定向时使用 defautl 模型的内容。

回答by axtavt

There are no good ways to solve this problem (i.e. without creating custom components, without excessive amounts of explicit xml configuration and without manual instantiation of RedirectView).

没有很好的方法来解决这个问题(即不创建自定义组件,没有过多的显式 xml 配置,也没有手动实例化RedirectView)。

You can either instantiate RedirectViewmanually via its 4-argument constructor, or declare the following bean in your context (near other view resolvers):

您可以RedirectView通过其 4 参数构造函数手动实例化,或者在您的上下文中声明以下 bean(靠近其他视图解析器):

public class RedirectViewResolver implements ViewResolver, Ordered {
    // Have a highest priority by default
    private int order = Integer.MIN_VALUE; 

    // Uses this prefix to avoid interference with the default behaviour
    public static final String REDIRECT_URL_PREFIX = "redirectWithoutModel:";     

    public View resolveViewName(String viewName, Locale arg1) throws Exception {
        if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
            String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
            return new RedirectView(redirectUrl, true, true, false);
        }
        return null;
    }

    public int getOrder() {
        return order;
    }

    public void setOrder(int order) {
        this.order = order;
    }
}

回答by Christian

Don't use @ModelAttribute. Store the users in the ModelMapexplicitly. You're doing as much with the command object anyway.

不要使用@ModelAttribute. 将用户ModelMap显式存储在。无论如何,您正在对命令对象做同样多的事情。

@RequestMapping(method=RequestMethod.GET)
    public String setupForm(ModelMap model){

        // Set up command object
        Intake intake = new Intake();
        intake.setIntakeDate(new Date());
        model.addAttribute("intake", intake);

        model.addAttribute("users", users);

        return "addIntake";
    }

The disadvantage to this is if a validation error takes place in addIntake(). If you want to simply return the logical name of the form, you must also remember to repopulate the model with the users, otherwise the form won't be setup correctly.

这样做的缺点是如果验证错误发生在addIntake(). 如果您只想返回表单的逻辑名称,您还必须记住用用户重新填充模型,否则表单将无法正确设置。

回答by skaffman

The @ModelAttributemethod annotation is intended to be used for exposing reference datato the view layer. I can't say for sure in your case, but I wouldn't say that a collection of users qualified as reference data. I suggest that you pass this information through to the model explicitly in your @RequestMapping-annotated handler methods.

@ModelAttribute方法注释旨在被用于参照数据曝光到视图层。对于您的情况,我不能肯定地说,但我不会说有资格作为参考数据的用户集合。我建议您在@RequestMapping-annotated 处理程序方法中明确地将此信息传递给模型。

If you still want to use @ModelAttribute, there's a blogentry here that discusses the redirect problem.

如果您仍然想使用@ModelAttribute,这里有一篇博客文章讨论了重定向问题。

But all the previous examples have a common issue, as all @ModelAttribute methods are run before the handler is executed, if the handler returns a redirect the model data will be added to the url as a query string. This should be avoided at all costs as it could expose some secrets on how you have put together your application.

但是之前的所有示例都有一个共同的问题,因为所有 @ModelAttribute 方法都在执行处理程序之前运行,如果处理程序返回重定向,模型数据将作为查询字符串添加到 url。应该不惜一切代价避免这种情况,因为它可能会暴露您如何组合应用程序的一些秘密。

His suggested solution (see part 4 of the blog) is to use a HandlerInterceptorAdapterto make the common reference data visible to the view. Since reference data shouldn't be tightly coupled to the controllers, this shouldn't pose a problem, design-wise.

他建议的解决方案(请参阅博客的第 4 部分)是使用 aHandlerInterceptorAdapter使公共参考数据对视图可见。由于参考数据不应该与控制器紧密耦合,因此在设计方面这应该不会造成问题。

回答by kaliatech

I know this question and answer is old, but I stumbled upon it after having similar issues myself and there isn't a lot of other info out there that I could find.

我知道这个问题和答案很旧,但我在自己遇到类似问题后偶然发现了它,而且我找不到很多其他信息。

I think the accepted answer is not a very good one. The answer right below it by axtavt is much better. The question is not whether annotating model attributes on a controller makes sense. It's about how to issue a "clean" redirect from within a controller that does normally use ModelAttributes. The controller itself normally requires the reference data, but sometimes it needs to redirect somewhere else for exceptional conditions or whatever, and passing the reference data doesn't make sense. I think this a valid and common pattern.

我认为接受的答案不是一个很好的答案。axtavt 在它下面的答案要好得多。问题不在于在控制器上注释模型属性是否有意义。它是关于如何从通常使用 ModelAttributes 的控制器中发出“干净”重定向。控制器本身通常需要参考数据,但有时它需要在异常情况或其他情况下重定向到其他地方,并且传递参考数据没有意义。我认为这是一种有效且常见的模式。

(Fwiw, I ran in to this problem unexpectedly with Tomcat. Redirects were simply not working and I was getting odd error messages like: java.lang.ArrayIndexOutOfBoundsException: 8192. I eventually determined that Tomcat's default max header length is 8192. I didn't realize the ModelAttributes were being added automatically to the redirect URL, and that was causing the header length to exceed Tomcat's max header length.)

(Fwiw,我在使用 Tomcat 时意外遇到了这个问题。重定向根本不起作用,我收到了奇怪的错误消息,例如:java.lang.ArrayIndexOutOfBoundsException: 8192。我最终确定 Tomcat 的默认最大标头长度为 8192。我没有t 意识到 ModelAttributes 被自动添加到重定向 URL,这导致标头长度超过 Tomcat 的最大标头长度。)

回答by sideways

model.asMap().clear();
return "redirect:" + news.getUrl();

:)

:)

回答by Sid

In my application I don't have any use case for exposing model attributes in redirect so I've extended org.springframework.web.servlet.view.UrlBasedViewResolver to override the createView method and used declared in application context:

在我的应用程序中,我没有任何在重定向中公开模型属性的用例,因此我扩展了 org.springframework.web.servlet.view.UrlBasedViewResolver 以覆盖 createView 方法并使用在应用程序上下文中声明:

public class UrlBasedViewResolverWithouthIncludingModeAtttributesInRedirect extends   UrlBasedViewResolver {

        @Override
        protected View createView(String viewName, Locale locale) throws Exception {
            // If this resolver is not supposed to handle the given view,
            // return null to pass on to the next resolver in the chain.
            if (!canHandle(viewName, locale)) {
                return null;
            }
            // Check for special "redirect:" prefix.
            if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
                String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
                boolean exposeModelAttributes = false;
                return new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible(), exposeModelAttributes);
            }
            // Check for special "forward:" prefix.
            if (viewName.startsWith(FORWARD_URL_PREFIX)) {
                String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
                return new InternalResourceView(forwardUrl);
            }
            // Else fall back to superclass implementation: calling loadView.
            return super.createView(viewName, locale);
        }

}


  <bean id="viewResolver" class="com.acme.spring.UrlBasedViewResolverWithouthIncludingModeAtttributesInRedirect">

  </bean>

回答by mabi

manually creating a RedirectView object worked for me:

手动创建一个 RedirectView 对象对我有用:

@RequestMapping(method=RequestMethod.POST)
public ModelAndView addIntake(@ModelAttribute("intake")Intake intake, BindingResult result){

    // Validate Intake command object and persist to database
    // ...

    String caseNumber = assignIntakeACaseNumber();

    RedirectView rv = new RedirectView("redirect:intakeDetails.htm?caseNumber=" + caseNumber);
    rv.setExposeModelAttributes(false);
    return new ModelAndView(rv); 
}

IMHO this should be the default behavior when redirecting

恕我直言,这应该是重定向时的默认行为

回答by alivelove

There is a workaround if it helps your cause.

如果它有助于您的事业,则有一种解决方法。

      @ModelAttribute("users")
      public Collection<String> getUsers(){
           return this.users;
      }

Here you have made it return Collection of String. Make it a Collection of User (it may be a class wrapping string representing a user, or a class with a bunch of data regarding a user). The problem happens with strings only. If the returned Collection contains any other object, this never happens. However, this is just a workaround, and may be, not required at all. Just my two cents. Just make it like -

在这里,您已使其返回字符串集合。使它成为一个用户集合(它可能是一个表示用户的类包装字符串,或者是一个包含一堆关于用户的数据的类)。问题只发生在字符串上。如果返回的 Collection 包含任何其他对象,则不会发生这种情况。但是,这只是一种解决方法,可能根本不需要。只有我的两分钱。让它像 -

      @ModelAttribute("users")
      public Collection<User> getUsers(){
           return this.users;
      }

回答by alivelove

Or, make that request a POST one. Get requests will only display the model attributes as request parameters appearing in the URL.

或者,将该请求设为 POST 请求。获取请求只会将模型属性显示为 URL 中出现的请求参数。

回答by Anonymoose

I implemented a variant of Sid's answerwith less copying and pasting involved:

我实现了Sid 答案的一个变体,涉及较少的复制和粘贴:

public class RedirectsNotExposingModelUrlBasedViewResolver extends UrlBasedViewResolver {

    @Override
    protected View createView(String viewName, Locale locale) throws Exception {
        View view = super.createView(viewName, locale);
        if (view instanceof RedirectView) {
            ((RedirectView) view).setExposeModelAttributes(false);
        }
        return view;
    }

}

This also requires a view resolver bean to be defined:

这还需要定义一个视图解析器 bean:

<bean id="viewResolver" class="com.example.RedirectsNotExposingModelUrlBasedViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
</bean>