Skip to content

Changed linkTo so that it can be invoked outside of a HttpRequest. #410

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import org.springframework.hateoas.core.DummyInvocationUtils;
import org.springframework.hateoas.mvc.ControllerLinkBuilder;
import org.springframework.web.util.UriComponentsBuilder;

/**
* Extension of {@link LinkBuilderFactory} for implementations that also support creating {@link LinkBuilder}s by
Expand Down Expand Up @@ -59,4 +60,16 @@ public interface MethodLinkBuilderFactory<T extends LinkBuilder> extends LinkBui
* @return
*/
T linkTo(Object methodInvocationResult);

/**
* Returns a {@link LinkBuilder} pointing to the URI mapped to the method the result is handed into this method. Use
* {@link DummyInvocationUtils#methodOn(Class, Object...)} to obtain a dummy instance of a controller to record a
* dummy method invocation on. See {@link ControllerLinkBuilder#linkTo(Object)} for an example.
*
* @see ControllerLinkBuilder#linkTo(Object)
* @param builder must not be {@literal null}.
* @param methodInvocationResult must not be {@literal null}.
* @return
*/
T linkTo(UriComponentsBuilder builder, Object methodInvocationResult);
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public static ControllerLinkBuilder linkTo(Class<?> controller, Object... parame
* @see org.springframework.hateoas.MethodLinkBuilderFactory#linkTo(Method, Object...)
*/
public static ControllerLinkBuilder linkTo(Method method, Object... parameters) {
return linkTo(method.getDeclaringClass(), method, parameters);
return linkTo(method.getDeclaringClass(), method, parameters);
}

/*
Expand Down Expand Up @@ -138,6 +138,18 @@ public static ControllerLinkBuilder linkTo(Object invocationValue) {
return FACTORY.linkTo(invocationValue);
}

/**
* Creates a {@link ControllerLinkBuilder} pointing to a controller method. Hand in a dummy method invocation result
* you can create via {@link #methodOn(Class, Object...)} or {@link DummyInvocationUtils#methodOn(Class, Object...)}.
*
* @param builder
* @param invocationValue
* @return
*/
public static ControllerLinkBuilder linkTo(UriComponentsBuilder builder, Object invocationValue) {
return FACTORY.linkTo(builder, invocationValue);
}

/**
* Wrapper for {@link DummyInvocationUtils#methodOn(Class, Object...)} to be available in case you work with static
* imports of {@link ControllerLinkBuilder}.
Expand Down Expand Up @@ -185,8 +197,13 @@ public UriComponentsBuilder toUriComponentsBuilder() {
* @return
*/
static UriComponentsBuilder getBuilder() {

HttpServletRequest request = getCurrentRequest();
HttpServletRequest request;
try {
request = getCurrentRequest();
} catch (IllegalStateException e){
// not in the context of a Http Request, so return blank UriComponentsBuilder...
return UriComponentsBuilder.fromPath("");
}
ServletUriComponentsBuilder builder = ServletUriComponentsBuilder.fromServletMapping(request);

ForwardedHeader forwarded = ForwardedHeader.of(request.getHeader(ForwardedHeader.NAME));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,21 @@ public ControllerLinkBuilder linkTo(Class<?> controller, Method method, Object..
return ControllerLinkBuilder.linkTo(controller, method, parameters);
}

/*
/*
* (non-Javadoc)
* @see org.springframework.hateoas.MethodLinkBuilderFactory#linkTo(java.lang.Object)
*/
@Override
public ControllerLinkBuilder linkTo(Object invocationValue) {
return linkTo(ControllerLinkBuilder.getBuilder(), invocationValue);
}

/*
* (non-Javadoc)
* @see org.springframework.hateoas.MethodLinkBuilderFactory#linkTo(UriComponentsBuilder builder, java.lang.Object)
*/
@Override
public ControllerLinkBuilder linkTo(UriComponentsBuilder builder, Object invocationValue) {

Assert.isInstanceOf(LastInvocationAware.class, invocationValue);
LastInvocationAware invocations = (LastInvocationAware) invocationValue;
Expand All @@ -118,7 +127,7 @@ public ControllerLinkBuilder linkTo(Object invocationValue) {
Method method = invocation.getMethod();

String mapping = DISCOVERER.getMapping(invocation.getTargetType(), method);
UriComponentsBuilder builder = ControllerLinkBuilder.getBuilder().path(mapping);
builder.path(mapping);

UriTemplate template = new UriTemplate(mapping);
Map<String, Object> values = new HashMap<String, Object>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;
import static org.springframework.web.util.UriComponentsBuilder.*;

import org.junit.Before;
import org.junit.Test;
import org.springframework.hateoas.Link;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.util.UriComponentsBuilder;

/**
* Test cases for {@link ControllerLinkBuilder} that are NOT inside an existing Spring MVC request
Expand All @@ -24,18 +27,28 @@ public void setUp() {
}

/**
* @see #342
* Calling linkTo outside of a HTTP request should not throw an exception.
* @see #408
*/
@Test(expected = IllegalStateException.class)
public void createsLinkToMethodOnParameterizedControllerRoot() {
@Test
public void callingLinkToOutsideOfHttpRequestShouldNotThrowException() {
Link link = linkTo(methodOn(ControllerLinkBuilderUnitTest.PersonsAddressesController.class, 15)
.getAddressesForCountry("DE")).withSelfRel();
assertThat(link.getHref(), is("/people/15/addresses/DE"));
}

try {
linkTo(methodOn(ControllerLinkBuilderUnitTest.PersonsAddressesController.class, 15)
.getAddressesForCountry("DE")).withSelfRel();
} catch (IllegalStateException e) {
assertThat(e.getMessage(), equalTo("Could not find current request via RequestContextHolder. Is this being called from a Spring MVC handler?"));
throw e;
}
/**
* Calling linkTo outside of a HTTP request, should allow passing in a custom builder.
* @see #408
*/
@Test
public void linkToShouldTakeAUriCompoentsBuilder() {
Link link = linkTo(fromUriString("https://myproxy.net:1234/somepath"), methodOn
(ControllerLinkBuilderUnitTest
.PersonsAddressesController
.class, 15)
.getAddressesForCountry("DE")).withSelfRel();
assertThat(link.getHref(), is("https://myproxy.net:1234/somepath/people/15/addresses/DE"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -447,14 +447,11 @@ public void linksToMethodWithRequestParamImplicitlySetToFalse() {
@Test
public void mentionsRequiredUsageWithinWebRequestInException() {

exception.expect(IllegalStateException.class);
exception.expectMessage("request");
exception.expectMessage("Spring MVC");

RequestContextHolder.setRequestAttributes(null);

linkTo(methodOn(ControllerLinkBuilderUnitTest.PersonsAddressesController.class, 15).getAddressesForCountry("DE"))
Link link = linkTo(methodOn(PersonsAddressesController.class, 15).getAddressesForCountry("DE"))
.withSelfRel();
assertThat(link.getHref(), endsWith("/people/15/addresses/DE"));
}

private static UriComponents toComponents(Link link) {
Expand Down