I am not able to call a #Future method - rest

I understand that I cannot directly call a future method from a batch class. But from many other answers, I can see that it is possible to do so by creating a helper class and calling the future method there. But it is not working for me. Please check my code below.
Also, I have tried to do it with the queueable class as suggested in this link, but it is not working for me. The error was " Callout not allowed from this future method. Please enable callout by annotating the future method. eg: #Future(callout=true)"
But I am more interested in the first and simpler way to do this.
public class OrdersItemsHelper {
static Document tDoc;
static blob csvBlob;
//prepare csv file to send
public static void CreateCsvFile(List<Order_Line_Items__c> orderItemsList)
{
//Code to create file here
csvBlob = Blob.valueOf(finalstr);
tDoc = new Document();
tDoc.Name = 'sales_items_' +date.today();
tDoc.Type = 'csv';
tDoc.body = csvBlob;
tDoc.FolderId = [select id from folder where name = 'Emarsys Order Files'].Id;
tDoc.ContentType = 'application/vnd.ms-excel';
Insert tDoc;
system.debug('doc inserted');
sendFile();
}
#Future(callout = true)
public static void sendFile()
{
System.debug('I am creating the post request');
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setMethod('POST');
request.setHeader('Authorization','Security Token');
request.setHeader('Accept','text/plain');
request.setHeader('Content-Type', 'text/csv');
request.setHeader('Authorization', 'Bearer ');
request.setBodyAsBlob(csvBlob);
HttpResponse response = http.send(request);
system.debug('response: ' + response);
}

So I tried again by doing it in the queueable apex class. The thing I was missing was "Database.AllowsCallouts" in the class heading. Below is my queueable class which is working with the batch class to send a rest post request.
public class OrderItemFilePostHelper implements System.Queueable,Database.AllowsCallouts
{
private Blob csvBlob;
public EmarsysOrderItemFilePostHelper(Blob csvBlob) {
this.csvBlob = csvBlob;
}
public void execute(System.QueueableContext objContext)
{
System.debug('I am creating the post request');
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setMethod('POST');
request.setHeader('Authorization','Security Token');
request.setHeader('Accept','text/plain');
request.setHeader('Content-Type', 'text/csv');
request.setHeader('Authorization', 'Bearer ');
request.setEndpoint('https://webhook.site/b0746268-e95c-4f94-bcb6-61d4bea54378');
request.setBodyAsBlob(csvBlob);
HttpResponse response = http.send(request);
system.debug('response: ' + response);
}
}

Related

POST requests for incoming events

I'm trying to create a webhook for Twitter's event, I don't understand POST requests for incoming events, can anybody give me a hint on how to do this on Net Core 3.1?, I have tried below but not sure what I did. I need advice. thank you so much
[HttpPost]
[Route("/Twitter/")]
[AllowAnonymous]
public ActionResult Listen()
{
string _json_response = string.Empty;
try
{
using (var reader = new StreamReader(Request.Body))
{
string _json = (await reader.ReadToEndAsync()).Trim();
if (!string.IsNullOrEmpty(_json))
{
Helper.SaveLog("TwitterController.Listen : here 2 " + _json, Helper.Log_Mode.Info);
}
Task.WaitAll();
}
}
return Content(_json, "application/json");
}
I expect to get the CRC challenge in the above POST, but it doesn't arrive there

HTTP for images between client and server sends empty stream

I am trying to send a stream (containing an image file) from a WASM client to a backend .NET Core 5 server. In the WASM app, I start with a MemoryStream that contains the file data. In order to send the data contained in this MemoryStream using HttpClient.PostAsync, I seem to have to convert it to a StreamContent object:
StreamContent streamContent = new StreamContent(imageMemoryStream);
I use the debugger to verify that the length of the content of streamContent is not zero at this point. So far so good.
I then use HttpClient.PostAsync to send this stream to the server:
var response = await Http.PostAsync("api/HttpStreamReceiver", streamContent);
On the server side, I have a controller that receives HTTP messages:
[Route("api/[controller]")]
[ApiController]
public class HttpStreamReceiverController : ControllerBase
{
[HttpPost]
public async Task<ActionResult> Get()
{
Stream imageStream;
try
{
imageStream = Request.Body;
}
catch (Exception)
{
return new BadRequestObjectResult("Error saving file");
}
}
}
Here, it seems that Request.Body is empty. Trying to evaluate the length of either Request.Body or of imageStream on the server side results in a System.NotSupportedException, and
await imageStream.ReadAsync(buffer);
leaves buffer blank. What am I doing wrong here?
The image file cannot be transmitted through the body unless it is serialized. I suggest you use MultipartFormDataContent to pass the file.
This is an example.
class Program
{
static async Task Main(string[] args)
{
string filePath = #"D:\upload\images\1.png";
HttpClient _httpClient = new HttpClient();
string _url = "https://localhost:44324/api/HttpStreamReceiver/";
if (string.IsNullOrWhiteSpace(filePath))
{
throw new ArgumentNullException(nameof(filePath));
}
if (!File.Exists(filePath))
{
throw new FileNotFoundException($"File [{filePath}] not found.");
}
//Create form
using var form = new MultipartFormDataContent();
FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
byte[] buffur = new byte[fs.Length];
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(buffur);
//var bytefile = AuthGetFileData(filePath);
var fileContent = new ByteArrayContent(buffur);
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
form.Add(fileContent, "image", Path.GetFileName(filePath));
//the other data in form
var response = await _httpClient.PostAsync($"{_url}", form);
response.EnsureSuccessStatusCode();
var responseContent = await response.Content.ReadAsStringAsync();
bw.Close();
}
}
Web api.
[Route("api/[controller]")]
[ApiController]
public class HttpStreamReceiverController: ControllerBase
{
[HttpPost]
public async Task<ActionResult> Get(IFormFile image)
{
//...
return Ok("get");
}
}
Result:

Best practices to handle Web API status codes

I have a web API project done with .NETCore.
My web API receives a request from another Service A, with the information I have I need to do some conversion on the data and send it to another Service B.
I am expecting that Service B send back some response: like OK or NOK. As the number of codes I can get back from Service B are so much. I would like to know which is the best practices to handle those codes?
As you will see in my code, I get the status code in this way:
var status = (int)response.StatusCode;
And the I have some if to handle this. Looking at my code it looks like a very poor status code Handling but at moment it is the best I can do. I am kindly asking suggestions to improve this.
I am using RestSharp.
Following my code:
[HttpPost]
[Produces("application/json", Type = typeof(MyModel))]
public async Task<IActionResult> Post([FromBody]MyModel myModel)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var response = (RestResponse) await _restHelper.GetResponse("ServiceB:url", myModel);
if (response != null)
{
var status = (int)response.StatusCode;
//2xx status OK
if (status >= 200 && status < 300)
{
return Ok(response.Content);
}
//Catch all status code
return StatusCode(status, response.Content);
}
//If for some reason, I don't get any response from ServiceB
return NotFound("No response from ServiceB");
}
catch (Exception ex)
{
_logger.LogError("POST_ERROR", "ServiceB-relay/Post UNEXPECTED ERROR", ex.Message);
return StatusCode(500, "Server error, not able to process your request");
}
}
and this is my restHelper
public class RestHelper: IRestHelper
{
private readonly IConfigurationRoot _config;
public RestHelper(IConfigurationRoot config)
{
_config = config;
}
public async Task<IRestResponse> GetResponse(string configKey, object dtoObject)
{
//Get the URL from the config.json
var url = _config[configKey];
//Create rest client and rest request
var restClient = new RestClient(url);
var request = new RestRequest {Timeout = 30000, Method = Method.POST};
//Add header
request.AddHeader("Accept", "application/json");
//convert the dto object to json
var jsonObject = JsonConvert.SerializeObject(dtoObject.ToString(), Formatting.Indented);
request.AddParameter("application/json", jsonObject, ParameterType.RequestBody);
var taskCompletion = new TaskCompletionSource<IRestResponse>();
//Execute async
restClient.ExecuteAsync(request, r => taskCompletion.SetResult(r));
//await the task to finish
var response = (RestResponse) await taskCompletion.Task;
return response;
}
Thanks

Apache HttpClient - REST API: Issue in converting response to customized object which is put as SerializableEntity

I am using Apache HttpClient to put/get customized object using REST APIs. Below is the sample code. My putObject() method works fine and I could serialize Person object and put properly. However, while getting the object, I got below error:
Exception in thread "main" java.lang.ClassCastException: [B cannot be cast to Person at MyTest.demoGetRESTAPI(MyTest.java:88) at MyTest.main(MyTest.java:21)
Seems the code to build Person object out of response entity is not correct
HttpEntity httpEntity = response.getEntity();
byte[] resultByteArray = EntityUtils.toByteArray(httpEntity);
Person person = (Person)SerializationUtils.deserialize(resultByteArray);
Am I doing somthing wrong while getting byte[] array and converting to Person object. Please help me out to solve this issue.
Complete Example Program:
import java.io.Serializable;
import org.apache.commons.lang.SerializationUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.SerializableEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
public class MyTest {
public static void main(String[] args) throws Exception {
putObject();
getObject();
}
public static void putObject() throws Exception
{
HttpClient httpClient = new DefaultHttpClient();
Person person = new Person();
person.setName("Narendra");
person.setId("1");
try
{
//Define a postRequest request
HttpPut putRequest = new HttpPut("http://localhost:9084/ehcache-server/rest/screeningInstance/2221");
//Set the API media type in http content-type header
putRequest.addHeader("content-type", "application/x-java-serialized-object");
//Set the request put body
SerializableEntity personSEntity = new SerializableEntity(SerializationUtils.serialize(person));
putRequest.setEntity(personSEntity);
//Send the request; It will immediately return the response in HttpResponse object if any
HttpResponse response = httpClient.execute(putRequest);
//verify the valid error code first
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != 201)
{
throw new RuntimeException("Failed with HTTP error code : " + statusCode);
}
}
finally
{
//Important: Close the connect
httpClient.getConnectionManager().shutdown();
}
}
public static void getObject() throws Exception
{
DefaultHttpClient httpClient = new DefaultHttpClient();
try
{
//Define a HttpGet request; You can choose between HttpPost, HttpDelete or HttpPut also.
//Choice depends on type of method you will be invoking.
HttpGet getRequest = new HttpGet("http://localhost:9084/ehcache-server/rest/screeningInstance/2221");
//Set the API media type in http accept header
getRequest.addHeader("accept", "application/x-java-serialized-object");
//Send the request; It will immediately return the response in HttpResponse object
HttpResponse response = httpClient.execute(getRequest);
//verify the valid error code first
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != 200)
{
throw new RuntimeException("Failed with HTTP error code : " + statusCode);
}
//Now pull back the response object
HttpEntity httpEntity = response.getEntity();
byte[] resultByteArray = EntityUtils.toByteArray(httpEntity);
Person person = (Person)SerializationUtils.deserialize(resultByteArray);
}
finally
{
//Important: Close the connect
httpClient.getConnectionManager().shutdown();
}
}
}
class Person implements Serializable{
String name;
String id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#Override
public String toString() {
return "Person [name=" + name + ", id=" + id + "]";
}
}
I got the solution. It was mistake in my code:
While putting object, I have written below code. That was doing two time serialization. First from Person object to byte[] and second from byte[] to byte[].
SerializableEntity personSEntity = new SerializableEntity(SerializationUtils.serialize(person));
putRequest.setEntity(personSEntity);
This is the right approach:
SerializableEntity personSEntity = new SerializableEntity(person);
putRequest.setEntity(personSEntity);
After getting binary from REST, code should be like below to get Object:
HttpEntity httpEntity = response.getEntity();
InputStream inputStream = null;
try {
inputStream = httpEntity.getContent();
Person p = (Person) SerializationUtils.deserialize(inputStream);
System.out.println("Person:" + p.getName());
}
finally {
inputStream.close();
}
This worked like CHARM !!

Why HandshakeRequest doesn't return HttpSession in my ServerEndpointConfig.Configurator?

There is a good example for sharing HttpSession between Websocket and Rest service. (Spring DispatchServlet cannot find resource within Jetty) But it doesn't work for me. I'm not sure is there any thing I'm missing?
I'm using Jetty as websocket server and also I created a WebApp as well which injected by SpringConfig.
private void init() throws Exception
{
Server server = new Server();
// Create SSL Connector
ServerConnector serverConnector = getSSLConnector(server);
// Bundle to server
server.setConnectors(new Connector[] { serverConnector });
// Create request handler collection
HandlerCollection handlers = new HandlerCollection();
// Add WebSocket handler
final ServletContextHandler servletContextHandler = getWebSocketContextHandler();
handlers.addHandler(servletContextHandler);
// Add Servlet handler
handlers.addHandler(getWebAppServletContextHandler());
server.setHandler(handlers);
// Initial WebSocket
WebSocketServerContainerInitializer.configureContext(servletContextHandler);
// Start Jetty
server.start();
server.join();
}
Both WebSocket and Rest are working under same port perfectly, of course, with different context paths.
Now, I created a Rest service:
#RequestMapping(value = "/login", method = RequestMethod.POST)
#Consumes({ MediaType.APPLICATION_JSON_VALUE })
#Produces({ MediaType.APPLICATION_JSON_VALUE })
public #ResponseBody Message login(#RequestBody Credential credential, #Context HttpServletRequest servlerRequest)
{
...
HttpSession session = servlerRequest.getSession(true);
session.setAttribute("userName", credential.getUserName());
...
Message message = new Message();
...
return message;
}
In this service I created a HttpSession and stored something in. As I said, it works, and so does the session.
Rest client:
public void login() throws KeyManagementException, NoSuchAlgorithmException
{
final String loginServiceUri = HTTP_SERVICE_BASE_URI + "/login";
ClientConfig clientConfig = new DefaultClientConfig();
...
Client client = Client.create(clientConfig);
WebResource webResource = client.resource(loginServiceUri);
ClientResponse response = webResource
.type("application/json")
.post(ClientResponse.class, new Credential("user","pass"));
if (response.getStatus() != 200) {
throw new RuntimeException("Failed : HTTP error code : " + response.getStatus());
}
List<NewCookie>cookies = response.getCookies();
ClientEndpointConfigurator.setCookies(cookies); <== Store cookies as well as session to ClientEndpointConfigrator class
Message message = response.getEntity(Message.class);
...
}
ClientEndpointConfigrator class has a static list for all cookies which like this:
public class ClientEndpointConfigurator extends ClientEndpointConfig.Configurator {
private static List<NewCookie> cookies = null;
public static void setCookies(List<NewCookie> cookies) {
ClientEndpointConfigurator.cookies = cookies;
}
...
#Override
public void beforeRequest(Map<String, List<String>> headers) {
...
if(null != cookies)
{
List<String> cookieList = new ArrayList<String>();
for(NewCookie cookie: cookies)
{
cookieList.add(cookie.toString());
}
headers.put("Cookie", cookieList);
}
...
}
}
beforeRequest() method will put all cookies to request header. If you inspect the cookieList, you will see:
[JSESSIONID=tvum36z6j2bc1p9uf2gumxguh;Version=1;Path=/rs;Secure]
Things looks prefect.
Finally, create a server end ServerEndpointConfigurator class, and override the modifyHandshake() method to retrieve the session and cookies
public class SpringServerEndpointConfigurator extends ServerEndpointConfig.Configurator {
#Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
super.modifyHandshake(sec, request, response);
httpSession = (HttpSession)request.getHttpSession(); <== **It returns null here!**
...
}
}
}
I can't get my HttpSession back! and if you print headers out, you will see the cookie has been changed:
Cookie: JSESSIONID="tvum36z6j2bc1p9uf2gumxguh";$Path="/rs"
Any one knows what's the reason?
All right, I figured it out, it's because I put WebSocket and Rest to different context handler. Jetty keeps handlers isolate to each other. To share session information, you have to put them together.
But if someone does want to separate them, it is still possible done by sharing SessionManager or SessionHandler. There are many ways to achieve this, you can inject SessionHandler to each ServletContext or just define it as a static variable and put it on somewhere every one can reach, each way works.