spring boot - linkto - ¿Hay algún medio para establecer el host y el puerto para Spring HATEOAS `ControllerLinkBuilder`?
spring hateoas linkto (2)
Spring HATEOAS proporciona el práctico ControllerLinkBuilder
para crear enlaces a métodos de controlador, que se agregarán como hrefs en el JSON / XML devuelto a un cliente. Por ejemplo:
resource.add(linkTo(methodOn(FooController.class)
.findFoo(entity.getClient().getId()))
.withRel("show"));
... podría generar JSON un poco como:
{
"name":"foo",
"links":[
{"rel":"show","href":"http://111.11.11.111:28080/foos/1"}
]
}
Sin embargo...
Tiendo a acceder a mis servicios a través de un proxy inverso. Lo cual creo que la mayoría de la gente probablemente haría. Esto me permite tener múltiples servicios en ejecución en diferentes puertos, pero me permite acceder a ellos a través de la misma URL base. Desafortunadamente, acceder a través de un proxy significa que la URL generada por Spring HATEOAS no es una URL válida para acceder al recurso.
Ahora podría codificar los enlaces, pero eso es bastante frágil. Tener el ControllerLinkBuilder
generar URLs basadas en mi controlador @RequestMapping
es valiosa para mí, ya que evita el riesgo de que mis enlaces no se sincronicen con la realidad.
Así que me preguntaba si hay una propiedad en alguna parte que podría usar para forzar los valores de host y puerto. Estoy usando Spring Boot, idealmente una propiedad que podría agregar al archivo application.properties
en cada entorno.
Nota:
Como este problema parece ser causado por un error en Spring, probablemente debería señalar que estoy usando Spring Boot 1.0.2.RELEASE.
Spring-Boot usa una versión anterior de Spring-HATEOAS, creo que fue .11 que agregaron soporte para X-Forwarded-Port y X-Forwarded-Ssl headers, solo agrega esa versión explícita a tu POM y si tu proxy está funcionando lo correcto y al agregar esos encabezados, deberías ir bien.
Además, si su proxy puede configurarse para NO reescribir el encabezado HOST, el constructor de enlace de controlador incorporado funcionará perfectamente.
Una respuesta pura a la pregunta que planteé originalmente parece implicar la escritura de mi propia implementación de ControllerLinkBuilder
, que tiene la opción de construir la URL en función de las variables de entorno que establezco. Puedo hacer eso.
Sin embargo, la razón por la que estaba tratando de forzar la URL es porque hay un error en ControllerLinkBuilder
. Vale la pena señalar que este error es un error en el código que se copió de ServletUriComponentsBuilder
.
String scheme = request.getScheme();
// The port number retrieved here is the port set by server.port
int port = request.getServerPort();
String host = request.getServerName();
String header = request.getHeader("X-Forwarded-Host");
if (StringUtils.hasText(header)) {
String[] hosts = StringUtils.commaDelimitedListToStringArray(header);
String hostToUse = hosts[0];
if (hostToUse.contains(":")) {
String[] hostAndPort = StringUtils.split(hostToUse, ":");
host = hostAndPort[0];
// Note that the port is set if there is a ":" in the address.
port = Integer.parseInt(hostAndPort[1]);
}
else {
host = hostToUse;
}
}
ServletUriComponentsBuilder builder = new ServletUriComponentsBuilder();
builder.scheme(scheme);
builder.host(host);
// Here lies the bug...
if ((scheme.equals("http") && port != 80) || (scheme.equals("https") && port != 443)) {
builder.port(port);
}
Básicamente, el puerto solo se establece cuando server.port
no es 80 o 443, en lugar de basarse en el puerto utilizado para la solicitud. Esto significa que si el X-Forwarded-Host
está utilizando un puerto predeterminado para el esquema (y, por lo tanto, no tiene nada después del ":"), entonces se usará el puerto de la aplicación en lugar del predeterminado.