Bad special character encoding with RESTEasy - java

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>

Related

Spring not setting statusText on response

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

How do I forward to another URL from a Jetty handler?

Somewhere in our chain of servlet filters there is a filter which forwards the request to the sign-in page when a 401 error is sent, as a usability tweak.
I'm trying to convert this to a Jetty handler because someone wants all web applications to be authenticated by the same logic instead of every webapp having to implement their own authentication.
(The main reason we're using a filter approach in the first place is that nobody was able to get Jetty's container-level authentication to work at all - we have the ability to choose Windows auth or built-in auth and want to be able to switch between these at runtime and were never able to figure out how to make that work with Jetty.)
Out in the Jetty handler, there is some logic like this:
private void handleErrorBetter(HttpServletRequest servletRequest,
HttpServletResponse servletResponse)
throws ServletException, IOException {
if (isPageRequest(servletRequest)) {
ServletContext rootContext = servletRequest.getServletContext().getContext("/");
RequestDispatcher dispatcher = rootContext.getRequestDispatcher("/sign_in");
dispatcher.forward(servletRequest, servletResponse);
} else {
// ...
}
}
servletRequest.getServletContext() appears to correctly return the context for /. Interestingly it appears to do this even if I make a request for a different webapp, but according to the Javadoc I have to use getContext("/") to be sure that I get the root context, so I'm doing that.
Getting the dispatcher succeeds too.
Then I call forward() and this always returns a 404 response to the client.
If I go to /sign_in directly from a web browser, the form loads.
There are only two contexts on the server: the root context /, and a /sample/ context which I'm using to test the second webapp. So I know that /sign_in will be in the root context, but why does forward() give a 404 when forwarding to it?
It turned out to be a bug.
https://bugs.eclipse.org/bugs/show_bug.cgi?id=386359

Getting "405 - Request method 'GET' not supported when calling" method=DELETE

I have a Spring MVC web app. In it a form with a button that's supposed to delete a resource from another resource:
<td>
<form action="/product-bases/${productBase.id}/positions/${position.id}" method="DELETE">
<input type="submit" value="delete" />
</form>
</td>
My controller:
#Controller
#RequestMapping(value = "/product-bases/{id}/positions")
public class ProductBasePositionController {
#RequestMapping(value = "/{positionId}", method = RequestMethod.DELETE)
public ModelAndView delete(#PathVariable Integer productBaseId, #PathVariable Integer positionId) {
So in theory the server should route to the controller. But alas it does not, hence the post ;)
I'm getting
HTTP Status 405 - Request method 'GET' not supported
type Status report
message Request method 'GET' not supported
description The specified HTTP method is not allowed for the requested resource (Request method 'GET' not supported).
Apache Tomcat/7.0.19
Obviously I don't have a get for /positions/id defined yet, but why should I, I want to do a delete for now..
(I'm also trying to run this from my spring-test-mvc framework on a mock servlet without any tomcat implementation in between and it gives me a 400 - bad request..
)
So what am I missing here?
Oh, just to cut some corners: post and get will work for other resources, so the rest of my setup is fine.
The booting server even tells me:
RequestMappingHandlerMapping [INFO] Mapped "{[/product-bases/{id}/positions/{positionId}],methods=[DELETE],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.ModelAndView our.view.controller.ProductBasePositionController.delete(java.lang.Integer,java.lang.Integer)
Anyone as confused as I am? If less so, please enlighten me!
Forms can be submitted via GET or POST only (maybe also PUT, but I doubt that is widely implemented), as form submission requires a method where data is transmitted to the server.
The DELETE method does not have a request body, so specifying it in a form action is unsupported.
Do you have the HiddenHttpMethodFilter filter in your web.xml?
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
http://static.springsource.org/spring/docs/current/spring-framework-reference/html/view.html#rest-method-conversion
The error messages indicate that the browser is actually sending a GET request rather than a DELETE request.
What you need to do is:
examine the source of the web page that the browser is on at the time, and
using the browser's web debugger, see what the request URL and method actually are.
In Microsoft Azure portal i got this error for a Java App when i turned on Authentication/Authorization. If it's the same problem in your case, revert your action.

java tomcat utf-8 encoding issue

I am developing a simple web application using java/jsp/tomcat/mysql, and the most problem lies on the character encoding because I need to deal with UTF-8 encoding instead of the default 8851.
First of I'd like to describe my program structure. I am using a Servlet called Controller.java to handle all request. So in web.xml, I have a Controller servlet which takes all request from *.do.
Then this Controller will dispatch the request based on the requested URL, for example, if client asks for register.do, Controller will dispatch the request to Register.java.
And in the Register.java, there is a method which takes the request as parameter, namely:
public String perform(HttpServletRequest request) {
do something with the request...
}
So the problem is if I want to print something in UTF-8 inside this method, it will give random characters. For example, I have an Enum which stores several constants, one of the properties the Enum has is its name in Traditional Chinese. If I print it in
public static void main(Stirng[] args{
System.out.println(MyEnum.One.getChn());
logger.info(MyEnum.One.getChn());
}
This is printed correctly in Chinese. However, if I put the exact code inside the method dealing with HttpServletRequest:
public String perform(HttpServletRequest request) {
System.out.println(MyEnum.One.getChn());
logger.info(MyEnum.One.getChn());
}
They are printed as random characters, but I can see from the debug window (eclipse) that the variables are holding correct Chinese characters.
So, the same situation happens when I want to store the value from request.getParameter(). In the debug window, I can see the variable is holding correct characters, but one I print it out or try to store it in the database, it is random characters.
I don't know why the behavior acts like this, and this is blocking me from reading submitted form values and store them into database. Could someone give some hints on this?
Great thanks.
Here is a small tutorial what you need to do to make UTF-8 work in your web application:
You have to implement Filter in your application for character encoding:
public class CharacterEncodingFilter implements Filter {
#Override
public void init(FilterConfig filterConfig)
throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
servletRequest.setCharacterEncoding("UTF-8");
servletResponse.setContentType("text/html; charset=UTF-8");
filterChain.doFilter(servletRequest, servletResponse);
}
#Override
public void destroy() {
}
}
You have to make sure that your tomcat's server.xml's file connector element has URIEncoding attribute which value is UTF-8.
<Connector port="8080"
protocol="HTTP/1.1"
connectionTimeout="20000"
URIEncoding="UTF-8"
redirectPort="8443"/>
Also you need to specify this in every JSP page:
<%#page contentType="text/html" pageEncoding="UTF-8"%>
If you need to use UTF-8 encoding (and really, everybody should be going this these days), then you can follow the "UTF-8 everywhere HOWTO" found in the Tomcat FAQ:
http://wiki.apache.org/tomcat/FAQ/CharacterEncoding#Q8
Remember that you also need to support UTF-8 in your database's text fields.
Also remember that sometimes "printing" a String with non-ASCII characters in it to a log file or the console can be affected by
The character encoding of the output stream
The character encoding of the file reader (e.g. cat/less/vi)
The character encoding of the terminal
You might be better off writing the values to a file and then using a hex editor to examine the contents to be sure that you are getting the byte values you are looking for.

How do I specify a query string in Tomcat's <servlet-mapping> <url-pattern>?

I am running Tomcat 5.5.4 and have a servlet running with no problems. However, I'd like to set up a mapping to only launch the servlet when a URL containing a particular query string is submitted.
Right now in web.xml I have:
<servlet-mapping>
<servlet-name>MyServer</servlet-name>
<url-pattern>/go/*</url-pattern>
</servlet-mapping>
If a browser submits http://localhost/MyServer/go?P=123 the servlet is launched and all is well. However, I'd like to only launch that servlet if the URL is exactly as just shown. Unfortunately, right now if the URL is http://localhost/MyServer/go?P=AnyDarnThing the servlet still launches. I have tried setting up the following:
<url-pattern>/go?P=123</url-pattern>
but this results in The requested resource (/MyServer/go) is not available.
I've tried numerous variations (quoting the string, ...) on the above URL pattern but I always get the above error. I notice that if I (for debugging purposes) drop the "?" as in
<url-pattern>/goP=123</url-pattern>
I no longer get the error message and the server launches (but, of course, it doesn't respond to the "query string" because it's not properly formed.) This suggest to me that the "?" is causing a problem in the mapping. I've tried replacing it with its URL special character equivalent as follows:
<url-pattern>/go%3FP=123</url-pattern>
but this gives the same result just described above when I tried dropping the "?" altogether.
I realize I can let the servlet get launched when any query string is submitted and then "ignore" the request for all but the one I care about but there is a reason I'd prefer to not have the servlet launched to begin with. So, my question is, how can I configure the servlet so that it is only launched when a specific query string is included?
Thank you.
You can't do that. The url-pattern is pretty limited.
If you want to have distinct actions taken based on a GET parameter, you can do that manually. In the doGet() method of the servlet have a simple if-clause and invoke different methods depending on the query string / get param.
You can't do that using URL patterns.
You can achive this using filters. Implement a filter which will forward to the Servlet only if the query params exists.
Here is the how the filter will look like:
public class ServletAcessFilter implements Filter
{
public void init(FilterConfig filterConfig) throws ServletException
{
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException
{
//validate the request, check if the request can be forwarded to servlet.
if(request.getParameter("P").equalsIgnoreCase("123")){
filterChain.doFilter(request, response);
} else {
//write what you want to do if the request has no access
//below code will write 404 not found, you can do based on your requirement
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(404);
}
}
public void destroy()
{
}
}
Define the filter in the web.xml like this:
<filter>
<filter-name>ServletAccessFilter</filter-name>
<filter-class>com.ServletAcessFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ServletAccessFilter</filter-name>
<url-pattern>/go/*</url-pattern>
</filter-mapping>
To add to Bozho response, you may also try to move to Clean URLs
This will greatly increase your options in terms of URL pattern matching, and, in particular, may significantly ease configuration of a fronting reverse proxy if you ever need one.

Categories

Resources