How to extract data from POST request in azure functions java - java

I send form data in POST request from angular app to my azure functions who wrriten in java.
the client side look like this:
#Injectable({
providedIn: 'root'
})
export class SendItemToAzureFunctionsService {
private functionURI: string;
constructor(private http: HttpClient) {
this.functionURI = 'https://newsfunctions.azurewebsites.net/api/HttpTrigger-Java?code=k6e/VlXltNs7CmJBu7lmBbzaY4tlo21lXaLuvfG/tI7m/XXXX';
}
// {responseType: 'text'}
sendItem(item: Item){
let body = new FormData();
body.append('title', item.title);
body.append('description', item.description);
body.append('link', item.link);
return this.http.post(this.functionURI, body)
.pipe(
map((data: string) => {
return data;
}), catchError( error => {
return throwError( 'Something went wrong!' );
})
)
}
}
when Item recived to azure functions.
the aim of functions is to send this item in push notifications via firebase to android app.
the azure functions with HTTP trigger look like this:
#FunctionName("HttpTrigger-Java")
public HttpResponseMessage run(#HttpTrigger(name = "req", methods = { HttpMethod.GET,
HttpMethod.POST }, authLevel = AuthorizationLevel.FUNCTION) HttpRequestMessage<Optional<String>> request,
final ExecutionContext context) {
context.getLogger().info("Java HTTP trigger processed a request.");
// Parse query parameter
String itemDetails = request.getBody().get();
if (itemDetails == null) {
return request.createResponseBuilder(HttpStatus.BAD_REQUEST)
.body("Please pass a name on the query string or in the request body").build();
} else {
// ======
String postUrl = "https://fcm.googleapis.com/fcm/send";
HttpClient httpClient = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(postUrl);
post.setHeader("authorization", FIREBAE_AUTH);
post.setHeader("Content-type", "application/json");
JSONObject contentJson = new JSONObject();
contentJson.put("title", "example title");
contentJson.put("description", "example text");
JSONObject pushNotificationJson = new JSONObject();
pushNotificationJson.put("data", contentJson);
pushNotificationJson.put("to", "/topics/newsUpdateTopic");
try {
StringEntity stringEntity = new StringEntity(pushNotificationJson.toString(), "UTF-8");
post.setEntity(stringEntity);
HttpResponse response = httpClient.execute(post);
System.out.println(response.getEntity().getContent().toString());
} catch (IOException var9) {
var9.printStackTrace();
}
// =========
}
return request.createResponseBuilder(HttpStatus.OK)
.body("succeed to send new item in push notification to clients").build();
}
when I am running String itemDetails = request.getBody().get();
I am getting:
------WebKitFormBoundary2gNlxQx5pqyAeDL3
Content-Disposition: form-data; ....
I will be glad to know how to get data item from that?

If you want to parse from-date type data in Azure function with java, you can try to use MultipartStream in SDK org.apache.commons.fileupload to implement it. For example
code
public HttpResponseMessage run(
#HttpTrigger(name = "req", methods = {HttpMethod.GET, HttpMethod.POST}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
final ExecutionContext context) throws IOException {
context.getLogger().info("Java HTTP trigger processed a request.");
String contentType = request.getHeaders().get("content-type");
String body = request.getBody().get(); // Get request body
String boundary = contentType.split(";")[1].split("=")[1]; // Get boundary from content-type header
int bufSize = 1024;
InputStream in = new ByteArrayInputStream(body.getBytes()); // Convert body to an input stream
MultipartStream multipartStream = new MultipartStream(in, boundary.getBytes(), bufSize, null); // Using MultipartStream to parse body input stream
boolean nextPart = multipartStream.skipPreamble();
while (nextPart) {
String header = multipartStream.readHeaders();
int start =header.indexOf("name=") + "name=".length()+1;
int end = header.indexOf("\r\n")-1;
String name = header.substring(start, end);
System.out.println(name);
multipartStream.readBodyData(System.out);
System.out.println("");
nextPart = multipartStream.readBoundary();
}
return request.createResponseBuilder(HttpStatus.OK).body("success").build();
}
Test. I test with postman

I've used #Jim Xu's code and created a class to get the data in easier way. Here is the gist - https://gist.github.com/musa-pro/dcef0bc23e48227e4b89f6e2095f7c1e

Related

OkHttp returns 403 as response

I'm trying to make a request to the Third-party API, but I'm running into some issues using OkHTTP.
I'm using AWS4Signer to sign the request. I'm able to generate the credentials for the same.
Request<Void> requestAws = new DefaultRequest<Void>("sts");
requestAws.setHttpMethod(HttpMethodName.POST);
requestAws.setEndpoint(URI.create("third pary api call which uses https"));
requestAws.addHeader("x-amz-security-token", sessionCredentials.getSessionToken());
requestAws.addHeader("Content-Type", "application/json");
//sign the request
AWS4Signer signer = new AWS4Signer();
signer.setServiceName(Constant.SERVICE_NAME);
signer.setRegionName(Constant.AWS_REGION);
signer.sign(requestAws, new AWSCredentials() {
#Override
public String getAWSSecretKey() {
return sessionCredentials.getAccessKeyId();
}
#Override
public String getAWSAccessKeyId() {
return sessionCredentials.getSecretAccessKey();
}
});
Map<String, String> headers = requestAws.getHeaders();
String x_date = null;
String x_token = null;
String authorization = null;
String x_content = null;
//get and assign values
for (Map.Entry<String, String> entry : headers.entrySet()) {
if (entry.getKey().equals("x-amz-security-token")) {
x_token = entry.getValue();
}
if (entry.getKey().equals("X-Amz-Date")) {
x_date = entry.getValue();
}
if (entry.getKey().equals("Authorization")) {
authorization = entry.getValue();
}
}
logger.info("Headers body response: " + JsonUtils.jsonize(headers));
String json = objectMapper.writeValueAsString(emailRequestBody);
postHandler.post(x_date, x_token, authorization, json);
Below is the request code of okHTTP
String post(String x_date, String x_token, String authorization, String json) throws IOException {
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
.url("https url is here")
.addHeader("Content-Type", "application/json")
.addHeader("X-Amz-Date", x_date)
.addHeader("x-amz-security-token", x_token)
.addHeader("Authorization", authorization)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
Below is how the request looks like with headers:
Request{method=POST, url=https://cbc.com/api/send/email, headers=[Content-Type:application/json, X-Amz-Date:20220125T111056Z, x-amz-security-token:FwoGZXIvYXdzEHUaDF6/kQ0g7Mog7W1f7CK0ATG5xhFIXP34wRjziAkJKhw9vE5cbADBOpji7uqtLp5GLGLay+e9O2deFRB4eSpUMOOThDCEQg1tum43iX4a+8Kikuc3fv5gDjbMrdLJYAK3piYVbOAET8BAXdDdkPZVG+nNu31cEWZe9HC60svIj0m95YZ9Xx5rBIDm0AVWtj4JRCmonNm1ymCNRB4GTjhEzgnxlkqEYfdUivFdlORq/IlIssUzzV04fkr0kiqDiE9GrmU51ijAtb+PBjIt9MWbM8+x4z+y+IV4JFjuK4zrVW3Iaw4xUG/C+mpcCrZrunh+8fWgVTR6In1r, Authorization:AWS4-HMAC-SHA256 Credential=medS2y7xvISbOf7ke3IWthyCMV5koeTDD5r3gkxJ/20220125/us-west-2/execute-api/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-amz-security-token, Signature=d862c9ed8175770244e17fd3cb216c2a92138183ad427ed67fc5f284a1a75266]}
Below is the response:
Response{protocol=h2, code=403, message=, url=https://cbc.com/api/send/email}
Why the response is returning 403? Can someone help me what I missed? Thank you for your time.

How can I pass headers using RestTemplate?

In my method I initially used RestTemplate postForObject method to post request to an endpoint. Now I have to add default OAuth token and pass it as Post request. Is there any way I can pass both request as well as Default Header as part of POST request by using postForObject?
Initiall I used below postForObject
String result = restTemplate.postForObject(url, request, String.class);
I am looking for something like below
restTemplate.exchange(url,HttpMethod.POST,getEntity(),String.class );
Here is my code
private final String url;
private final MarkBuild header;
public DataImpl(#Qualifier(OAuth) MarkBuild header,RestTemplate restTemplate) {
this.restTemplate= restTemplate;
this.header = header;
}
public void postJson(Set<String> results){
try {
Map<String, String> requestBody = new HashMap<>();
requestBody.put("news", "data");
JSONObject jsonObject = new JSONObject(requestBody);
HttpEntity<String> request = new HttpEntity<String>(jsonObject.toString(), null);
String result = restTemplate.postForObject(url, request, String.class);
}
}
Below is getHttpEntity which I want to pass with Post request
private HttpEntity getHttpEntity(Set <String>results) {
return new HttpEntity<>( null, getHttpHeaders() );
}
private HttpHeaders getHttpHeaders() {
return header.build();
}
}
Is there any way I can pass both request as well as Default Header as
part of POST request by using postForObject?
Yes, there is a way to do that, I can give a basic example:
HttpHeaders lHttpHeaders = new HttpHeaders();
lHttpHeaders.setContentType( MediaType.APPLICATION_JSON );//or whatever it's in your case
String payload="<PAYLOAD HERE>"
try
{
String lResponseJson = mRestTemplate.postForObject( url, new HttpEntity<Object>( payload, lHttpHeaders ), String.class);
return lResponseJson;
}
catch( Exception lExcp )
{
logger.error( lExcp.getMessage(), lExcp );
}
Let me know if this doesn't work!!

Avoid encoding the data while url creation

I am trying to make get call like this:
#GET(AppConstants.BASE_URL + "{category_type}/")
Call<JsonObject> callCustomFilterApI(#Path("category_type") String type,
#QueryMap(encoded = true) Map<String,String> fields ,
#Query("page") String pageNo);
But #QueryMap can have "&" in the data, so retrofit encoding it to %26.
Is there anyway "&" do not change to "%26".
Solution I tried:
Solution mentioned here
setting encoded=true/false
And also this one.
#DebDeep Asked:
I am passing data in QueryMap as:
private void callCustomFilterSearchPagesApi(String type, ArrayList<FilterListWithHeaderTitle> customFiltersList, int pageNumber, final ApiInteractor listener) {
Map<String, String> queryMap = new HashMap<>();
for (FilterListWithHeaderTitle item: customFiltersList) {
String pairValue;
if (queryMap.containsKey(item.getHeaderTitle())){
// Add the duplicate key and new value onto the previous value
// so (key, value) will now look like (key, value&key=value2)
// which is a hack to work with Retrofit's QueryMap
String oldValue=queryMap.get(item.getHeaderTitle());
String newValue="filters[" + item.getHeaderTitle() + "][]"
+oldValue+ "&"+"filters[" + item.getHeaderTitle() + "][]"+item.getFilterItem();
pairValue=newValue;
}else {
// adding first time
pairValue= item.getFilterItem();
}
try {
//pairValue= URLEncoder.encode(pairValue, "utf-8");
// LoggerUtils.logE(TAG,pairValue);
//queryMap.put(item.getHeaderTitle(), Html.fromHtml(pairValue).toString());
queryMap.put(item.getHeaderTitle(), pairValue);
}catch (Exception u){
LoggerUtils.crashlyticsLog(TAG,u.getMessage());
}
}
Call<JsonObject> call = TagTasteApplicationInitializer.mRetroClient.callCustomFilterApI(type, queryMap, "1");
requestCall(call, listener);
}
Use Interceptor and convert %26 to &:
class RequestInterceptor implements Interceptor {
#Override
Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
String stringurl = request.url().toString();
stringurl = stringurl.replace("%26", "&");
Request newRequest = new Request.Builder()
.url(stringurl)
.build();
return chain.proceed(newRequest);
}
}
Set this to your OkHttp builder:
OkHttpClient client = new OkHttpClient.Builder();
client.addInterceptor(new RequestInterceptor());

get Header in jersey from a GET request

From a js page (in angular) I call a REST request, GET method, were I would to pass an header, this is the function that I call from the REST request:
allstaffworking: function(_getstaff){
var currentToken = _GetToken();
var Headers = {
token: currentToken.stringtoken
};
console.log("idtoken"+Headers);
if (currentToken !== null) {
$http({
method : 'GET',
headers: Headers,
url : REST_URL+'staff/working'
}).then(function successCallback(response) {
_getstaff(response)
}, function errorCallback(response) {
console.log(response.statusText);
});
} else {
console.log("NON SEI LOGGATO!!!");
}
},
Whithout headers: Headers, it works, but I want to pass an important json string: {"idtokenStaff":11,"staffType":{"idstaffType":2,"type":"Dipendente"},"tokenStaff":"88d08m8ve4n8i71k796vajkd01"} in the Headers. I don't know How I can take this string in Jersey. This is java file in with I have the REST method:
#Path("/staff")
public class StaffController {
BaseDao sDao = new StaffDaoImpl();
StaffDao stfDao = new StaffDaoImpl();
TokenStaffDao tsDao = new TokenStaffDaoImpl();
TokenStaff ts = new TokenStaff();
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Staff> getStaff()
{
List<Staff> listOfStaff=sDao.getAll(Staff.class);
return listOfStaff;
}
#GET
#Path("/working")
#Produces(MediaType.APPLICATION_JSON)
#Consumes("application/json")
public List<Staff> getWStaff(#HeaderParam("token") String token) throws JSONException
{
JSONObject jsonObj = new JSONObject(token);
Boolean id = tsDao.getExistence(jsonObj.getInt("idtokenStaff"));
if (id){
List<Staff> listOfWStaff=stfDao.getAllW();
return listOfWStaff;
}
else
return null;
}
}
Taking header from: #HeaderParam("token") String token. How Can I take the element of the header?
A bit late to answer this, but you can also use #Context annotation to get httpheaders.
Eg.
public List<Staff> getWStaff(#Context HttpHeaders httpHeaders) {
String token = httpHeaders.getHeaderString("token");
JSONObject jsonObj = new JSONObject(token);
}

Bonita Web API - 401 Unauthorized Error

I am trying to use Bonita Web API. I My code is below. As you can see I call the loginservice before calling any other API service. It logs in OK 200. But when I make the subsequent call to get the list of processes I get a 401 error. You get a JSESSIONID from the first call and you are suppose to pass it to the subsequent calls to authenticate you.
var baseAddress = new Uri(<base address>);
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
HttpResponseMessage result = client.PostAsync("/bonita/loginservice", new StringContent("login=<username>,password=<password>,redirect=false")).Result;
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage result2 = client.GetAsync("/bonita/API/bpm/process").Result;
result2.EnsureSuccessStatusCode();
}
This works for .Net 2.0 C# but has some interesting things to check.
WebClient wc = new WebClient();
wc.Proxy = WebRequest.GetSystemWebProxy();
//wc.Headers[HttpRequestHeader.AcceptEncoding] = "gzip, deflate";
string strLogin = wc.DownloadString("http://localhost:8080/bonita/loginservice?username=walter.bates&password=bpm&redirect=false");
wc.Headers[HttpRequestHeader.Cookie] = wc.ResponseHeaders[HttpResponseHeader.SetCookie].ToString();
string strCookie = wc.ResponseHeaders[HttpResponseHeader.SetCookie].ToString();
string strProcesses = wc.DownloadString("http://localhost:8080/bonita/API/bpm/process?p=0");
First of all you should know how to determine that the executed operation is successful ( login, getProcesses and whatever) When you try to login you will always get the header (for example "JSESSIONID=50E509D37AC28E2D725CBD45A8112FA7; Path=/bonita; HttpOnly") and OK 200 even if your login attempt in Bonita is unsuccesful.
For the successful login on the previous example
1) You must Pass mandatory form data: username, password and redirect You must also be sure to pass redirect in lower case ."False" will not work, "false" will work. So for .Net suppose you have a property-> Boolean redirect. You must make it lowercase with redirect.ToString().ToLower() cause either way the value will be "False" and you don't want that.
Let's say you try to login only with username and password without passing redirect. the result is that you will get both OK 200 and the header but you will also get a response which is wrong (the response must be empty), so on the next request (i.e getProcesses) you'll get (401) Unauthorized. Guess the results you will have if you pass redirect=False instead of redirect=false. Exactly the same.
2)You must get: strLogin="" // the body of the response must be empty strCookie="JSESSIONID=4F67F134840A2C72DBB968D53772FB22; Path=/bonita; HttpOnly"
For the successful getProcesses on the previous example you pass the header you got from login
wc.Headers[HttpRequestHeader.Cookie] = wc.ResponseHeaders[HttpResponseHeader.SetCookie].ToString();
and then you call the process and get a string in json format for example
"[{\"id\":\"6996906669894804403\",\"icon\":\"\",\"displayDescription\":\"\",\"deploymentDate\":\"2014-11-19 17:57:40.893\",\"description\":\"\",\"activationState\":\"ENABLED\",\"name\":\"Travel request\",\"deployedBy\":\"22\",\"displayName\":\"Travel request\",\"actorinitiatorid\":\"4\",\"last_update_date\":\"2014-11-19 17:57:41.753\",\"configurationState\":\"RESOLVED\",\"version\":\"1.0\"}]"
(or [] which means an empty json)
If the cookie is not passed correctly you will get again 401 error.
Solution for .Net 4.5.1
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web;
namespace BonitaRestApi
{
class BonitaApi
{
private CookieCollection collection;
string strCookietoPass;
string sessionID;
static void Main(string[] args)
{
BonitaApi obj = new BonitaApi();
Task login = new Task(obj.Login);
login.Start();
login.Wait();
Console.ReadLine();
Task GetProcesses = new Task(obj.GetProcesses);
GetProcesses.Start();
GetProcesses.Wait();
Console.ReadLine();
Task logout = new Task(obj.Logout);
logout.Start();
logout.Wait();
Console.ReadLine();
}
public async void Login()
{
const string url = "http://localhost:8080/bonita/";
var cookies = new CookieContainer();
var handler = new HttpClientHandler();
handler.CookieContainer = cookies;
using (var client = new HttpClient(handler))
{
var uri = new Uri(url);
client.BaseAddress = uri;
//client.DefaultRequestHeaders.Accept.Clear();
//client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("username", "helen.kelly"),
new KeyValuePair<string, string>("password", "bpm"),
new KeyValuePair<string, string>("redirect", "false"),
new KeyValuePair<string, string>("redirectUrl", ""),
});
HttpResponseMessage response = await client.PostAsync("loginservice", content);
if (response.IsSuccessStatusCode)
{
var responseBodyAsText = await response.Content.ReadAsStringAsync();
if (!String.IsNullOrEmpty(responseBodyAsText))
{
Console.WriteLine("Unsuccessful Login.Bonita bundle may not have been started, or the URL is invalid.");
return;
}
collection= cookies.GetCookies(uri);
strCookietoPass = response.Headers.GetValues("Set-Cookie").FirstOrDefault();
sessionID = collection["JSESSIONID"].ToString();
Console.WriteLine(string.Format("Successful Login Retrieved session ID {0}", sessionID));
// Do useful work
}
else
{
Console.WriteLine("Login Error" + (int)response.StatusCode + "," + response.ReasonPhrase);
}
}
}
public async void Logout()
{
const string url = "http://localhost:8080/bonita/";
var cookies = new CookieContainer();
var handler = new HttpClientHandler();
handler.CookieContainer = cookies;
using (var client = new HttpClient(handler))
{
var uri = new Uri(url);
client.BaseAddress = uri;
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("redirect", "false")
});
HttpResponseMessage response = await client.PostAsync("logoutservice", content);
if (response.IsSuccessStatusCode)
{
var responseBodyText = await response.Content.ReadAsStringAsync();
if (!String.IsNullOrEmpty(responseBodyText))
{
Console.WriteLine("Unsuccessful Logout.Bonita bundle may not have been started, or the URL is invalid.");
return;
}
Console.WriteLine("Successfully Logged out.");
}
else
{
Console.WriteLine("Logout Error" + (int)response.StatusCode + "," + response.ReasonPhrase);
}
}
}
public async void GetProcesses()
{
var handler = new HttpClientHandler();
Cookie ok = new Cookie("Set-Cookie:",strCookietoPass);
handler.CookieContainer.Add(collection);
using (var client = new HttpClient(handler))
{
var builder = new UriBuilder("http://localhost/bonita/API/bpm/process");
builder.Port = 8080;
var query = HttpUtility.ParseQueryString(builder.Query);
query["p"] = "0";
query["c"] = "10";
builder.Query = query.ToString();
Uri uri= new Uri(builder.ToString());
client.BaseAddress = uri;
HttpResponseMessage response = await client.GetAsync(uri.ToString());
if (response.IsSuccessStatusCode)
{
var responseBodyText = await response.Content.ReadAsStringAsync();
if (String.IsNullOrEmpty(responseBodyText))
{
Console.WriteLine("Unsuccessful GetProcesses.Bonita bundle may not have been started, or the URL is invalid.");
return;
}
Console.WriteLine("Successfully GetProcesses:" + responseBodyText);
}
else
{
Console.WriteLine("GetProcesses Error" + (int)response.StatusCode + "," + response.ReasonPhrase);
}
}
}
}
}
I had the same problem (401 errors) for every single non-GET request.
I finally got through this by looking to the CSRF documentation:
http://documentation.bonitasoft.com/7.4?page=csrf-security
(See the "Is there an impact on REST API calls?" section)
After succesfull login, you have to put a special header in your request:
key: X-Bonita-API-Token
value: the one you got after your login (check the relevant cookie)

Categories