29
@RequestMapping(value = "/contact.html", method = RequestMethod.POST)
public final ModelAndView contact(
        @RequestParam(value = "name", required = false) Optional<String> name) {

How does Spring's @RequestMapping handle an Optional from Guava library if the parameter value is not required and nothing is sent?

Will it be:

  • Set to null
  • Set to Optional.absent()

Can Optional.fromNullable(T) be used to accept the request?

sebkur
  • 658
  • 2
  • 9
  • 18
Anders
  • 9,988
  • 7
  • 30
  • 36
  • 1
    I believe that it will be `null`, because Spring does not know anything of Guava, at least by default. – Vladimir Matveev Nov 27 '13 at 10:11
  • It may be possible to register a `PropertyEditor` to tell Spring how to convert the `name` request parameter to an `Optional`. http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/validation.html#beans-beans-conversion – Will Keeling Nov 27 '13 at 10:16
  • 2
    There's no such thing as `Optional.of(null)~. – Louis Wasserman Nov 27 '13 at 23:09

3 Answers3

47

EDIT (October 2015): Spring 4 handles java.util.Optional (from Java 8) out of the box and guarantees that Optional itself is not null, but original question was about Guava's com.google.common.base.Optional which usage as @RequestParam is highly discouraged in this specific case (because it can be null).

ORIGINAL ANSWER (about Guava's Optional):

Don't do that, just use String and let Spring handle null in its way.

Optional<T> is supposed to be used as return value and rarely as a parameter. In this particular case Spring will map missing "name" parameter to null, so even if after implementing custom property editor you'll finish with null check:

@RequestMapping("foo")
@ResponseBody
public String foo(@RequestParam(required = false) final Optional name) {
  return "name: " + (name == null ? "null" : name.get());
}

which is completely unnecessary (and missuses Optional), because it can be achieved with:

@RequestMapping("foo")
@ResponseBody
public String foo(@RequestParam(required = false) final String name) {
  return "name: " + (name == null ? "null" : name);
}
sebkur
  • 658
  • 2
  • 9
  • 18
Grzegorz Rożniecki
  • 27,415
  • 11
  • 90
  • 112
  • as far as I know latest version of Spring works good with jdk Optional type – Yura Apr 13 '15 at 10:59
  • @Yura Spring 4 handles [`java.util.Optional`](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html) out of the box, but I'm quite sure that [`com.google.common.base.Optional`](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Optional.html) (which OP uses) is not supported. – Grzegorz Rożniecki Apr 13 '15 at 11:57
  • methodName(@RequestParam("appRefNo") String appRefNo, @RequestParam(value="appStatus", required=false) String appStatus) – Md. Kamruzzaman Jun 09 '16 at 06:02
4

I recommend to use the Java 8 version: java.util.Optional. Look at the Oracle documentation in http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html. Also put a name to the variable, specially if your using Spring 3 or higher:.

import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class LoginController
{

    private static final Logger LOGGER = LoggerFactory.getLogger(LoginController.class);

    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public ModelAndView getLoginPage(@RequestParam(name = "error", required = false) Optional<String> errorMsg)
    {
        //error.ifPresent(LOGGER::debug); //Java 8 with Optional
        return new ModelAndView("login", "error", errorMsg);
    }

}

java.util.Optional is very useful for managing optional parametrers, like errors in Spring.

Community
  • 1
  • 1
EliuX
  • 11,389
  • 6
  • 45
  • 40
  • Isn't the parameter name specified with value instead of name? – jzonthemtn Jun 26 '15 at 23:22
  • As you may see here https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html, value is an alias of name – EliuX Mar 17 '16 at 04:12
  • I am using like bellow search(@RequestParam("appRefNo") String appRefNo, @RequestParam(value="appStatus", required=false) String appStatus) { } – Md. Kamruzzaman Jun 09 '16 at 06:02
  • @Md.Kamruzzaman your approach is right, but i would rather not to deal with `null` parameters and using `Optional` the code is shorter and you can provide more features using high order functions. – EliuX Jun 09 '16 at 18:41
0

The answer on you question will be optional parameter first is setting to null.

In Spring HandlerMethodInvoker I found resolveRequestParam method

    Object paramValue = null;
    ...
    if (multipartRequest != null) {
    ...
    // Check if this is multipart request and set paramValue in this case.
    }
    // Otherwise
    if (paramValue == null) {
        String[] paramValues = webRequest.getParameterValues(paramName);
        if (paramValues != null) {
            paramValue = (paramValues.length == 1 ? paramValues[0] : paramValues);
        }
    }
    if (paramValue == null) {
       if (defaultValue != null) {
            paramValue = resolveDefaultValue(defaultValue);
        }
        else if (required) {
            raiseMissingParameterException(paramName, paramType);
        }
        ...
     }
     ...

So first we check if it is a multipart request. Otherwise we get parameters values by parameter name from servlet request. Finally if parameter value null we check if parameter is required. If required we throw exception, otherwise return null.

mvb13
  • 1,514
  • 3
  • 18
  • 33