I'm trying to read custom error message I sent along an HTTP response for a web API project using Java.
Currently, I have this piece of Java code to read Header Response,
import java.net.HttpURLConnection;
import java.net.URL;
public class URLReader {
public static void main(String[] args) throws Exception {
URL oracle = new URL(URL);
HttpURLConnection connection =(HttpURLConnection)oracle.openConnection();
connection.setRequestMethod("GET");
connection.connect();
System.out.println(connection.getHeaderField(0));
}
}
An output of HTTP response header looks like this (Fiddler):
How can I get My Error Message text using Java?
#Abzal Kalimbetov is wrong about getErrorStream(), which will return an InputStream when an exception is raised, indicating response code >= 400 is received from the server.
Recap:
InputStream inputStream = null;
try {
// normal operation
inputStream = connection.getInputStream();
}
catch(IOException exception)
{
inputStream = connection.getErrorStream();
//#TODO you can now extract your custom error message from inputStream.
}
If the request status is greater than or equal 400, you use getErrorStream() method
if(connection.getResponseCode()>=400){
String myErrorMessage = connection.getErrorStream();
}
You can set a header in your server application which you then can read out very simple like this: ( Servlet usage is needed for this. )
private void test(ServletRequest request, ServletResponse response) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.addHeader( "myErrorMessage", "this is the message" );
String message = httpServletRequest.getHeader( "myErrorMessage" );
}
if you don't have these resources available it is possible to get the message like this:
//get all headers
Map<String, List<String>> map = connection.getHeaderFields();
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
System.out.println("Key : " + entry.getKey() +
" ,Value : " + entry.getValue());
}
//get header by 'key'
String server = connection.getHeaderField("Server");
Related
I have a simple HTML form to send a request to a REST API. It works well, when I submit, it sends the form data to API and displays the response in the browser.
<form name="theForm" action="localhost:8080/App/rest/consumeForm" method="post">
<input name="userName" value="Bob Smith" /><br/>
<input type="submit" value="submit"/>
</form>
Browser shows:
{"address": "12 First St.", "city": "Toronto"}
I would like to capture the response. Any ideas? (no ajax or javascript, just plain old Servlet or JSP please)
PART 2:
I now POST my form to a servlet I created, which handles the request and response from the REST API. It works nicely, but it needs the form data URLEncoded. Anyone know if there is a way to convert form data to such a string, or even convert form data to JSON directly?
String charset = java.nio.charset.StandardCharsets.UTF_8.name();
String userName = "Bob Smith";
String country = "Canada";
String queryString = String.format("userName=%s&country=%s"
,URLEncoder.encode(userName, charset)
,URLEncoder.encode(country, charset)
);
Can I build the above queryString dynamically?
//// send request
URLConnection connection = new URL("localhost:8080/App/rest/consumeForm").openConnection();
connection.setDoOutput(true); // Triggers POST.
connection.setRequestProperty("Accept-Charset", charset);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + charset);
try (OutputStream output = connection.getOutputStream()) {
output.write(queryString.getBytes(charset));
}
//// get response
BufferedReader apiResponse = new BufferedReader(new InputStreamReader((connection.getInputStream())));
String output;
System.out.println("\n\n\nrecieved....");
while ((output = apiResponse.readLine()) != null) {
System.out.println(output);
}
I would like to capture the response. Any ideas?
Install a servlet Filter that handles this. When it receives a request for the REST API endpoint, it can feed an HttpServletResponse to the next element in the chain that is equipped with any tooling you want. You would probably find HttpServletResponseWrapper to be a useful base class for your custom-tooled response class.
The Filter implementation might be along these lines:
public class ResponseCapturingFilter implements Filter {
private static final String SERVLET_TO_FILTER = "...";
#Override
public void init(ServletConfig config) {
// ...
}
#Override
public void destroy() {
// ...
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (((HttpServletRequest) request).getServletPath().equals(SERVLET_TO_FILTER)) {
response = new MyCapturingResponseWrapper(response);
}
chain.doFilter(request, response);
}
}
To capture the response text, you would want your wrapper to override at least getOutputStream() and getWriter() appropriately.
It turns out that submitting to a servlet using POST and communicating with the REST API using the servlet works for me. There may be better ways, but this seems relatively clean for junior developers to follow and maintain. (I'm still open to other options).
I build a queryString with the form data (req is the HttpServletRequest)
String theQueryString="domainId=1";
for(Entry<String, String[]> qsParm:req.getParameterMap().entrySet()) {
theQueryString+="&"+qsParm.getKey()+"="+URLEncoder.encode(req.getParameter(qsParm.getKey()), charset);
}
// set up connection to use as API interaction
URLConnection connection = new URL("localhost:8080/App/rest/consumeForm").openConnection();
connection.setDoOutput(true); // Triggers POST apparently
connection.setRequestProperty("Accept-Charset", java.nio.charset.StandardCharsets.UTF_8.name());
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + java.nio.charset.StandardCharsets.UTF_8.name());
// send request to API via connection OutputStream
try (OutputStream output = connection.getOutputStream()) {
output.write(theQueryString.getBytes(java.nio.charset.StandardCharsets.UTF_8.name())); // this sends the request to the API url
}
// get response from connection InputStream and read as JSON
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonMap = mapper.readTree(connection.getInputStream());
// now the response can be worked with in at least two ways that I have tried
String user1 = jsonMap.get("userName").asText();
String user2 = jsonMap.at("user").getValueAsText();
I am using Zuul post filter to intercept the response. My requirement is to add one new field to response json. I'm able to intercept the response and edit it. But, unable to set the updated response to RequestContext.How it is possible to read a response body ,edit and update it back to RequestContext while using Zuul as a proxy in post filter?
Please find the below code i am using.
private void updateResponseBody(RequestContext ctx) throws IOException, JSONException {
final InputStream responseDataStream = ctx.getResponseDataStream();
String responseData = CharStreams.toString(new InputStreamReader(responseDataStream, "UTF-8"));
JSONObject jsonObj = new JSONObject(responseData);
JSONArray groupsArray = jsonObj.getJSONArray("list");
for (int i = 0; i < groupsArray.length(); i++) {
JSONObject groupId = groupsArray.getJSONObject(i);
groupId.accumulate("new_json_field_name", "new_json_field_value");
}
String updatedResponse = jsonObj.toString();
// ctx.setResponseBody(body); // also not working
ctx.setResponseDataStream(org.apache.commons.io.IOUtils.toInputStream(updatedResponse, "UTF-8"));
}
Error I am getting is :
Error while sending response to client: java.io.IOException: An existing connection was forcibly closed by the remote host.
Can anyone please help me on this.
I had the same error and got crazy modifying the code described in How to get response body in Zuul post filter? trying different possibilities. Finally I found the solution in this post by writing the answer in the OutputStream from servletResponse.getOutputStream() instead of ctx.setResponseDataStream():
HttpServletResponse servletResponse = ctx.getResponse();
...
String updatedResponse = jsonObj.toString();
try {
OutputStream outStream = servletResponse.getOutputStream();
outStream.write(updatedResponse.getBytes(), 0, updatedResponse.length());
outStream.flush();
outStream.close();
} catch (IOException e) {
log.warn("Error reading body", e);
}
I had a similar task and tried to do it by writing to the OutputStream. This worked, but had a strange side effect that it made the HttpHeaders in the response to be deleted or corrupted. This made the call produce CORS errors in production even though it ran fine locally through Postman.
I wrote the following method that I call from the run() method of my Post Zuul Filter to add a single node/value to the return Json.
private void addJsonNode(RequestContext requestContext,String name, String id) {
HttpServletResponse servletResponse = requestContext.getResponse();
try {
final InputStream responseDataStream = requestContext.getResponseDataStream();
String responseData = CharStreams.toString(new InputStreamReader(responseDataStream, "UTF-8"));
JSONObject jsonObject = new JSONObject(responseData);
jsonObject.put(name, id);
String updatedResponse = jsonObject.toString(4);
requestContext.setResponseBody(updatedResponse);
} catch (IOException e) {
log.warn("Error reading body", e);
} catch (JSONException e) {
log.warn("Error reading body", e);
}
}
I have implemented servlet which behaves not stable, sometimes it mixes header in content and writing same twice.
and sometimes it is returning file which contains response header mixed by content like this:
Server: Apache-Coyote/1.1
: W/"43-1353687036000"
DatCCoonntenntt--DDiissppoosittiioonn: : atatatacehnmte;n tf;i lfenlaemnea=m20=12201112211127325421_4W1_Wirnkgi_nSgc_Seern.xnlsx
sx
Content-Typ-eT: ype: applaipcatciaoti/on/toctestt-rstare
am
ConCtoententy-pTeype: appalicatcion/oon/octet-setarm
m
CCoonntent-Lnegtht h: 4199
Date: te: FriF,r i2,3 2No vNo2v0 120162: 215:25 :G4M2T
....
File content bytes ...
And again same header and content
UPDATE
*This situation happens on Tomcat7*
I have tested also on Tomcat6 and Jetty, in both cases there is no injection of HTTP-Header to response content but HTTP-Header is wrong and returns wrong file name, the file content is correct file. I have noticed that wrong return from servlet happens when
returns transfer-encoding is chunked.
When I am removing header stuff, and second part of bytes, it is valid file.
Is it possible that is synchronization issue ?
UPDATE
Here is full source of servlet :
public class ExcelDownloadServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
private static final Logger LOG = Logger
.getLogger (ExcelDownloadServlet.class);
#Override
protected void doGet (HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException
{
try
{
TransactionId transactionId = getTransactionId (request);
String fileName =
request.getParameter (GlobalConstants.EXCEL_FILE);
ExcelDownloadType downloadType =
ExcelDownloadType
.valueOf (request
.getParameter (GlobalConstants.EXCEL_DOWNLOAD_TYPE));
ActionContextFactory actionContextFactory =
ApplicationContext.getContext ()
.getActionContextFactory ();
//suppress warning. HttpServletRequest.getLocales does not support generics
#SuppressWarnings("unchecked")
ActionContext actionContext =
actionContextFactory.create (request.getSession ()
.getId (), Collections.<Locale> list (request
.getLocales ()));
GetExcelDataResponse dataResponse =
new GetExcelData (transactionId, fileName, downloadType)
.execute (actionContext);
writeToResponse (response, dataResponse.getFileName (),
dataResponse.getData ());
}
catch (InvalidSessionException e)
{
LOG.error ("Invalid session in Excel download", e);
throw new ServletException (e);
}
catch (ActionException e)
{
LOG.error ("Could not download into excel.", e);
throw new ServletException (e);
}
}
protected TransactionId getTransactionId (HttpServletRequest request)
{
return RequestParameterDeserializer.<TransactionId> deserialize (
request, GlobalConstants.TRANSACTION_ID);
}
protected void writeToResponse (HttpServletResponse response,
String rawFileName, byte[] data) throws IOException
{
ServletOutputStream sout = null;
try
{
response.setContentType ("application/octet-stream");
response.setContentLength (data.length);
// removing blanks from the file name, since FF cuts file names
// otherwise.
String fileNameWithTime = rawFileName.replaceAll (" ", "_");
response.setHeader ("Content-Disposition", "attachment; filename="
+ fileNameWithTime);
sout = response.getOutputStream ();
sout.write (data, 0, data.length);
}
finally
{
if (sout != null)
{
sout.close ();
}
}
}
UPDATE
*The call comes from GWT application when is generating the URL of servlet with required parameters and sets in IFrame, then servlet calls and file is downloading. Are there any suggestions ?*
I had a similar issue a long time ago.
It turned out that closing the ServletOutputStream triggered an unexpected behaviour on the request flow.
Servlets are not supposed to close the container provided OutputStream.
Another issue could be manually setting the content length, it is responsibility of the container producing the correct value.
To summarize, try removing out.close() and response.setContentLength()
I need to log the full http request and response in a JAX-WS WebService call. For the request I need the request headers and the body and for the response, response headers and body.
After some researching, I've found that I can get this information with the property:
-Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true
and show the information that I need but it dumps it to the console and I need to store it in the database with an internal request id.
I've tried to implement a handler:
public class LoggingHandler implements SOAPHandler<SOAPMessageContext> {
#Override
public boolean handleMessage(SOAPMessageContext context) {
Boolean outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outbound) {
System.out.println("SOAP outbound!!!!!");
Map<String, List<String>> responseHeaders = (Map<String, List<String>>) context
.get(SOAPMessageContext.HTTP_RESPONSE_HEADERS);
try {
String headers = getHeaders(responseHeaders);
System.out.println(headers);
String body = getBody(context.getMessage());
System.out.println(body);
} catch (Exception ex) {
// TODO: What do I have to do in this case?
}
} else {
System.out.println("SOAP inbound!!!!!");
Map<String, List<String>> requestHeaders = (Map<String, List<String>>) context
.get(SOAPMessageContext.HTTP_REQUEST_HEADERS);
try {
String headers = getHeaders(requestHeaders);
System.out.println(headers);
String body = getBody(context.getMessage());
System.out.println(body);
} catch (Exception ex) {
// TODO: What do I have to do in this case?
}
}
return true;
}
private String getBody(SOAPMessage message) throws SOAPException, IOException {
OutputStream stream = new ByteArrayOutputStream();
message.writeTo(stream);
return stream.toString();
}
public String getFullHttpRequest(HttpServletRequest request) throws IOException {
InputStream in = request.getInputStream();
String encoding = request.getCharacterEncoding();
encoding = encoding == null ? "UTF-8" : encoding;
String body = IOUtils.toString(in, encoding);
return body;
}
private String getHeaders(Map<String, List<String>> headers) throws IOException {
StringBuffer result = new StringBuffer();
if (headers != null) {
for (Entry<String, List<String>> header : headers.entrySet()) {
if (header.getValue().isEmpty()) {
// I don't think this is legal, but let's just dump it,
// as the point of the dump is to uncover problems.
result.append(header.getValue());
} else {
for (String value : header.getValue()) {
result.append(header.getKey() + ": " + value);
}
}
result.append("\n");
}
}
return result.toString();
}
}
but in this case, I can get the http request headers and body but in the response, I only get the body, http response headers are always empty.
Any idea on how to archieve this? The objective is to be able to store the full http request and response in a database.
Thanks!!
You could also try
-Dcom.sun.xml.ws.transport.http.HttpAdapter.dump=true
I'm assuming you're providing your web service from within a Java EE application server of some sort (and not from a standalone client). You cannot have access to Java EE infrastructure like HttpServletRequest and HttpServletResponse outside of the context of a web/Java EE container.
You could try to get your hands on the actual servlet response object (within a web context) with
HttpServletResponse response = (HttpServletResponse) messageContext.get(SOAPMessageContext.SERVLET_RESPONSE); //messageContext is the SOAPMessageContext
List<String> responseHeaderNames = (List<String>)response.getHeaderNames();
for(String headerName : responseHeaderNames){
//Do whatever you want with it.
}
I seriously doubt that you'll be able to get your hands on the full response headers within a handler though. Your question really intrigued me and I've spent quite some time researching that part. In all the code samples I've seen, Not even the example on the metro site attempt to implement this functionality and I think the reason is simple. As at the point where a handler is invoked, the container may not have enough definitive information to stamp an http header on the outbound message. You might be able to add stuff but that's doubtful as well.
I would like to start a file download from a clickevent in my gwt web app. So I wrote a Servlet which writes the data to the output and should start the download. The data is received via http get.
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String data = request.getParameter("data");
String filename = request.getParameter("filename");
byte[] streamData = data.getBytes();
response.setContentType("application/force-download");
response.setHeader("Content-Disposition", "attachment; fileName="
+ filename + ".csv");
response.setContentLength(streamData.length);
ServletOutputStream out = response.getOutputStream();
out.write(streamData);
out.flush();
}
In the client I start the get method via requestBuilder.sendRequest():
RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, /download");
requestBuilder.sendRequest("filename=dues&data="+ theDataAsString, new RequestCallback() {
#Override
public void onResponseReceived(Request request,
Response response) {
// Anything to do here?
}
#Override
public void onError(Request request, Throwable exception) {
exception.printStackTrace();
SC.warn("Error while creating export file.");
}
});
Nothing happens. But why? Shouldn't the browser ask to begin a download?
Does it, in this case, matter if i use post or get?
I don't want to use somthing like
Window.open("/download?data=myData&filename=filename", "_blank", "");
Any ideas?
On the client side, use an Anchor instead of a request builder and invoke the servlet directly.