I have a web application built using Spring Boot with Apache Camel and I'm implementing a REST interface.
Currently, using either Camel default Servlet
or Restlet
component, I'm not getting the HTTP Status code reason in the response.
Here is an example response I'm getting while setting the HTTP Status code to 403:
< HTTP/1.1 403
< Date: Mon, 19 Feb 2018 10:01:21 GMT
< Server: Restlet-Framework/2.4.0
< Content-Type: application/json
< Content-Length: 75
How it should be:
< HTTP/1.1 403 Forbidden
< Date: Mon, 19 Feb 2018 10:01:21 GMT
< Server: Restlet-Framework/2.4.0
< Content-Type: application/json
< Content-Length: 75
How can I configure Camel/Restlet/Servlet to include the reason on the HTTP Status code?
My current configuration:
Application.java
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
private static final Logger appLogger = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
appLogger.info("--Application Started--");
}
@Bean
public ServletRegistrationBean servletRegistrationBean() {
SpringServerServlet serverServlet = new SpringServerServlet();
ServletRegistrationBean regBean = new ServletRegistrationBean(serverServlet, "/*");
Map<String,String> params = new HashMap<>();
params.put("org.restlet.component", "restletComponent");
regBean.setInitParameters(params);
return regBean;
}
@Bean
public Component restletComponent() {
return new Component();
}
@Bean
public RestletComponent restletComponentService() {
return new RestletComponent(restletComponent());
}
}
Route Configuration:
@Component
public class RestRouteBuilder extends RouteBuilder {
private static final Logger appLogger = LoggerFactory.getLogger(RestRouteBuilder.class);
private Predicate isAuthorizedRequest = header(HttpHeaders.AUTHORIZATION).isNotNull();
@Override
public void configure() throws Exception {
restConfiguration().component("restlet")
.contextPath("/overlay")
.bindingMode(RestBindingMode.json)
.skipBindingOnErrorCode(false)
.dataFormatProperty("prettyPrint", "true");
rest("/")
.get()
.route()
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(403))
.setBody(constant("Forbidden"))
.endRest();
}
}
I also tried adding .setHeader(Exchange.HTTP_RESPONSE_TEXT, constant("Forbidden"))
but the result was the same.
How can I configure Camel/Restlet/Servlet to include the reason on the HTTP Status code?
The response is being sent at org.restlet.engine.adapter.ServerCall.sendResponse(), where the response head and body are written to the OutputStream:
writeResponseHead(response); // <--
if (responseEntity != null) {
responseEntityStream = getResponseEntityStream();
writeResponseBody(responseEntity, responseEntityStream);
}
... and writeResponseHead(response) does nothing by default, check it:
protected void writeResponseHead(Response response) throws IOException {
// Do nothing by default
}
Update: ... the HttpStatus(value, reasonPhrase) has the reasonPhrase, but isn't used to stringify:
HttpStatus(int value, String reasonPhrase) {
this.value = value;
this.reasonPhrase = reasonPhrase;
}
...
@Override
public String toString() {
return Integer.toString(this.value);
}
Update 2: ... the DefaultRestletBinding.populateRestletResponseFromExchange does the following:
// get response code
Integer responseCode = out.getHeader(Exchange.HTTP_RESPONSE_CODE, Integer.class);
if (responseCode != null) {
response.setStatus(Status.valueOf(responseCode));
}
... it only uses the Status.valueOf.
Although there is a Status.reasonPhrase, it isn't accessible.
6.1.1 Status Code and Reason Phrase
(...) The client is not required to examine or display the Reason-Phrase.
(...) The reason phrases (...) MAY be replaced by local equivalents without affecting the protocol.
3.1.2. Status Line
(...) A client SHOULD ignore the reason-phrase content.
8.1.2.4. Response Pseudo-Header Fields
(...) HTTP/2 does not define a way to carry the version or reason phrase that is included in an HTTP/1.1 status line.