I am using some 3rd party stuff that is requiring the statusText on the response to be set. No matter what I do, it is just "". I am using Spring 4.3.4 and Spring Boot 1.4.2.
I tried having the HttpServletResponse as a parameter in my controller method and then call sendError like this:
public void foo(#PathVariable String myVar, HttpServletResponse response) throws IOException
{
//... other stuff
response.sendError(200, "OKOKOKOKOK");
}
However, when the actual response comes back to the browser, the response looks like this:
{"timestamp":1486068130749,"status":200,"error":"OK","message":"OKOKOKOKOK","path":"/my/path"}
And the statusText is still "". I know Spring must be generating this response before sending back to the client. How can I prevent this? Is there some way I can get a non empty string set for the statusText?
I think you must be referring to the HTTP status line sent by the Servlet container.
As of Tomcat 8.5, the HTTP reason phrase is not written anymore, since it's not required and should not be read by HTTP clients: See this Spring Boot issue for more details.
1.4.2 version of Spring-boot uses an embedded tomcat 8.5.6 in which the response text was dropped. So, you are getting statusText as "".
Initially the reason phrase was removed by Tomcat-8.5.
However, later a patch was provided to configure it with an attribute in the Http Connector.
All you have to do is set sendReasonPhrase="true" attribute and it will work as before.
Use any of the below method to configure :
Method-1 : Update the .../apache-tomcat-8.5.x/conf/server.xml to include the attribute as below :
<Connector port="8081" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" sendReasonPhrase="true" />
Method-2 : Configure through bean. (add in the config file)
#Bean
public EmbeddedServletContainerFactory servletContainerFactory() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.addConnectorCustomizers(connector ->
((AbstractProtocol) connector.getProtocolHandler()).setSendReasonPhrase(true));
return factory;
}
NOTE : This config will be dropped in Tomcat-9.0 (ie : response message will not be sent)
Refs :
Bug Report & Discussion
Tomcat documentation for configuring the attribute
Related
Try 1
builder.setHandler(new HttpHandler() {
#Override
public void handleRequest(final HttpServerExchange exchange) throws Exception {
exchange.getResponseHeaders().put(new HttpString("Access-Control-Allow-Origin"), "*");
exchange.getResponseHeaders().put(new HttpString("Access-Control-Allow-Methods"),
"GET, POST, PUT, DELETE, OPTIONS");
String ss = exchange.getResponseHeaders().get(new HttpString("Access-Control-Allow-Headers"))
.getFirst();
System.out.println(ss);
exchange.getResponseHeaders().put(new HttpString("Access-Control-Allow-Headers"), ss);
}
});
Try 2
HttpHandler enHandler = new SetHeaderHandler("Access-Control-Allow-Origin", "*");
builder.setHandler(enHandler);
I tried this above settings to enable the CORS in my undertow service, but it not working with my angular UI. works fine in postman.
it works after adding these line
ResteasyDeployment rd = new ResteasyDeployment();
CorsFilter filter = new CorsFilter();
filter.setAllowedMethods("GET,POST,PUT,DELETE,OPTIONS");
filter.getAllowedOrigins().add("*");
rd.setProviderFactory(new ResteasyProviderFactory());
rd.getProviderFactory().register(filter);
I've recently had the same issue with the Undertow server's ability to add the Access-Control-Allow-Origin header.
Although not very convenient, I was able to set the header inside of the HttpHandler.handleRequest method as such:
httpServerExchange.getResponseHeaders()
.put(new HttpString("Access-Control-Allow-Origin"), "*");
As described in UNDERTOW-1376, the Undertow community seems to suggest using either undertow-cors-filter or cors-filter as community developed solutions.
When protected resources are accessed by unauthenticated request, Servlet based filters will not have a chance to intercept such requests for sending CORS headers. So, any servlet based filters may not be suitable for such requirements.
Also, the undertow-cors-filter seems to be not updated and not working (when I tried with Wildfly 15). Its failing with regex related errors.
CORS Filter functionality could be achieved in Wildfly using Undertow Predicates & Exchange Attributes. A regex can be used to define the list of domains to be whitelisted. And the CORS related response headers could be written conditionally by checking the incoming Origin header and comparing it against the whitelist using regex matching. All these could be configured in the standalone.xml of Wildfly without any Java coding. The following article explains the same.
https://medium.com/amritatech/a-mechanism-to-enable-cors-filter-with-no-coding-57ef2906e023
I'm setting up and application using Undertow, I've set up a ResourceHandler for static files, and Servlet to be used by apache-camel to expose rest services.
I've done this using spring and servlet3.0 in an app container.
In a class extending org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer
#Override
public void onStartup(ServletContext servletContext) throws ServletException
{
super.onStartup(servletContext);
ServletRegistration.Dynamic servlet = servletContext.addServlet("RestServlet", new CamelHttpTransportServlet());
servlet.setLoadOnStartup(1);
servlet.addMapping("/rest/*");
}
And in camel route
restConfiguration()
.component("RestServlet")
.bindingMode(RestBindingMode.json)
.dataFormatProperty("prettyPrint", "true");
Pretty close to whats described in http://camel.apache.org/servlet.html
But if I do this in Undertow as an embedded I get org.apache.camel.NoSuchBeanException: No bean could be found in the registry for: RestServlet of type: org.apache.camel.spi.RestConsumerFactory as I guess Guice never finds the servlets created by Undertow. I tried to manually expose the CamelHttpTransportServlet as a Guice Binding but that doesn't seem to change things.
ClassLoader classLoader = getClass().getClassLoader();
ResourceHandler staticHandler = new ResourceHandler(new ClassPathResourceManager(classLoader, STATIC_RESOURCE_ROOT))
.addWelcomeFiles(INDEX_HTML);
DeploymentInfo deploymentInfo = Servlets.deployment()
.setClassLoader(classLoader)
.setContextPath(ROOT_MAPPING)
.setDeploymentName(DEPLOYMENT_NAME)
.addServlet(
Servlets.servlet("RestServlet", CamelHttpTransportServlet.class)
.addMapping(REST_MAPPING)
.setLoadOnStartup(1)
);
DeploymentManager manager = Servlets.defaultContainer().addDeployment(deploymentInfo);
manager.deploy();
PathHandler path = Handlers.path()
.addPrefixPath(REST_MAPPING, manager.start())
.addPrefixPath(ROOT_MAPPING, staticHandler);
undertow = Undertow.builder()
.addHttpListener(port, LOCALHOST)
.setHandler(path)
.build();
undertow.start();
The static resource work as expected, and it also seems the rest servlet is running and getting the responses but CamelContext won't start up.
I can't use restlet or anything in camel as then the port will be in use so I need to use different port for static files and rest.
Is there any way to have camel identify the Servlet initiated by Undertow?
Ok I finally found out where it went wrong.
I suspect I always used .component("servlet") and not .component("RestServlet"), but Camel wouldn't link this automatically before.
I changed this section to
restConfiguration()
.bindingMode(RestBindingMode.json)
.component("servlet")
.dataFormatProperty("prettyPrint", "true")
.endpointProperty("servletName", "RestServlet);
And the deployment I changed the servlets mapping to /* or else request.getPathInfo() would return null inside CamelHttpTransportServlet.
NB I encountered a problem beause I initially set contextPath to /rest/* which messed up sessions and cookies
DeploymentInfo deploymentInfo = Servlets.deployment()
.setClassLoader(classLoader)
.setContextPath("/rest/")
.setDeploymentName(DEPLOYMENT_NAME)
.addServlet(
Servlets.servlet("RestServlet", CamelHttpTransportServlet.class)
.addMapping("/*")
.setLoadOnStartup(1)
);
I am developing a web application with jboss eap 6.3 which uses resteasy rest framework, I have got a bad encoding problem with special characters passed as FormParam in a POST resources, for example:
#POST
#Path("/post")
public Response createTask(#FormParam("param") String param) {
LOGGER.info("HELLO POST XML. "+param);
return Response.ok(param).build();
}
If I pass a thing like abc èèè i will get a stuff like "abc èà èà è", with jersey rest framework this issue don'exists.
What should i do?
Thanks
RESTEasy solution
Since RESTEasy interprets the request for you using a servlet, your best bet is to use a servlet filter to set the request character encoding:
public class CharacterEncodingFilter implements javax.servlet.Filter {
// ...
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
filterChain.doFilter(request, response);
}
}
Reference How to set charset for my web application?
JBoss solution
To ensure that the application server receives the request parameters in the correct encoding from client requests, you have to configure the connector. For JBoss AS (before version 7) change:
<jboss_install>/server/deploy/jbossweb.sar/server.xml
or in other JBoss AS versions:
<jboss_install>/server/(default)/deploy/jboss-web.deployer/server.xml
to set the connector URIEncoding:
<Connector port="8080" URIEncoding="UTF-8" />
Reference JBoss Seam documentation: 16.1 Internationalizing your app.
This configuration is done differently by changing standalone.xml in JBoss AS 7 and later, as in this answer (also answered in JBossDeveloper forum).
Server independent solution
Since the above is a JBoss dependent solution, my answer would not be complete without providing a server-independent solution.
The most basic is to use a context parameter indicating the character encoding choice for all forms in the application. Setting the context parameter is done in the WEB-INF/web.xml file.
<context-param>
<param-name>PARAMETER_ENCODING</param-name>
<param-value>UTF-8</param-value>
</context-param>
Then your application can read the context parameter and can set the request character encoding before reading any request parameters. You can set the request encoding in either a Java Servlet or in JSP syntax:
<%
String paramEncoding = application.getInitParameter("PARAMETER_ENCODING");
request.setCharacterEncoding(paramEncoding);
String name = request.getParameter("NAME");
%>
Reference Character Conversions from Browser to Database.
Database involvement
You may still have to set the character encoding of your database, otherwise you can lose information as in this diagram:
Reference Character Conversions from Browser to Database.
Miscellaneous
Additional information at Character encoding JSP -displayed wrong in JSP but not in URL and for Tomcat at HttpServletRequest - setCharacterEncoding seems to do nothing.
You can also set the default encoding for the JVM.
A bug titled "Text responses should default to charset UTF-8" was fixed in RESTEasy version 2.3.7.
I just declared in my project jboss-web.xml default encoding, and this solved the problem
<jboss-web>
<default-encoding>UTF-8</default-encoding>
</jboss-web>
Integrating Resteasy in my netty 4 + Guice application works perfectly using server adapter provided by Resteasy (great job guys).
Now my JAX-RS server runs on a different port as my HTTP server (also based on Netty).
So, I need to implement Cross Origin Resource Sharing (CORS) on my Resteasy server by adding following HTTP headers to the response:
Access-Control-Allow-Origin : *
Access-Control-Allow-Methods : GET,POST,PUT,DELETE,OPTIONS
Access-Control-Allow-Headers : X-Requested-With, Content-Type, Content-Length
For now, I have forked NettyJaxrsServer and RestEasyHttpResponseEncoder classes and it works quite good (but not a very "clean" solution to me).
I just wonder how to add those headers to response using something like a customized encoder that I could add to my Netty pipeline (or something else...)
Thanks.
Solution : create a SimpleChannelInboundHandler like this:
public class CorsHeadersChannelHandler extends SimpleChannelInboundHandler<NettyHttpRequest> {
protected void channelRead0(ChannelHandlerContext ctx, NettyHttpRequest request) throws Exception {
request.getResponse().getOutputHeaders().add("Access-Control-Allow-Origin", "*");
request.getResponse().getOutputHeaders().add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
request.getResponse().getOutputHeaders().add("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Content-Length");
ctx.fireChannelRead(request);
}
}
and add this to Netty pipeline just before RestEasyHttpResponseEncoder.
NOTE: Finnally I have added RestEasyHttpRequestDecoder, RestEasyHttpResponseEncoder and RequestHandler at the end of my existing Netty Http server pipeline, so I don't need CORS headers anymore.
I'm developing a REST-ful web service using RESTEasy deployed on Tomcat. I've configured an error page which takes the exception's message and generates an XML based on it when any exception occurs during the request.
This works fine for any application generated exceptions. However, if client sends an invalid XML which cannot be unmarshalled correctly, an javax.xml.bind.UnmarshalException is thrown and Tomcat's default error page is used instead of mine.
I have configured my error page to the error-code 500 in web.xml.
Is using error pages the correct way to handle errors when using RESTEasy or is there an alternative way?
The best way is to use an ExceptionMapper. You create a class UnmarshalExceptionMapper that implements ExceptionMapper. You annotate this with "#Provider" and in your Application constructor you do "classes.add(UnmarshalExceptionMapper.class)" or "singletons.add(new UnmarshalExceptionMapper())".
Your exception mapper will look something like this:
#provider
public class UnmarshalExceptionMapper
implements ExceptionMapper<UnmarshalException> {
public Response toResponse(UnmarshalException exception) {
ResponseBuilder rb =
Response.status(
Response.Status.BAD_REQUEST) // Most appropriate HTTP status code
.entity(your xml goes here)
.type("application/xml");
return rb.build();
}
}
Note that you must set the type to "application/xml" because currently content negotiation is NOT done for exception mappers. To do your own content negotiation, get the HttpHeaders from the request, find the "accept" header, and set the type accordingly.