I am using grails.plugins.rest.client.RestResponse to GET a binary resource of content-type png from a rest endpoint.
import grails.plugins.rest.client.RestBuilder
import grails.plugins.rest.client.RestResponse
String url = "http://localhost:8080/rest-endpoint/image.png"
RestBuilder rest = new RestBuilder()
RestResponse restResponse = rest.get(url){
auth username, password
accept "image/png"
contentType "image/png"
}
byte[] png_image = restResponse.responseEntity.body
println "length " + png_image.length
I'm not sure why the length that is returned is ~500bytes less than expected and I have tried with a different image and a different url and the length returned is lower everytime. Any idea why?
I had the same problem and ended up using HTTPBuilder instead.
import groovyx.net.http.ContentType
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.Method
class RestUtil {
static byte[] getBytes(String url) {
new HTTPBuilder(url).request(Method.GET, ContentType.BINARY) {
requestContentType = ContentType.BINARY
response.success = { resp, binary ->
return binary.bytes
}
}
}
}
Hope that's helpful.
Related
I'm trying to create a service in the Android App that consumes a SOAP API. Sent values and returned values are XML.
Previously i used FormUrlEncoded + JSON in another API and worked, but with XML i'm struggling as the API seems that is not being called (HttpLoggingInterceptor don't show and also the Mockup service don't show any petition).
If i change to FormUrlEncoded my service i can see that the request is done (i checked it with HttpLoggingInterceptor, but if i remove the FormUrlEncoded seems like service is not called never.
My NetModule where is create the retrofir, parser, etc:
#Module
class NetModule {
#Provides
#Singleton
fun provideRetrofit(): Retrofit {
val client =
OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
.build()
val strategy = AnnotationStrategy()
val serializer = Persister(strategy)
return Retrofit.Builder()
.baseUrl(BuildConfig.API_URL)
.client(client)
.addConverterFactory(SimpleXmlConverterFactory.create(serializer))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}
#Provides
#Singleton
fun provideFilesService(retrofit: Retrofit): FilesService =
retrofit.create(FilesService::class.java)
}
My FilesService.kt where the interface is defined is:
import com.liderasoluciones.enviotest.data.model.FileSendResponse
import com.liderasoluciones.enviotest.data.model.FileSendEnvelope
import io.reactivex.Flowable
import retrofit2.http.*
interface FilesService {
#Headers(
"Content-Type: application/soap+xml",
"Accept-Charset: utf-8"
)
#POST("mockWSSMTSoap")
fun sendFile(#Body body: FileSendEnvelope): Flowable<FileSendResponse>
}
My model for the Body, Request and data is FileSendEnvelope.kt and is:
import org.simpleframework.xml.Element
import org.simpleframework.xml.Root
import org.simpleframework.xml.Namespace;
import org.simpleframework.xml.NamespaceList;
#Root(name = "GetInfoByState", strict = false)
#Namespace(reference = "http://www.webservicetest.net")
class FileSendData {
#Element(name = "FileName", required = false)
var name: String? = null
}
#Root(name = "soap12:Body", strict = false)
class FileSendBody {
#Element(name = "GetInfoByFile", required = false)
var fileSendData: FileSendData? = null
}
#Root(name = "soap12:Envelope")
#NamespaceList(
Namespace(prefix = "xsi", reference = "http://www.w3.org/2001/XMLSchema-instance"),
Namespace(prefix = "xsd", reference = "http://www.w3.org/2001/XMLSchema"),
Namespace(prefix = "soap12", reference = "http://www.w3.org/2003/05/soap-envelope")
)
class FileSendEnvelope {
#Element(name = "soap12:Body", required = false)
var body: FileSendBody? = null
}
From the RemoteDataSource class is where i call the api:
class RemoteFilesDataSource(private val filesService: FilesService,
private val genericResponseEntityMapper: GenericResponseEntityMapper):
FilesDataSource {
override fun sendFile(userToken: String): Flowable<GenericResponseEntity> {
var petitionEnvelope = FileSendEnvelope()
var petitionBody = FileSendBody()
var petitionData = FileSendData()
petitionData.name = "test.png"
petitionBody.fileSendData = petitionData
petitionEnvelope.body =
return filesService.sendFile(petitionEnvelope)
.map { it.result }
.map { genericResponseEntityMapper.transform(it) }
}
}
At this moment i'm not taking so much care about the XML sent or parse the response, i just "want to check" that the API is called.
I tried to follow this info:
https://github.com/asanchezyu/RetrofitSoapSample
http://geekcalledk.blogspot.com/2014/08/use-simple-xml-with-retrofit-for-making.html
Even are java examples and i'm using Kotlin but no luck.
Any help is appreciated.
Thanks in advance.
Have you tried to use text/xml for your Content-type in your header? (and try it without the Accept-Charset header as well)
#Headers(
"Content-type: text/xml"
)
Using Grails 3.0.9, and grabbing the freshest REST API with this snippet in gradle.build:
compile 'org.grails:grails-datastore-rest-client:4.0.7.RELEASE', {
['commons-codec', 'grails-async', 'grails-core',
'grails-plugin-converters', 'grails-web', 'groovy'].each {
exclude module: it
}
}
I am trying to make the following POST request:
def rest = new RestBuilder(headers:["X-LSS-Env":"devmo"], connectTimeout:10000, readTimeout:20000)
response = rest.post("http://..../..") {
accept "application/json"
contentType "application/json"
json jsonBuilder
}
Now, the POST receiver gets the json okay, give back a response okay, but this is the problem: it receives the headers as an empty map or as null!
So, what is the correct way of passing header data to the POST receiver? This is needed because the environment key X-LSS-Env could have different values, which instructs the receiver to do further routing based on it. Same with the GET request of course.
* UPDATE *
The consumer of my POST requests is actually a Java application, running on Apache Tomcat/8.0.26. The is how the service looks on the other side:
private javax.servlet.http.HttpServletRequest hsr;
#POST
#Path("/na")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response postSomething(Ggfp ggfp ){
try {
Enumeration<String> hnames = hsr.getHeaderNames();
int i = 0;
while (hnames.hasMoreElements()) {
String headerName = hnames.nextElement();
System.out.println(++i+ " headerName: " + headerName);
String val = hsr.getHeader(headerName);
System.out.println(" val: " + val);
}
String hval = hsr.getHeader("X-LSS-Env");
return Response.status(Status.OK).entity("X-LSS-Env is " + hval).build();
} catch (Exception e) {
}
}
Calling this service from Postman works, headers are identified. Calling it from the Grails app results into an empty map - like I am sending no headers!
The RestBuilder constructor never liked the way I used (or abused) it. Here is a clean way of achieving what I set out to do, with tryCatch logic if a timeout transpires.
def makePostWsr(serviceUrl, jsonBuilder) {
try {
def rest = new RestBuilder(connectTimeout:connectTimeout, readTimeout:readTimeout)
def response = rest.post("$wsUrl/$serviceUrl") {
header 'X-LSS-Env', 'devmo'
accept "application/json"
contentType "application/json"
json jsonBuilder
}
response
} catch (Exception e) {
println "== problem makePostWsr on $serviceUrl"
null
}
}
I'm trying to make GET and then PUT call on XML REST web service.
I do it this way:
#Grab('org.codehaus.groovy.modules.http-builder:http-builder:0.7')
import groovyx.net.http.HTTPBuilder
import static groovyx.net.http.ContentType.*
import static groovyx.net.http.Method.*
import groovy.xml.XmlUtil
def url = "http://localhost:81"
def pathPrefix = "/api/v1"
def http = new HTTPBuilder(url)
def profile = http.request(GET, XML) { req ->
uri.path = "$pathPrefix/profiles/55"
response.success = {resp, xml ->
xml
}
}
println XmlUtil.serialize(profile) // this is fine!
Now i'm going to change and save
profile.name = "New Name"
// this is not fine (i have 400 Bad Request)
// because it sends body not in XML
def savedProfile = http.request(PUT, XML) { req ->
uri.path = "$pathPrefix/profiles/55"
body = profile
response.success = {resp, xml ->
xml
}
}
println XmlUtil.serialize(savedProfile)
When i make PUT request HTTPBuilder do not send XML. It sends string, made of profile.toString().
It it not what i'm expecting.
How to send XmlSlurper object (that i obtained earlier) in PUT request?
Thank you.
I think i found the solution.
When i define body configuration value, i have to write
body = {
mkp.yield profile
}
My code is given below
WebResource webResource1 = cl.resource("https://api.box.com/2.0/files/{fileId}/content");
ClientResponse res1 = webResource1.header("Authorization", "Bearer"+p1.getAccess_token()).get(ClientResponse.class);
String jsonStr1 = res1.getEntity(String.class);
And my response is given below-
{Object-Id=[file_20317568941], Cache-control=[private], Date=[Wed, 24 Sep 2014 12:11:43 GMT], Content-Length=[27], X-Robots-Tag=[noindex, nofollow], Content-Disposition=[attachment;filename="upload.txt";filename*=UTF-8''upload.txt], Accept-Ranges=[bytes, bytes], Connection=[keep-alive], Content-Type=[text/plain; charset=UTF-8], Server=[nginx], X-Content-Type-Options=[nosniff]}
I am getting status code 200, OK; but to get the location attribute I need to have the status code 302 along with the location url (https://dl.boxcloud.com/*).
Without getting location: https://dl.boxcloud.com/* attribute in the response, how can I download file from box api?
last Saturday I got some time to look into your issue. The basic problem is that if you need to get the Location value you need to stop the automatic redirection. Following is the explanation & solutions of your problem:
Quoting Box API docs of Download a File:
If the file is available to be downloaded, the response will be a 302
Found to a URL at dl.boxcloud.com.
From Wikipedia article on HTTP 302:
The HTTP response status code 302 Found is a common way of performing
URL redirection.
An HTTP response with this status code will additionally provide a URL
in the Location header field. The user agent (e.g. a web browser) is
invited by a response with this code to make a second, otherwise
identical, request to the new URL specified in the Location field.
So to get the Location attribute in the response header you need to stop the automatic redirection. Otherwise as per the box doc you will get the raw data of the file instead of the download URL.
Following is a solution implemented using Commons HTTPClient:
private static void getFileDownloadUrl(String fileId, String accessToken) {
try {
String url = MessageFormat.format("https://api.box.com/2.0/files/{0}/content", fileId);
GetMethod getMethod = new GetMethod(url);
getMethod.setFollowRedirects(false);
Header header = new Header();
header.setName("Authorization");
header.setValue("Bearer " + accessToken);
getMethod.addRequestHeader(header);
HttpClient client = new HttpClient();
client.executeMethod(getMethod);
System.out.println("Status Code: " + getMethod.getStatusCode());
System.out.println("Location: " + getMethod.getResponseHeader("Location"));
} catch (Exception cause) {
cause.printStackTrace();
}
}
An alternate solution using java.net.HttpURLConnection:
private static void getFileDownloadUrl(String fileId, String accessToken) {
try {
String serviceURL = MessageFormat.format("https://api.box.com/2.0/files/{0}/content", fileId);
URL url = new URL(serviceURL);
HttpURLConnection connection = HttpURLConnection.class.cast(url.openConnection());
connection.setRequestProperty("Authorization", "Bearer " + accessToken);
connection.setRequestMethod("GET");
connection.setInstanceFollowRedirects(false);
connection.connect();
int statusCode = connection.getResponseCode();
System.out.println("Status Code: " + statusCode);
Map<String, List<String>> headerFields = connection.getHeaderFields();
List<String> locations = headerFields.get("Location");
if(locations != null && locations.size() > 0) {
System.out.println("Location: " + locations.get(0));
}
} catch (Exception cause) {
cause.printStackTrace();
}
}
Since Commons HTTPClient is outdated the following solution is based on the Apache HttpComponents:
private static void getFileDownloadUrl(String fileId, String accessToken) {
try {
String url = MessageFormat.format("https://api.box.com/2.0/files/{0}/content", fileId);
CloseableHttpClient client = HttpClientBuilder.create().disableRedirectHandling().build();
HttpGet httpGet = new HttpGet(url);
BasicHeader header = new BasicHeader("Authorization", "Bearer " + accessToken);
httpGet.setHeader(header);
CloseableHttpResponse response = client.execute(httpGet);
int statusCode = response.getStatusLine().getStatusCode();
System.out.println("Status Code: " + statusCode);
org.apache.http.Header[] headers = response.getHeaders(HttpHeaders.LOCATION);
if(header != null && headers.length > 0) {
System.out.println("Location: " + headers[0]);
}
} catch (Exception cause) {
cause.printStackTrace();
}
}
I've been using rest-client-builder plugin (http://grails.org/plugin/rest-client-builder) and faced with problem to send a file as inputStream object.
From plugin documentation:
Multipart requests are possible by setting properties of the request body to File, URL, byte[] or InputStream instances:
def resp = rest.post(url) {
contentType "multipart/form-data"
zip = new File(pluginPackage)
pom = new File(pomFile)
xml = new File(pluginXmlFile)
}
My code:
def post(String url, InputStream photo, String contentType, Cookie[] cookies = null) {
def rest = new RestBuilder()
def cookiesHeaderString = ""
if (cookies) {
cookiesHeaderString = WebUtils.buildCookiesHeader(cookies)
}
def resp = rest.post(url) {
header "Cookie", cookiesHeaderString
file = photo
contentType "multipart/form-data"
}
return resp?.responseEntity?.body
}
Could somebody suggest how can I send an InputStream object or what I'm doing wrong?
For File type we need to set "file" property on the RequestCustomizer. The below code worked for me.
File myFile = new File("myFile.txt")
def restResponse = rest.post(url) {
header headerName, headerValue
contentType "multipart/form-data"
setProperty "file", myFile
}
I know I am quite late for this answer but I was searching for the answer and nothing seemed to work. So, by trial and error I finally have found my answer working, so would like to post it here.
RestTemplate restTemplate=new RestTemplate()
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
def restBuilder=new RestBuilder(restTemplate)
File f = new File("C:/Users/USER/Documents/hello.txt")
MultiValueMap<String, File> form = new LinkedMultiValueMap<String, File>()
form.add("fileUpload", f)
return client.post(path) {
auth('ngtest1', 'ngtest1')
header :['contentType':"multipart/form-data"]
setProperty "fileUpload", f
body (form)
}
This worked for me. I have given the name as 'fileUpload' in my application. Hope this helps.