How parse object in HttpServletResponse? - java

I have a problem when I try to encode object in http response. I do not know how to do it. I will have use the header?
public class Download extends HttpServlet{
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException{
PersistenceManager pm = PMF.get().getPersistenceManager();
String method = req.getParameter("method");
if(method.equals("view")){
Query query = pm.newQuery(Article.class);
List<Article> articles=null;
try {
articles=(List<Article>) query.execute();
}
finally {
query.closeAll();
}
Article article= art.get(0);
res.setContentType("application/octet-stream");//??
//problem here
}
}
}

There is a setHeader() method on the HttpServletResponse class. For example, you can set the content type using the following statement:
response.setHeader("Content-Type", "text/html");
Here is a link with a good tutorial on the topic: http://tutorials.jenkov.com/java-servlets/httpresponse.html
Here is the JavaDoc on the class if you need more parameters:
http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html

Related

SpringBoot embedded tomcat server reads unicode character in query parameter as null

I have a rest end point designed in spring boot. Tomcat is being used as embedded server. It takes a query parameter.
When I pass query parameter as param1%uFF07 tomcat internally reads parameter as null
When I pass query parameter as param1%FF07 tomcat reads as some character.
tomcat only reads '%' character when followed by two hexadecimal numbers, if u is placed after '%' character tomcat parse parameter as null with message
Character decoding failed. Parameter [name] with value [param1%uFF07]
has been ignored. Note that the name and value quoted here may be
corrupted due to the failed decoding. Use debug level logging to see
the original, non-corrupted values. Note: further occurrences of
Parameter errors will be logged at DEBUG level.
Here is spring boot controller code
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value = "name", required = false) String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
}
You are passing % sign in your url, but % is symbol in url, to pass % as it is... you will have to pass %25 then it will work as you expected.
So, if you pass %25uFF07 then it will show you %uFF07 as value.
No need to change anything in application.properties or any kind of settings. I have tested this in my project.
Please feel free to ask for any clarification. Hope It Helps.
I found out a way using filters. Basics about filters could be found over here. We can intercept request query string there and use Tomcat UDecoder class to parse the query string and if any exception is thrown we can show response of 400
public class SimpleFilter implements Filter {
private final UDecoder urlDecoder = new UDecoder();
private final Logger logger = LoggerFactory.getLogger(SimpleFilter.class);
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
String queryString = httpServletRequest.getQueryString();
if (queryString != null) {
ByteChunk byteChunk = new ByteChunk();
byteChunk.setBytes(queryString.getBytes(), 0, queryString.length());
try {
urlDecoder.convert(byteChunk, true);
} catch (IOException ioException) {
logger.error("Hazarduos character found in request parameter.");
httpServletResponse.setStatus(HttpStatus.BAD_REQUEST.value());
return;
}
}
chain.doFilter(request, response);
}
#Override
public void destroy() {
}
}

Posting data from Servlet to Rest webservice

I want to submit form data to servlet which is Jersey rest client
and inside this servlet I have to call a Restful Webservice.
I have to pass all form data to that rest Webservice and after that we will get a response object from rest to servlet.
I have to pass this response object directly to JSP page here request and response will be in JSON format.
you can use servlet to send data to web service by sendRedirect("<url>")
Is it really necessary to call REST Class from a Servlet?
If so, the following is the way. But your REST call will be treated as a plain class. To call any method of the Rest class you have to create its object and access its methods.
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String requestURI = req.getRequestURI();
RestClass restClassObj = new RestClass();
String parameter1 = "";
String parameter2 = "";
String parameter3 = "";
String restResponse = "";
if(StringUtils.endsWith(requestURI, "services") || StringUtils.endsWith(requestURI, "services/")){
parameter1 = req.getParameter("parameter1");
parameter2 = req.getParameter("parameter2");
parameter3 = req.getParameter("parameter3");
restResponse = restClassObj.getRestClassMethodResponse(parameter1,parameter2,parameter3);
}
resp.setContentType("application/json");
resp.getWriter().write(restResponse);
}
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
Now the RestClass is,
public class RestClass {
public RestClass() {
}
public String getRestClassMethodResponse(#FormParam("parameter1") String parameter1, #FormParam("parameter2") String parameter2, #FormParam("parameter3") String parameter3){
//Now write your own logic and return the data to the Servlet class in //JSON format
return jsonResponse;
}
}

Forward request to another rest service using filter

I am writing a servlet filter to forward Jersy requests based on certain condition. But they does not seem to forwarding.
public class SampleFilter
extends GenericFilterBean
{
#Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException
{
String generateRedirectUrl=FormURL((HttpServletRequest)req);
RequestDispatcher dispatcher = req.getRequestDispatcher(generateRedirectUrl);
dispatcher.forward(req, resp);
}
private String FormURL(HttpServletRequest req)
{
// get the request check if it contains the customer
String reqUrl = req.getRequestURI();
log.info("Original Url is"+reqUrl);
if(reqUrl.contains("test"))
{
return "/api/abcd/" +"test";
}
return "Someurl";
}
}
I need to forward the url as below.
Original: http://localhost/api/test/1234/true
New URL:http://localhost/api/abcd/1234/true
Am I doing any thing wrong.
"Am I doing any thing wrong."
In general I think this will work - you can forward from a filter, but your logic is wrong.
Using your rule below:
Original: http://localhost/api/test/1234/true
New URL:http://localhost/api/abcd/1234/true
The code:
if(reqUrl.contains("test"))
{
return "/api/abcd/" +"test";
}
will produce /api/abcd/test which is not what you're after.
I would do something like the following:
private String formURL(HttpServletRequest req) {
// get the request check if it contains the customer
String reqUrl = req.getRequestURI();
if (log.isInfoEnabled() {
log.info("Original Url is"+reqUrl);
}
if(reqUrl.contains("test")) {
return reqUrl.replace("test", "abcd");
}
return "Someurl";
Also, the code is very brutal. This will also change the URL
http://test.site.com/abd/def
to
http://abcd.site.com/abd/def
which is probably not what you want. You'll probably need to either do more clever string manipulation or convert to something like the URI class which will allow you to target the path more accurately.
Hope this helps,
Will

Does Accept-Ranges work with RequestBuilder in GWT?

I am trying to use RequestBuilder in GWT to see if Accept-Ranges is supported.
Following is my client code:
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET,pathToServlet);
builder.setHeader("Range","bytes=0-10");
RequestCallback callback = new RequestCallback() {
#Override
public void onError(Request arg0, Throwable arg1) {
}
#Override
public void onResponseReceived(Request req, Response res) {
log.info("Text:"+res.getText());
log.info("Code:"+res.getStatusCode());
}
};
try {
builder.sendRequest(null, callback);
} catch (RequestException e) {}
And my servlet code is just a simple test code:
public class RangeTest extends HttpServlet{
static Logger log = Logger.getLogger(RangeTest.class);
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
String output = new String("This is a test string to be sent to the client");
response.setContentType("text/xml");
PrintWriter out = response.getWriter();
out.println(output);
}
}
In the output I get on the client, following is printed:
Text:This is a test string to be sent to the client
Code:200
What I expected was, since I gave the range header as 0-10 in the request, only the first 10 bytes will be sent to the client. But here the entire string is getting sent. What am I doing wrong here? Is there anything I have missed?
I feel my comment is more readable for other as answer (and effectively it is one):
You are not evaluating the range-header in your servlet-method. And the super class HttpServlet does not evaluate it either (but DefaultServlet from Tomcat).
The servlet specification has left most of the implementation work to providers like Apache. This explains why API classes like HttpServlet does not do the work of interpreting special http headers, but provider classes like the mentioned Tomcat-DefaultServlet. The main purpose of a specification is mainly to enable different implementations not to force people to only one.

java.lang.IllegalStateException: getReader() has already been called for this request

I want to add logging to my Servlet, so I've created Filter which should display request and go to the Servlet. But unfortunately I've encoutered exception:
java.lang.IllegalStateException: getReader() has already been called for this request
at org.apache.catalina.connector.Request.getInputStream(Request.java:948)
at org.apache.catalina.connector.RequestFacade.getInputStream(RequestFacade.java:338)
at com.noelios.restlet.ext.servlet.ServletCall.getRequestEntityStream(ServletCall.java:190)
So to fix this problem I've found solution with Wrapper, but it doesn't work. What else can I use/change in code? Any ideas?
[MyHttpServletRequestWrapper]
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper
{
public MyHttpServletRequestWrapper(HttpServletRequest request)
{
super(request);
}
private String getBodyAsString()
{
StringBuffer buff = new StringBuffer();
buff.append(" BODY_DATA START [ ");
char[] charArr = new char[getContentLength()];
try
{
BufferedReader reader = new BufferedReader(getReader());
reader.read(charArr, 0, charArr.length);
reader.close();
}
catch (IOException e)
{
e.printStackTrace();
}
buff.append(charArr);
buff.append(" ] BODY_DATA END ");
return buff.toString();
}
public String toString()
{
return getBodyAsString();
}
}
[MyFilter]
public class MyFilterimplements Filter
{
#Override
public void init(FilterConfig filterConfig) throws ServletException
{
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
final HttpServletRequestWrapper requestWrapper = new MyHttpServletRequestWrapper(httpServletRequest);
final String requestBody = requestWrapper.toString();
chain.doFilter(request, response);
}
}
Looks like the restlet framework has called getRequestEntityStream() on the Request object which in turn calls getInputStream(), so calling getReader() on the request throws IllegalStateException. The Servlet API documentation for getReader() and getInputStream() says:
public java.io.BufferedReader getReader()
...
...
Throws:
java.lang.IllegalStateException - if getInputStream() method has been called on this request
public ServletInputStream getInputStream()
...
...
Throws:
java.lang.IllegalStateException - if the getReader() method has already been called for this request
From the documentation it seems that we cannot call both getReader() and getInputStream() on the Request object. I suggest you use getInputStream() rather than getReader() in your wrapper.
Use ContentCachingRequestWrapper class. Wrap HttpServletRequest in thi will resolve issue
Sample : if you want to convert your "HttpServletRequest servletRequest" you can do some thing like
import org.springframework.web.util.ContentCachingRequestWrapper;
ContentCachingRequestWrapper request = new ContentCachingRequestWrapper(servletRequest);
Hope it helps!!!
As far as I can tell servlets are fundamentally broken in this regard. You can try and work around this problem as outlined here but that causes other mysterious problems when other things try and work with it.
Effectively he suggests cloning the request, reading the body and then in the the cloned class overriding the getReader and getInputStream methods to return the stuff already retrieved.
The code I ended up with was this:
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
//this class stops reading the request payload twice causing an exception
public class WrappedRequest extends HttpServletRequestWrapper
{
private String _body;
private HttpServletRequest _request;
public WrappedRequest(HttpServletRequest request) throws IOException
{
super(request);
_request = request;
_body = "";
try (BufferedReader bufferedReader = request.getReader())
{
String line;
while ((line = bufferedReader.readLine()) != null)
_body += line;
}
}
#Override
public ServletInputStream getInputStream() throws IOException
{
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(_body.getBytes());
return new ServletInputStream()
{
public int read() throws IOException
{
return byteArrayInputStream.read();
}
};
}
#Override
public BufferedReader getReader() throws IOException
{
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}
Anyway this appeared to be working fine until we realised that uploading a file from the browser wasn't working. I bisected through the changes and discovered this was the culprit.
Some people in the comments in that article say you need to override methods to do with parameters but don't explain how to do this.
As a result I checked to see if there was any difference in the two requests. However after cloning the request it had identical sets of parameters (both original request + cloned had none) aswell as an identical set of headers.
However in some manner the request was being effected and screwing up the understanding of the request further down the line - in my case causing a bizaare error in a library (extdirectspring) where something was trying to read the contents as Json. Taking out the code that read the body in the filter made it work again.
My calling code looked like this:
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException
{
HttpServletRequest properRequest = ((HttpServletRequest)request);
String pathInfo = properRequest.getPathInfo();
String target = "";
if(pathInfo == null)
pathInfo = "";
if(pathInfo.equals("/router"))
{
//note this is because servlet requests hate you!
//if you read their contents more than once then they throw an exception so we need to do some madness
//to make this not the case
WrappedRequest wrappedRequest = new WrappedRequest(properRequest);
target = ParseExtDirectTargetFrom(wrappedRequest);
request = wrappedRequest;
}
boolean callingSpecialResetMethod = pathInfo.equals("/resetErrorState") || target.equals("resetErrorState");
if(_errorHandler.IsRejectingRequests() && !callingSpecialResetMethod)
return;
try {
filterChain.doFilter(request, response);
}
catch (Exception exception) {
((HttpServletResponse) response).sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "ERROR");
_errorHandler.NotifyOf(exception);
}
}
I've ommitted the contents of ParseExtDirectTargetFrom but it calls getReader().
In my case the filter was working for all other requests but the strange behaviour in this case made me realise something wasn't quite right and what I was trying to do (implement sensible exception handling behaviour for tests) wasn't worth potentially breaking random future requests (as I couldn't figure out what had caused the request to become broken).
Also it's worth noting that the broken code is unavoidable - I assumed it might be something from spring but ServletRequest goes all the way up - thats all you get even if you were making a servlet from scratch by subclassing HttpServlet
My recommendation would be this - don't read the request body in a filter. You'll be opening up a can of worms that will cause strange problems later on.
The main problem is that you can't read the input both as binary stream and character stream, not even if the one is called in a filter and the other in the servlet.
Well, maybe this is something quite obvious, but I want to share with you this code that work OK for me. In a Spring boot project with JWT, for request of client, was necesary save all requests with their responses in a database table, and the same time authorize the access to consume the resources. Off Course i use getReader() for get request body, but i was obtain java.lang.IllegalStateException...
#Slf4j
#Component
#RequiredArgsConstructor
public class CustomAuthorizationFilter extends OncePerRequestFilter {
private final AuthorizationService authorizationService;
private String requestBody;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
HttpRequestDto requestDto = new HttpRequestDto();
try {
if (RequestMethod.POST.name().equalsIgnoreCase(request.getMethod()) && requestBody != null) { //This line and validation is useful for me [requestBody != null]
requestBody = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
}
//Do all JWT control
requestDto.setRequestURI(request.getRequestURI());
requestDto.setMethod(request.getMethod());
requestDto.setBody(requestBody);
}catch (IOException ie) {
responseError(_3001, response, ie);
} finally {
try {
ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(response);
filterChain.doFilter(request, responseWrapper);
saveResponse(responseWrapper, requestDto);
} catch (ServletException | IOException se) {
responseError(_3002, response, se);
}
}
}
private void saveResponse(ContentCachingResponseWrapper responseWrapper, HttpRequestDto requestDto) {
try {
HttpResponseDto responseDto = new HttpResponseDto();
responseDto.setStatus(responseWrapper.getStatus());
byte[] responseArray = responseWrapper.getContentAsByteArray();
String responseBody = new String(responseArray, responseWrapper.getCharacterEncoding());
responseDto.setBody(responseBody);
responseWrapper.copyBodyToResponse();
authorizationService.seveInDatabase(requestDto, responseDto);
} catch (Exception e) {
log.error("Error ServletException | IOException in CustomAuthorizationFilter.saveResponse", e);
}
}
private void responseError(LogCode code, HttpServletResponse response, Exception e) {
try {
Map<String, Object> error = new HashMap<>();
error.put("log", LogUtil.getLog(code));
error.put("message", e.getMessage());
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper().writeValue(response.getOutputStream(), error);
e.printStackTrace();
} catch (IOException ie) {
log.error("Error IOException in HttpLoggingFilter.responseError:", ie);
}
}
public String getRequestBody() {
return requestBody;
}
public void setRequestBody(String requestBody) {
this.requestBody = requestBody;
}
}
So my solution was use getter and setter methods of de local attribute requestBody, for validate if this is or not null and does not call again getReader() method because save in memory when set value. This worked perfect for me. Regards.

Categories