How to reproduce this cURL request using Spring RestTemplate? - spring

I am trying to achieve GET request calls containing a request body. Yeah, I know. So here is my problem.
I have a Spring MVC controller responding fine to the following pseudo-command:
curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET http://localhost:9098/a/{aID}/b/{bID}/c -d '{"header":{"foo":"foofoofoo","timestamp":"2015-06-23T03:45:43-04:00"}}'
I am trying to reproduce this call from another spring mvc app using RestTemplate.exchange, but keep getting a 400 bad request error on the client side app -- the server side application doesn't seem to log any error regarding the call.
The client side controller
#GET
#Produces(APPLICATION_JSON)
#Path("/a/{aID}/b/{bID}/c")
public String getC(
#PathParam(value = "aID") String aId,
#PathParam(value = "bID") String bId) {
ResponseEntity<String> result = null;
RestTemplate restTemplate = new RestTemplate();
StringBuilder url = new StringBuilder(serverApplicationUrlLocalhost9098);
url.append("/a/").append(aId);
url.append("/b/").append(bId);
url.append("/c");
String finalUrl = url.toString();
try {
org.springframework.http.MediaType mediaType = org.springframework.http.MediaType.APPLICATION_JSON;
List<org.springframework.http.MediaType> accepts = new ArrayList<org.springframework.http.MediaType>();
accepts.add(org.springframework.http.MediaType.APPLICATION_JSON);
HttpHeaders headers = new HttpHeaders();
headers.setAccept(accepts);
headers.setContentType(mediaType);
Foo foo = new Foo();
HttpEntity<Foo> request = new HttpEntity<Foo>(foo, headers);
result = restTemplate.exchange(finalUrl, HttpMethod.GET, request, String.class);
} catch (Exception e) {
logger.error("Error calling REST service: " + finalUrl, e);
}
return result.getBody();
}
For the purpose of testing, I have a private class Foo
private class Header {
private String foo = "foofoofoo";
private String timestamp = "2015-06-23T03:45:43-04:00";
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
}
private class Foo {
private Header header = new Header();
public Header getHeader() {
return header;
}
public void setHeader(Header header) {
this.header = header;
}
}

Related

Invoke lambda function handler java

I have a lambda function which has a handler which inturn has multiple routers. Each router corresponds to an API.
I have created a lambda client in java and need to call those APIs. To call these APIs, I need to invoke the handler and pass a payload to the client along with it. Can you guys help me with the syntax for invoking the handler and passing the payload.
If I understand your question correctly I first created a Lambda that looked like:
public class SampleHandler implements RequestStreamHandler {
private static final Logger logger = LogManager.getLogger(SampleHandler.class);
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
logger.info("handlingRequest");
LambdaLogger lambdaLogger = context.getLogger();
ObjectMapper objectMapper = new ObjectMapper();
String inputString = new BufferedReader(new InputStreamReader(inputStream)).lines().collect(Collectors.joining("\n"));
JsonNode jsonNode = objectMapper.readTree(inputString);
String route = jsonNode.get("route").asText();
RouterResult routerResult = new RouterResult();
switch( route ) {
case "requestTypeA":
RequestTypeA requestTypeA = objectMapper.readValue(inputString, RequestTypeA.class);
routerResult.setResult(handleRequestTypeA(requestTypeA));
break;
case "requestTypeB":
RequestTypeB requestTypeB = objectMapper.readValue(inputString, RequestTypeB.class);
routerResult.setResult(handleRequestTypeB(requestTypeB));
break;
default:
logger.error( "don't know how to handle route of type \"" + route + "\n" );
routerResult.setResult("error");
}
outputStream.write(objectMapper.writeValueAsString(routerResult).getBytes(StandardCharsets.UTF_8));
logger.info("done with run, remaining time in ms is " + context.getRemainingTimeInMillis() );
}
private String handleRequestTypeA(RequestTypeA requestTypeA) {
logger.info("handling requestTypeA, requestTypeA.requestA is " + requestTypeA.getRequestA() );
return "handled requestTypeA";
}
private String handleRequestTypeB(RequestTypeB requestTypeB) {
logger.info("handling requestTypeB, requestTypeB.requestB is " + requestTypeB.getRequestB() );
return "handled requestTypeB";
}
}
with RouterRequest.java:
public class RouterRequest {
protected String route;
public String getRoute() {
return route;
}
}
and RequestTypeA.java:
public class RequestTypeA extends RouterRequest {
private String requestA;
public RequestTypeA() {
route = "requestTypeA";
}
public String getRequestA() {
return requestA;
}
public void setRequestA(String requestA) {
this.requestA = requestA;
}
}
and RequestTypeB.java
public class RequestTypeB extends RouterRequest {
private String requestB;
public RequestTypeB() {
route = "requestTypeB";
}
public String getRequestB() {
return requestB;
}
public void setRequestB(String requestB) {
this.requestB = requestB;
}
}
And a result class, RouterResult.java:
public class RouterResult {
private String result;
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
#Override
public String toString() {
return "RouterResult{" +
"result='" + result + '\'' +
'}';
}
}
Then, to invoke this Lambda, you will need a role that has the lambda:InvokeFunction permission. The code to invoke looks like:
public class RouterRunner {
private static final String AWS_ACCESS_KEY_ID = "<access key>";
private static final String AWS_SECRET_ACCESS_KEY = "<access secret>";
public static void main( String[] argv ) throws IOException {
AWSCredentials credentials = new BasicAWSCredentials( AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY );
AWSLambda lambda = AWSLambdaClientBuilder.standard()
.withRegion(Regions.US_WEST_2)
.withCredentials(new AWSStaticCredentialsProvider(credentials)).build();
RequestTypeA requestTypeA = new RequestTypeA();
requestTypeA.setRequestA("set from the runner, request type A");
ObjectMapper objectMapper = new ObjectMapper();
InvokeRequest invokeRequest = new InvokeRequest()
.withFunctionName("lambda-router")
.withPayload(objectMapper.writeValueAsString(requestTypeA));
invokeRequest.setInvocationType(InvocationType.RequestResponse);
InvokeResult invokeResult = lambda.invoke(invokeRequest);
String resultJSON = new String(invokeResult.getPayload().array(), StandardCharsets.UTF_8);
System.out.println( "result from lambda is " + resultJSON );
RouterResult routerResult = objectMapper.readValue(resultJSON, RouterResult.class);
System.out.println( "result.toString is " + routerResult.toString() );
RequestTypeB requestTypeB = new RequestTypeB();
requestTypeB.setRequestB("set from the runner, request type B");
invokeRequest = new InvokeRequest()
.withFunctionName("lambda-router")
.withPayload(objectMapper.writeValueAsString(requestTypeB));
invokeRequest.setInvocationType(InvocationType.RequestResponse);
invokeResult = lambda.invoke(invokeRequest);
resultJSON = new String(invokeResult.getPayload().array(), StandardCharsets.UTF_8);
System.out.println( "result from lambda is " + resultJSON );
routerResult = objectMapper.readValue(resultJSON, RouterResult.class);
System.out.println( "result.toString is " + routerResult.toString() );
}
}
There likely needs to be some error handling improvement and I'm sure you could make this a bit more efficient. But that's the overall idea. Ultimately on the Lambda side I convert the InputStream to a String and convert that String to some sort of object based on a common field in the request types. On the client side I convert the objects into JSON, send them out, and convert the result back from a JSON String back into the result object.

Post Request return 404 for Spring Boot with Postman

I am trying to use postman to test one of the post requests I created for my spring boot application. My post requests through postman always return 404.
I have created a same mapping route for a get request and with the postman, the get request works as expected.
I have tested with aws cli and made sure that I have the correct access key and secret key for uploading files to S3.
Code for my services
#Service
public class AmazonClient {
private AmazonS3 s3client;
#Value("${amazonProperties.endpointUrl}")
private String endpointUrl;
#Value("${amazonProperties.bucketName}")
private String bucketName;
#Value("${amazonProperties.accessKey}")
private String accessKey;
#Value("${amazonProperties.secretKey}")
private String secretKey;
#PostConstruct
private void initializeAmazon() {
AWSCredentials credentials = new BasicAWSCredentials(this.accessKey, this.secretKey);
this.s3client = AmazonS3ClientBuilder.standard().withRegion(Regions.US_EAST_2).withCredentials(
new AWSStaticCredentialsProvider(credentials)).build();
}
#Async
public String uploadFile(MultipartFile multipartFile, boolean enablePublicReadAccess) {
String fileUrl = "";
System.out.println("Reach");
try {
File file = convertMultiPartToFile(multipartFile);
String fileName = generateFileName(multipartFile);
System.out.println("FileName: " + fileName);
fileUrl = endpointUrl + "/" + bucketName + "/" + fileName;
PutObjectRequest putObjectRequest = new PutObjectRequest(this.bucketName, fileName, file);
if (enablePublicReadAccess) {
putObjectRequest.withCannedAcl(CannedAccessControlList.PublicRead);
}
s3client.putObject(putObjectRequest);
file.delete();
} catch (Exception e) {
e.printStackTrace();
}
return fileUrl;
}
private File convertMultiPartToFile(MultipartFile file) throws IOException {
File convFile = new File(file.getOriginalFilename());
FileOutputStream fos = new FileOutputStream(convFile);
fos.write(file.getBytes());
fos.close();
return convFile;
}
private String generateFileName(MultipartFile multiPart) {
return new Date().getTime() + "-" + multiPart.getOriginalFilename().replace(" ", "_");
}
public String deleteFileFromS3Bucket(String fileUrl) {
String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);
s3client.deleteObject(new DeleteObjectRequest(bucketName, fileName));
return "Successfully deleted";
}
}
Code for my controller:
#RestController
#RequestMapping("/storage/files")
public class BucketController {
private AmazonClient amazonClient;
#Autowired
BucketController(AmazonClient amazonClient) {
this.amazonClient = amazonClient;
}
#GetMapping
public String getFile(){
return "Files";
}
#PostMapping("/file")
public String file() {
return "Reach!";
}
#PostMapping
public String uploadFile(#RequestPart(value = "file") MultipartFile file) {
System.out.println("Reach!!");
return this.amazonClient.uploadFile(file, true);
}
#DeleteMapping
public String deleteFile(#RequestPart(value = "url") String fileUrl) {
return this.amazonClient.deleteFileFromS3Bucket(fileUrl);
}
}
My security config:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/css/**", "/js/**", "/fonts/**", "/index").permitAll()
.antMatchers("/storage*").permitAll();
Through postman, I have selected a POST request and put http://localhost:8080/storage/files/file, in the body, I have entered a key "file" and set the value to a file type and chose a file from my local.
Here is the response:
{
"timestamp": "2019-09-02T19:09:54.864+0000",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/storage/files/file"
}
Project Structure
Postman Results
This is almost certainly your security config interfering.
Have you tried: .antMatchers("/storage/**") instead?

POST Multipart file as form data to a REST service

I am trying to POST multipart file as a form data to a REST service which returns me a url after it saved at REST service end. In postman the request look like this.
I have a Spring Boot service which has a method to get Multipart file form frontend via jquery fileuploader. I need to post the file to the above URL which postman sends and saves in there end. I think i have to construct form data in my Spring boot service. Below are few snaps of the Spring boot service.
Controller end.
#RequestMapping(method = RequestMethod.POST, value = "/file-upload/{profileName:.+}")
public Attachment uploadFile(#RequestParam("file") MultipartFile input,
#PathVariable("profileName") String profileName) throws IOException {
Attachment attachment = new Attachment();
if (input != null) {
log.info("Upload a new attachment item" + input.getName());
byte[] fileByteArray = input.getBytes();
attachment.setEncodedFile(Utils.encodeBytes(fileByteArray));
attachment.setFileName(input.getOriginalFilename());
socialMediaService.uploadMedia(input, profileName);
}
return attachment;
}
SocialMediaService
public String uploadMedia(MultipartFile input, String profileName) {
String mediaUploadPath = "wall_attach/lenne-public";
Map < String, String > params = new HashMap < > ();
String mediaUploadFullPath =
UrlBuilder.build(System.getenv(Constants.HUBZILLA_URL), mediaUploadPath, params);
if (!isRestServiceProvided) {
restService = new RestService(RequestType.POST, mediaUploadFullPath);
}
MultipartEntityBuilder builder = restService.getEntityBuilder();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
try {
builder.addBinaryBody("userfile", input.getBytes(), ContentType.DEFAULT_BINARY, input.getOriginalFilename());
} catch (IOException e) {
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
String strResp = restService.execute(profileName, Constants.HUBZILLA_PW);
return strResp;
}
return null;
}
RestService class
public class RestService {
private Logger log;
private HttpClient client = null;
private HttpRequest request = null;
private RequestType reqType = null;
private String body;
MultipartEntityBuilder builder = null;
public RestService() {
this.log = LoggerFactory.getLogger(RestService.class);
}
/**
* Create REST service with external parameters.
*
* #param reqType RequestType
* #param client HttpClient
* #param request External HttpRequest
*/
public RestService(RequestType reqType, HttpClient client, HttpRequest request, Logger log) {
this.reqType = reqType;
this.client = client;
this.request = request;
this.log = log;
}
/**
* Create REST service string parameters.
*
* #param reqType RequestType
* #param fullPath Full path of REST service
*/
public RestService(RequestType reqType, String fullPath) {
this.client = HttpClientBuilder.create().build();
this.reqType = reqType;
this.log = LoggerFactory.getLogger(RestService.class);
if (reqType == RequestType.GET) {
this.request = new HttpGet(fullPath);
} else if (reqType == RequestType.POST) {
this.request = new HttpPost(fullPath);
} else if (reqType == RequestType.DELETE) {
this.request = new HttpDelete(fullPath);
}
}
/**
* Execute REST service without authentication.
*
* #return - Result of the service.
*/
public String execute() {
return execute(null, null);
}
/**
* Execute REST web service with basic authentication.
*
* #return - Result of the service.
*/
public String execute(String user, String password) {
try {
if (user != null && password != null) {
StringBuilder authString = new StringBuilder();
authString.append(user).append(":").append(password);
String authBase = new String(
Base64.getEncoder().encode(authString.toString().getBytes(Charset.forName("UTF-8"))));
String authType = "Basic ";
String authHeader = authType + authBase;
request.setHeader(HttpHeaders.AUTHORIZATION, authHeader);
}
HttpResponse response = null;
if (this.reqType == RequestType.GET) {
HttpGet get = (HttpGet) request;
response = client.execute(get);
} else if (this.reqType == RequestType.POST) {
HttpPost post = (HttpPost) request;
if (body != null) {
StringEntity stringEntity = new StringEntity(body);
post.setEntity(stringEntity);
}
if (builder != null) {
HttpEntity entity = builder.build();
post.setEntity(entity);
}
response = client.execute(post);
} else {
throw new NotImplementedException();
}
if (response != null && (response.getStatusLine().getStatusCode() == Status.OK.getStatusCode()
|| response.getStatusLine().getStatusCode() == Status.CREATED.getStatusCode())) {
HttpEntity entity = response.getEntity();
return EntityUtils.toString(entity);
}
} catch (Exception e) {
log.error("External service call failed ", e);
}
return null;
}
public void setBody(String body) {
this.body = body;
}
public MultipartEntityBuilder getEntityBuilder() {
this.builder = MultipartEntityBuilder.create();
return this.builder;
}
}
My problem is not getting any result after I executed the rest service upload media method. But it worked perfectly via postman.
Can anybody let me know what am I missing? Is the way I constructed the form data in java correct?
Thank you in advance.
Try adding consumes parameter in #RequestMapping like this (consumes = "multipart/form-data")
#RequestMapping(method = RequestMethod.POST, consumes = "multipart/form-data" ,value = "/file-upload/{profileName:.+}")
public Attachment uploadFile(#RequestParam("file") MultipartFile input,
#PathVariable("profileName") String profileName) throws IOException {
----
----
}
There is another relevant issue here:
Trying to upload MultipartFile with postman
Please read the comments under answer in this link.
Hope it helps!

Consuming Soap Service in spring boot application

I need to consume a soap service in spring boot. How can i do that easily using annotations like we do for Rest. I need to send headers, form the body for my service. Please help me with the solution
public String sendMessage(String processInstanceId) {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
String request = "<SOAP:Envelope xmlns:" + "SOAP='http://schemas.xmlsoap.org/soap/envelope/'>" + "<SOAP:Body>"
+ "<SendMessage xmlns='http://schemas.cordys.com/bpm/execution/1.0'>" + "<receiver>" + processInstanceId
+ "</receiver>" + "<message overwrite='false' />" + "</SendMessage>" + "</SOAP:Body>"
+ "</SOAP:Envelope>";
SendMessageAPI sendMessageObject = new SendMessageAPI();
StreamSource source = new StreamSource(new StringReader(request));
StreamResult result = new StreamResult(System.out);
System.out.println("called service" + request);
webServiceTemplate.sendSourceAndReceiveToResult(
"url",
source, result);
return "Success";
You may use Spring Web Service where it's present the WebServiceTemplate similar to the RestTemplate
In order to add SOAP Header and/or HTTP Header you can implement the WebServiceMessageCallback interface.
Here a simple example for adding HTTP Headers
The WebServiceMessageCallback implementation (note I'm using Axiom as MessageFactory)
public class WsHttpHeaderCallback implements WebServiceMessageCallback
{
private String headerKey;
private String headerValue;
private String soapAction;
public WsHttpHeaderCallback(String headerKey, String headerValue, String soapAction)
{
super();
this.headerKey = headerKey;
this.headerValue = headerValue;
this.soapAction = soapAction;
}
public WsHttpHeaderCallback()
{
super();
}
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException
{
validateRequiredFields();
addRequestHeader(headerKey, headerValue);
if (StringUtils.hasText(this.soapAction))
{
AxiomSoapMessage axiomMessage = (AxiomSoapMessage) message;
axiomMessage.setSoapAction(this.soapAction);
}
}
private void addRequestHeader(String headerKey, String headerValue)
{
TransportContext context = TransportContextHolder.getTransportContext();
WebServiceConnection connection = context.getConnection();
if (connection instanceof HttpComponentsConnection)
{
HttpComponentsConnection conn = (HttpComponentsConnection) connection;
HttpPost post = conn.getHttpPost();
post.addHeader(headerKey, headerValue);
}
else if( connection instanceof ClientHttpRequestConnection )
{
ClientHttpRequestConnection conn = (ClientHttpRequestConnection)connection;
conn.getClientHttpRequest().getHeaders().add(headerKey, headerValue);
}
}
}
The WebServiceMessageCallback usage:
WebServiceResponse resp = (WebServiceResponse)webSvcTemplate.marshalSendAndReceive(wsUrl, request, new WsHttpHeaderCallback(headerKey, headerValue, "http://ws.com/soapAction") );
I hope it's usefull
Angelo

Spring HttpRequestHandler + XMLHttpRequest

i have a problem HttpRequestHandler does not receive any data when i send post data by javascript. i want to receive value of content, but it does not work.
Here is javascript code:
function utils_saveElementAndGetId(url,content) {
var xhr = new XMLHttpRequest()
xhr.open("post", url, false);
xhr.send(content);
if (xhr.status != 200) {
alert(xhr.status + ': ' + xhr.statusText)
} else {
return xhr.responseText
}
}
here is code of HttpRequestHandler:
public class HeaderServlet implements HttpRequestHandler {
private static final Logger log = LoggerFactory.getLogger(HeaderServlet.class);
TemplateDao templateDao;
HeaderElementDao headerElementDao;
CheckboxElementDao checkboxElementDao;
#Autowired
public HeaderServlet(TemplateDao templateDao, HeaderElementDao headerElementDao, CheckboxElementDao checkboxElementDao) {
this.templateDao = templateDao;
this.headerElementDao = headerElementDao;
this.checkboxElementDao = checkboxElementDao;
}
public void handleRequest(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
String content = req.getParameter("content");
HeaderElement headerElement = new HeaderElement(content);
Long templateId = (Long) req.getSession().getAttribute("id");
Template template = templateDao.get(templateId);
headerElement.template = template;
headerElementDao.create(headerElement);
template.headerElements.add(headerElement);
templateDao.saveOrUpdate(template);
resp.setStatus(200);
resp.setContentType("text/plain");
resp.getOutputStream().println(headerElement.getId());
resp.flushBuffer();
}
}
I have solved the problem , the problem was in javascript side , i have just forgot to add xhr.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");

Resources