Groovy/Grails posting XML over HTTP (using REST plugin) - http

I am trying HTTP Post an XML string to a WebMethods server using basic auth. I was trying to use the REST plugin which sits on top of HTTP Builder. I've tried a few things all resulting in a 0 length response. Using Firefox poster I have used the exact same XML and user auth and the WebMethods response is to echo back the request with some extra info, so it is something I am doing in the code below that is wrong. Hope someone has a pointer for doing a HTTP Post of XML.
string orderText = "<item>
<item>1</item>
<price>136.000000</price>
</item>"
def response = withHttp(uri: "https://someserver.net:4433") {
auth.basic 'user', 'pass'
// have tried body: XmlUtil.serialize(orderText)
def r = post(path: '/invoke/document', body: orderText, contentType: XML, requestContentType: XML)
{ resp, xml ->
log.info resp.status
log.info resp.data
resp.headers.each {
log.info "${it.name} : ${it.value}"
}
}
log.info r
return r
}
Logs say:
04-02-2011 14:19:39,894 DEBUG HTTPBuilder - Response code: 200; found handler: OrdersService$_closure1_closure2_closure3_closure4#36293b29
04-02-2011 14:19:39,895 INFO HTTPBuilder - Status: 200
04-02-2011 14:19:39,896 INFO HTTPBuilder - Data: null
04-02-2011 14:19:39,896 INFO HTTPBuilder - XML: null
04-02-2011 14:19:39,913 INFO HTTPBuilder - Content-Type : application/EDIINT; charset=UTF-8
04-02-2011 14:19:39,913 INFO HTTPBuilder - Content-Length : 0
Cheers,
Steve

Here is what I ended up with. It is quite standard use of common HTTP Client
For basic auth over SSL you can simply have your url like: https://user:pass#www.target.com/etc
Grails remember to copy the HTTPClient jar to the lib folder or in my case I installed the REST plugin which includes HTTPClient anyway.
There are good docs on the HTTPClient site: http://hc.apache.org/httpcomponents-client-ga/
import org.apache.http.HttpEntity
import org.apache.http.HttpResponse
import org.apache.http.client.HttpClient
import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.DefaultHttpClient
def sendHttps(String httpUrl, String data) {
HttpClient httpClient = new DefaultHttpClient()
HttpResponse response
try {
HttpPost httpPost = new HttpPost(httpUrl)
httpPost.setHeader("Content-Type", "text/xml")
HttpEntity reqEntity = new StringEntity(data, "UTF-8")
reqEntity.setContentType("text/xml")
reqEntity.setChunked(true)
httpPost.setEntity(reqEntity)
log.info "executing request " + httpPost.getRequestLine()
response = httpClient.execute(httpPost)
HttpEntity resEntity = response.getEntity()
log.info response.getStatusLine()
if (resEntity != null) {
log.with {
info "Response content length: " + resEntity.getContentLength()
if (isDebugEnabled()) {
debug "Response Chunked?: " + resEntity.isChunked()
debug "Response Encoding: " + resEntity.contentEncoding
debug "Response Content: " + resEntity.content.text
}
}
}
// EntityUtils.consume(resEntity);
}
finally {
// When HttpClient instance is no longer needed,
// shut down the connection manager to ensure
// immediate deallocation of all system resources
httpClient.getConnectionManager().shutdown()
}
return response.getStatusLine()
}

try it this way:
HTTPBuilder builder = new HTTPBuilder( url )
builder.request( Method.POST ) {
// set uriPath, e.g. /rest/resource
uri.path = uriPath
requestContentType = ContentType.XML
// set the xml body, e.g. <xml>...</xml>
body = bodyXML
// handle response
response.success = { HttpResponseDecorator resp, xml ->
xmlResult = xml
}
}

I guess there's no need to get it done that difficult, I use a simpler approach (by the way you don't need extra plugins). So consider the next piece of code, and of course I'm ommiting the authentication part
class YourController{
static allowedMethods = [operation:['POST','GET']]
def operation(){
def xmlRequest = request.reader.text
println xmlRequest
//TODO: XML postprocessing, here you might use XmlParser.ParseText(xmlRequest)
}
}
I know this might be out of context because you are asking for the REST plugin, yet I wanted to share this since there's another alternative.
I'm using grails 2.3.2 and Firefox RESTClient to test the webservice.

Related

Apache Http EntityUtils.consume() vs EntityUtils.toString()?

I have written a HTTP client, where I am reading the data response from a REST web service. My confusion arises after reading multiple blogs on EntityUtils.consume() and EntiryUtils.toString(). I wanted to know the following:
If EntityUtils.toString(..) ONLY is sufficient as it also closes the stream after reading char bytes. Or I should also do EntityUtils.consume(..) as a good practice.
If both toString() and consume() operation can be used. If yes, then what should be there order.
If I EntityUtils.toString() closes the stream; then why the next call in EntityUtils.consume(..) operations which is entity.isStreaming() still returns true?
Could anyone guide me here to use these operations in a standard way. I am using HTTP version 4+.
I have to use these configurations in multithreaded(web-app) environment.
Thanks
I looked at the recommended example from the apache httpclient commons website.
In the example, they used EntityUtils.toString(..) without needing to use EntityUtils.consume(..) before or after.
They mention that calling httpclient.close() ensures all resources are closed.
source: https://hc.apache.org/httpcomponents-client-ga/httpclient/examples/org/apache/http/examples/client/ClientWithResponseHandler.java
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
HttpGet httpget = new HttpGet("http://httpbin.org/");
System.out.println("Executing request " + httpget.getRequestLine());
// Create a custom response handler
ResponseHandler<String> responseHandler = new ResponseHandler<String>() {
#Override
public String handleResponse(
final HttpResponse response) throws ClientProtocolException, IOException {
int status = response.getStatusLine().getStatusCode();
if (status >= 200 && status < 300) {
HttpEntity entity = response.getEntity();
return entity != null ? EntityUtils.toString(entity) : null;
} else {
throw new ClientProtocolException("Unexpected response status: " + status);
}
}
};
String responseBody = httpclient.execute(httpget, responseHandler);
System.out.println("----------------------------------------");
System.out.println(responseBody);
} finally {
httpclient.close();
}
This is what is quoted for the above example:
This example demonstrates how to process HTTP responses using a response handler. This is the recommended way of executing HTTP requests and processing HTTP responses. This approach enables the caller to concentrate on the process of digesting HTTP responses and to delegate the task of system resource deallocation to HttpClient. The use of an HTTP response handler guarantees that the underlying HTTP connection will be released back to the connection manager automatically in all cases.

how to update the incoming request in pact jvm requestFilter?

I have a spring boot API in java which is using pact-jvm for pact verification.
We have a new client who wants to use the same API using a new path, which the gateway will take care of, but this causes issue for pacts, I want to intercept the request and modify the path of the request for new pacts to point to old path.
I was trying to refer some material online and found this :
https://medium.com/dazn-tech/pact-contract-testing-dealing-with-authentication-on-the-provider-51fd46fdaa78
The below code prints the updated value of the request, but the pact still fails with 404 error as if it is still using new path
requestFilter = { req ->
println "incoming request : $req"
if ("$req".contains('/new-context') ) {
req = "$req".replace('/new-context', '/old-context')
println "updated request : $req"
}
}
The problem in the above code was I was treating req as string and doing manipulations, but it is an HttpRequest object and the below code solved the issue for me:
requestFilter = { req ->
def uriText = req.getURI()
println "incoming request uri : $uriText"
if ("$uriText".contains('/new-context') ) {
def uriTextNew = "$uriText".replace('/new-context', '/old-context')
println "updated request uri : $uriTextNew"
URI newURI = new URI(uriTextNew)
req.setURI(newURI)
}
}

Angular / Asp.Net Web Api request binary data

I have an Angular 4 application which consumes an Asp.Net Web Api, and I want the Api to return a binary file. The Api route seems to be working correctly - I tested it using a rest console and the response is as expected. However, when trying to use the same route in the Angular app, the request sends but returns an error. I can see with the C# debugger that the request is executing completely and doesn't fail on the server. Here's the error in the JS console:
This error occurs on all browsers tested (Chrome, Firefox, IE, Edge, Safari).
Here's the server side code:
[Route("api/getfile")]
public IHttpActionResult GetFile()
{
byte[] file = <file generator code>;
System.Net.Http.HttpResponseMessage responseMessage = new System.Net.Http.HttpResponseMessage
{
Content = new System.Net.Http.StreamContent(new System.IO.MemoryStream(file))
};
responseMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
responseMessage.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
responseMessage.Content.Headers.ContentDisposition.FileName = "file.pdf";
return this.ResponseMessage(responseMessage);
}
And here's the Angular code:
let headers = new Headers({
"Accept": "application/octet-stream",
"X-Requested-With": "XMLHttpRequest",
"Authorization": `Bearer ${token}`
});
let opts = new RequestOptions({headers = headers});
opts.responseType = ResponseContentType.Blob;
// Uses old #angular/http, not HttpClient
this.http
.get(`${apiUrl}/getfile`, opts)
.map(res => res.blob())
.catch(err => handleError(err));
EDIT: I tried using a plain XMLHttpRequest instead of Angular's Http service and it works. What do I need to do to get this to work with Angular?
EDIT 2: It works if I fetch an actual file on the file system that's accessible using the same host that the Angular app is running on. The Angular app is on localhost:8080, while the api is on a different port. If I expose a file on localhost:8080 (e.g., in the build folder) than I can fetch that file. This makes me wonder if it's a security issue, or maybe has to do with the headers or the way Web Api returns the binary data.
On your Api that will return your PDF
FileContentResult result;
if(!string.IsNullOrEmpty(fileName))
{
string absoluteFileName = Path.Combine(pathToFile, fileName);
byte[] fileContents = System.IO.File.ReadAllBytes(absoluteFileName);
result = new FileContentResult(fileContents, "application/pdf");
}
And then on Angular:
downloadFile(api: string) {
window.open(this.endPoint + api);
}
Try the old Way:
FileInfo fileInfo = New FileInfo(filePath)
Response.Clear()
Response.ClearHeaders()
Response.ClearContent()
Response.AddHeader("Content-Disposition", "attachment;filename=" + fileInfo.Name)
Response.AddHeader("Content-Type", "application/pdf")
Response.ContentType = "application/pdf"
Response.AddHeader("Content-Length", fileInfo.Length.ToString())
Response.TransmitFile(fileInfo.FullName)
Response.Flush()
Response.End()
This is for an image that I was keeping in a blob column in a db, but process should be similar. I ended up doing something like this back in Angular 4 (although this was 4.3+ which means HttpClient, not Http) to handle downloading files on clicking a button:
public downloadImage(id: number, imageName: string, imageType: string) {
this.http.get(urlToApiHere, { responseType: 'blob' }).subscribe((image: Blob) => {
if (isPlatformBrowser(this.platformId)) {
let a = window.document.createElement("a");
document.body.appendChild(a);
let blobUrl = window.URL.createObjectURL(image);
a.href = blobUrl;
a.download = imageName;
a.click();
window.URL.revokeObjectURL(blobUrl);
document.body.removeChild(a);
}
})
}
This API is .Net Core, but should be similar in .Net MVC, I believe:
[HttpGet]
public FileResult DisplayLineItemImage(int lineItemID)
{
using (var db = new Context())
{
var image = //retrieve blob binary, type, and filename here
if (image.Image == null)
{
//throw error
}
return File(image.Image, image.Type, image.Name);
}
}
The second answer by Crying Freeman, using Response directly, does work, but it bypasses Owin's processing and would mean having to manually implement things like CORS or anything else normally handled using CORS.
I found another solution, to use a custom formatter to allow returning a byte array from the controller method. This is also nicer because I don't need to set any headers manually, not even Content-Type.

Apache Camel - from jms to http

I have a spring-boot project using Apache Camel.
I want to read a message from an activemq queue containing a file and send it to a web server.
I am trying to find the proper way to do this.
I believe I can make something like:
from("activemq:queue").bean(MyBean.class, "process")
And manually build a http request but I can't help thinking there is probably a better way to do it. Like:
from("activemq:queue").bean(MyBean.class, "process")
.setHeader(Exchange.HTTP_METHOD,constant("POST"))
.to("http://localhost:8080/test");
But I don't know how to manipulate the "exchange" to have a valid http Message.
MyBean receives an Exchange object containing a JmsMessage. I see that there is also a HTTPMessage but I don't think I should build that manually. (It requires HTTPRequest and Response objects I am not sure how to get.)
Can someone shed some light on this problem?
Update
I am going for the bean solution.
from("activemq:queue").bean(MyBean.class, "sendMultipart");
public void sendMultipart(Exchange exchange) {
ByteArrayInputStream in = new ByteArrayInputStream((byte[]) exchange.getIn().getBody());
InputStreamBody contentBody = new InputStreamBody(in, ContentType.create("application/octet-stream"), "filename");
HttpEntity entity = MultipartEntityBuilder
.create()
.addPart("file", contentBody)
.build();
HttpPost httpPost = new HttpPost("http://localhost:8080/upload/");
httpPost.setEntity(entity);
CloseableHttpClient httpClient = HttpClients.createDefault();
try {
CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
System.out.println(httpResponse);
} catch (IOException e) {
e.printStackTrace();
}
}
Updated post
I found this http://hilton.org.uk/blog/camel-multipart-form-data. It allows you to leverage the camel http component.
"jms:queue/SomeQ" ==> {
process(toMultipart)
setHeader(Exchange.CONTENT_TYPE, "multipart/form-data")
process((e: Exchange) => e.getIn.setHeader(Exchange.HTTP_URI,"http://localhost:8111/foo"))
to ("http:DUMMY")
}
def toMultipart(exchange: Exchange): Unit = {
val data = exchange.in[java.io.File]
val entity = MultipartEntityBuilder.create()
entity.addBinaryBody("file", data)
entity.addTextBody("name", "sample-data")
// Set multipart entity as the outgoing message’s body…
exchange.in = entity.build
}
Side note: this would really be a nice use-case to try-out reactive streams.
Original post
I am still having some problems understanding your actual problem. Perhaps some code might help:
I am now assuming you are receiving bytes in some character encoding and want to sent it onward to a dynamically established http-endpoint.
Is the following something you are looking for (code is in camel's scala-dsl)
"jms:queue/SomeQ" ==> {
convertBodyTo(classOf[String],"UTF-32" )
process((e: Exchange) => e.in = e.in[String].toUpperCase + "!")
process((e: Exchange) => e.getIn.setHeader(Exchange.HTTP_URI,"http://localhost:8111/foo"))
to ("http:DUMMY")
}
It will be send as an HTTP POST as the body is not null.
I receive it all well on another endpoint i created to ensure the code above is correct:
"jetty:http://localhost:8111/foo" ==> {
log("received on http 8111 endpoint ${body}")
}

Is there a way to allow Google App Engine to send body or payload with a DELETE request?

I'm attempting to interface with an API that requires XML data to be contained in the body of an HTTP DELETE request. I'm using urlfetch in AppEngine and the payload is simply ignored for DELETE requests.
After reading this article: Is an entity body allowed for an HTTP DELETE request?, I realize that the standard probably doesn't allow body content on DELETE requests and that's why urlfetch is stripping the body.
So my question is: is there some sort of work-around to append body content in app engine when urlfetch ignores the payload?
Per the docs,
The URL fetch service supports five
HTTP methods: GET, POST, HEAD, PUT and
DELETE. The request can include HTTP
headers, and body content for a POST
or PUT request.
Given that the GAE Python runtime is heavily sandboxed, it's extremely unlikely that you'll be able to get around this restriction. I consider that to be a bug, and you should probably file a bug report here.
You can make DELETE request with body via sockets, sample Java code that checks HTTPRequest and does different request for DELETE with body:
public static HTTPResponse execute(HTTPRequest request) throws ExecutionException, InterruptedException {
if (request == null) {
throw new IllegalArgumentException("Missing request!");
}
if (request.getMethod() == HTTPMethod.DELETE && request.getPayload() != null && request.getPayload().length > 0) {
URL obj = request.getURL();
SSLSocketFactory socketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
try {
HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory);
con.setRequestMethod("DELETE");
for (HTTPHeader httpHeader : request.getHeaders()) {
con.setRequestProperty(httpHeader.getName(), httpHeader.getValue());
}
con.setDoOutput(true);
con.setDoInput(true);
OutputStream out = con.getOutputStream();
out.write(request.getPayload());
out.flush();
out.close();
List<HTTPHeader> responseHeaders = new ArrayList<>();
for (Map.Entry<String, List<String>> stringListEntry : con.getHeaderFields().entrySet()) {
for (String value : stringListEntry.getValue()) {
responseHeaders.add(new HTTPHeader(stringListEntry.getKey(), value));
}
}
return new HTTPResponse(con.getResponseCode(), StreamUtils.getBytes(con.getInputStream()), con.getURL(), responseHeaders);
} catch (IOException e) {
log.severe(e.getMessage());
}
} else {
Future<HTTPResponse> future = URLFetchServiceFactory.getURLFetchService().fetchAsync(request);
return future.get();
}
return null;
}
You can get around this using the App Engine Socket API, here is how that looks in Go:
client := http.Client{
Transport: &http.Transport{
Dial: func(network, addr string) (net.Conn, error) {
return socket.Dial(c, network, addr)
},
},
}

Resources