Groovy : Cannot set a request body for a DELETE/GET method - groovy

When i am executing this via curl its working :
curl -u -X DELETE -H 'accept:application/json' http://localhost:13000/test/test_userid"
I made a common function which accept methodtype ( GET, POST, DELETE etc) and content type( JASON, TEXT ) for the httpbuilder.
def public httpRequest(String url, String content, Method requestType, ContentType contentType)
{
try{
def myClient = new HTTPBuilder(url)
myClient.request(requestType,contentType) { req ->
headers.'Content-Type' = 'application/json'
body=content
response.success = { resp, data ->
def reponse=[resp:resp,data:data]
return reponse
}
response.failure = { resp ->
println 'Response Code '+resp.statusLine
}
// called only for a 404 (not found) status code:
response.'404' = { resp ->
println 'Not found'
}
}
}
catch(Exception e)
{
println "error"+e.getProperties()
}
}
Now if i make a POST request , its working.
However if i make a GET or DELETE request using
def response = httpRequest(url,"",DELETE,JSON)
or
def response = httpRequest(url,"",GET,TEXT)
its shows the following error :-
error[message:Cannot set a request body for a DELETE/GET method, class:class java.lang.IllegalArgumentException
Do i need to make a separate function for GET/DELETE?
because
myClient.request(requestType) { req ->
headers.'Content-Type' = 'application/json'
body=content
response.success = { resp, data ->
def reponse=[resp:resp,data:data]
return reponse
}
response.failure = { resp ->
println 'Response Code '+resp.statusLine
}
// called only for a 404 (not found) status code:
response.'404' = { resp ->
println 'Not found'
}
}
}
WORKS

Delete and Get wont accept Body , hence the solution is to make a check and execute accordingly
if(requestType.equals(DELETE)||requestType.equals(GET))
{
try{
def myClient = new HTTPBuilder(url)
myClient.request(requestType) { req ->
headers.'Content-Type' = 'application/json'
headers.Accept = 'application/json'
response.success = { resp, data ->
def reponse=[resp:resp,data:data]
return reponse
}
response.failure = { resp ->
println 'Response Code '+resp.statusLine
}
// called only for a 404 (not found) status code:
response.'404' = { resp ->
println 'Not found'
}
}
}
catch(Exception e)
{
println "error"+e.getProperties()
}
}
else
<---post request -->

Related

SOAP with Groovy script answers with 'ERROR' while PUT

I have two same paths in my SOAP project, both of them use the same script
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
class Response {
Integer code
String message
String accountId
}
// Match based on body
try {
def requestBody = mockRequest.getRequestContent()
def requestJson = new JsonSlurper().parseText(requestBody)
def phone = requestJson.phone as String
def email = requestJson.email as String
def accountId = requestJson.accountId as String
def response
if (phone == null || phone == "") {
response = new Response(code: 1011, message: 'Field phone is required')
} else if (phone == "+79777777777") {
response = new Response(code: 1021, message: 'The phone field must be in the format +79991112233')
} else if (email == "testt#testt.com") {
response = new Response(code: 1022, message: 'The email field must be in the format test#test.com')
} else {
response = new Response(code: 0, message: 'Success', accountId: accountId)
}
requestContext.response = JsonOutput.toJson(response).toString()
return "response"
} catch (Exception ex) {
requestContext.response = JsonOutput.toJson(new Response(code: 500, message: 'Emulator error: ' + ex.message)).toString()
return "response"
}
My problem is in sending a request to PUT method with the script. The request is
{
"phone":"+79987654125",
"email":"test#notify1.com",
"accountId":"12345"
}
If I send POST, everything is okay
{
"message": "Success",
"code": 0,
"accountId": "12345"
}
If I send PUT, in a response I will see
{
"message": "Emulator error: Text must not be null or empty",
"code": 500,
"accountId": null
}
From the logs I see that the PUT script got requestContext as 'null'. What can be a problem?

Fetching data from Azure Cosmos DB in Flutter?

I wrote a function to fetch data using query from Azure Cosmos Database in Flutter/Dart, however it's giving error :
response.body: {"code":"BadRequest","message":"Message: {\"Errors\":[\"The input content is invalid because the required properties - 'æ©; ' - are missing\"]}\r\nActivityId: f75a0c6e-2c8d-4f13-a020-6e3c13fa5458, Request URI: /apps/f4533d11-81e3-4512-b639-0f0475c10611/services/401c9130-a85e-46a6-8311-c2dc8e5070d6/partitions/b5d2a58d-1304-414b-92c7-10e7fa95f679/replicas/131768862196689298p, RequestStats: , SDK: Microsoft.Azure.Documents.Common/2.0.0.0"}
I/flutter ( 5284): response.status: 400
Here is my piece of code
final response = await http.post(
endpointResource,
// Query
body: query,
// Post new message
headers: {
HttpHeaders.AUTHORIZATION: authToken,
HttpHeaders.CONTENT_TYPE: "application/query+json",
//'content-type': 'application/json',
'Accept': 'application/json',
//c.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
'x-ms-version': '2017-02-22',
'x-ms-date': date,
'x-ms-documentdb-isquery': 'true',
'x-ms-query-enable-crosspartition': 'true',
'x-ms-documentdb-query-enable-scan': 'true',
},
);
What should I do to get data back?
I managed to translate a code snippet from a working example in Javascript to Dart.
import 'dart:io';
import 'dart:convert';
import 'dart:async';
import 'dart:core';
import 'package:crypto/crypto.dart';
import 'package:hex/hex.dart';
class Cosmos{
String documentDBMasterKey;
Cosmos({this.documentDBMasterKey});
Future queryCosmos({url, method, body}) async{
String auth;
String documentDBMasterKey = this.documentDBMasterKey;
print("mastKey: $documentDBMasterKey");
method = method.trim(); //GET, POST, PUT or DEL
url = url.trim();
String utcString = HttpDate.format(DateTime.now());
print('RFC1123time: $utcString');
print('request url = ' + url);
String strippedurl =
url.replaceAllMapped(RegExp(r'^https?://[^/]+/'), (match) {
return '/';
});
print('stripped Url: $strippedurl');
List strippedparts = strippedurl.split('/');
int truestrippedcount = strippedparts.length - 1;
print('$truestrippedcount');
String resourceId;
String resType;
if (truestrippedcount % 2 != 0){
print('odd');
resType = strippedparts[truestrippedcount];
print('$resType');
if (truestrippedcount > 1){
int lastPart = strippedurl.lastIndexOf('/');
resourceId = strippedurl.substring(1, lastPart);
print('ResourceId: ' + resourceId);
}
}
else{
print('even');
resType = strippedparts[truestrippedcount -1];
print('resType: $resType');
strippedurl = strippedurl.substring(1);
print('strippedurl $strippedurl');
resourceId = strippedurl;
print('ResourceId: ' + resourceId);
}
String verb = method.toLowerCase();
String date = utcString.toLowerCase();
Base64Codec base64 = const Base64Codec();
var key = base64.decode(documentDBMasterKey); //Base64Bits --> BITS
print('key = ${HEX.encode(key)}');
print('masterKey = $documentDBMasterKey');
String text = (verb ?? '').toLowerCase() + '\n' +
(resType ?? '').toLowerCase() + '\n' +
(resourceId ?? '') + '\n' +
(date ?? '').toLowerCase() + '\n' +
'' + '\n';
print('text: $text');
var hmacSha256 = Hmac(sha256, key);
List<int> utf8Text = utf8.encode(text);
var hashSignature = hmacSha256.convert(utf8Text);
String base64Bits = base64.encode(hashSignature.bytes);
//Format our authentication token and URI encode it.
var masterToken = "master";
var tokenVersion = "1.0";
auth = Uri.encodeComponent('type=' + masterToken + '&ver=' + tokenVersion + '&sig=' + base64Bits);
print('auth= $auth');
Map<String, String> headers = {
'Accept': 'application/json',
'x-ms-version': '2016-07-11',
'Authorization': auth,
'x-ms-date': utcString,
'x-ms-documentdb-isquery' : 'true',
'Content-Type' : 'application/query+json',
'x-ms-documentdb-query-enablecrosspartition' : 'true',
};
Future<String> readResponse(HttpClientResponse response) {
final completer = Completer<String>();
final contents = StringBuffer();
response.transform(utf8.decoder).listen((data) {
contents.write(data);
}, onDone: () => completer.complete(contents.toString()));
return completer.future;
}
HttpClientRequest request;
HttpClient httpClient = new HttpClient();
if (method=='GET'){
request = await httpClient.getUrl(Uri.parse(url));
}
else if(method=='POST'){
request = await httpClient.postUrl(Uri.parse(url));
}
else if(method=='PUT'){
request = await httpClient.putUrl(Uri.parse(url));
}
else if(method=='DEL'){
request = await httpClient.deleteUrl(Uri.parse(url));
}
headers.forEach((key, value) {
request.headers.set(key,value);
});
if(body != null) {
request.add(utf8.encode(json.encode(body)));
}
HttpClientResponse aresponse = await request.close();
httpClient.close();
String aresponseString = await readResponse(aresponse);
return jsonDecode(aresponseString);
}
}
Just instantiate the class with your Cosmos Master Key:
Cosmos cosmos = Cosmos( documentDBMasterKey:'{your_cosmos_db_master_key}');
Query Cosmos Db by calling queryCosmos method. Pass 'url', 'method' and an optional 'body' as parameters:
// GET Example
() async {
Map<String, dynamic> get_dbs = await cosmos.queryCosmos(
url: 'https://{your_base_url}.documents.azure.com:443/dbs', method: 'GET');
print(get_dbs);
}
// POST Example (Query)
() async {
final Map<String, dynamic> body = {
"query":
"SELECT * FROM Faults f WHERE f.FaultId = #faultId",
"parameters": [
{"name": "#faultId", "value": 306000}
]
};
Map<String, dynamic> get_fault = await cosmos.queryCosmos(
url:
'https://{your_base_url}.documents.azure.com:443/dbs/{your_db}/colls/{your_collection}/docs',
method: 'POST',
body: body);
print('Get fault $get_fault');
}
Original code (JavaScript - Download to Postman and check pre-req script): https://github.com/MicrosoftCSA/documentdb-postman-collection
My code (dart): https://github.com/fvarela/cosmos_db_dart
I've created a Dart library to fetch data from a CosmosDB. For example to fetch documents from a collection you can just call this function:
import 'package:cosmosdb/cosmosdb.dart';
void main() {
final cosmosDB = CosmosDB(
masterKey: '<YOUR_MASTER_KEY>',
baseUrl: '<YOUR_BASE_URL>',
);
// get all documents from a collection
final documents = cosmosDB.documents.list('<YOUR_DATABASE>', '<YOUR_COLLECTION>');
print(documents);
}
GitHub: https://github.com/jonasfranz/cosmosdb
Package: https://pub.dev/packages/cosmosdb

aws lambda call not populating body on POST

Thanks to help from EVK on my previous Q (can use API GET but not API POST) I was able to resolve the problem of not being able to make an API POST request from an aws lambda in node, when I could hit the GET. The problem was not populating post_options.
Anyway, now I am able to successfully call the post but I cant get the body populated.
related documentation https://nodejs.org/api/http.html#http_http_request_options_callback
If you see my API POST call below.
//// POST api/<controller>
public string SapEaiCall([FromBody]string xmlFile)
{
string responseMsg = "Failed Import Active Directory User";
if (string.IsNullOrEmpty(xmlFile))
{
responseMsg = "XML file is NULL";
}
if (responseMsg != "XML file is NULL")
{
xmlFile = RemoveFirstAndLastQuotes(xmlFile);
if (!IsNewestVersionOfXMLFile(xmlFile))
{
responseMsg = "Not latest version of file, update not performed";
}
else
{
Business.PersonnelReplicate personnelReplicate = BusinessLogic.SynchronisePersonnel.BuildFromDataContractXml<Business.PersonnelReplicate>(xmlFile);
bool result = Service.Personnel.SynchroniseCache(personnelReplicate);
if (result)
{
responseMsg = "Success Import Sap Cache User";
}
}
}
return "{\"response\" : \" " + responseMsg + " \" , \"isNewActiveDirectoryUser\" : \" false \"}";
}
Every time I call it from the aws lambda, it returns responseMsg = "XML file is NULL";
Please see my example below:
var querystring = require('querystring');
var https = require('https');
var fs = require('fs');
exports.handler = function(event, context) {
const post_data = querystring.stringify({'msg': 'Hello World!'});
// An object of options to indicate where to post to
var post_options = {
host: 'URL',
protocol: 'https:',
port: '443',
path: '/api/SyncPersonnelViaAwsApi/SapEaiCall',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': post_data.length
}
};
//ignores SSL
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var post_request = https.request(post_options, function(res) {
var body = "";
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
context.done(body);
});
res.on('error', function(e) {
context.fail('error:' + e.message);
});
});
// post the data
post_request.write(post_data);
post_request.end();
console.log("posted data " +post_data); //returns posted data msg=Hello%20World!
};
So I have populated post data, and also tried populating the body. Still returns XML file is NULL
Does anyone please have any idea why?
Thanks
You are sending the following text in body:
msg=Hello%20World!
And you state that request content type is:
'Content-Type': 'application/json'
Does your body represents valid json? It doesn't. So that's first problem - content type stated in your request and actual data your send do not match each other.
Then let's look at:
public string SapEaiCall([FromBody]string xmlFile)
It basically says: look at the body of request and use binder appropriate for request content type to bind value of xmlFile parameter. Since request content type is "application/json" - binder expects body to contain one single json string, that is, body in such case should be (including quotes):
"Hello World!"
So if you pass that (for example via const post_data = JSON.stringify('Hello World!'); - it should work.
But then what if you want to pass more parameters in your body, not just xmlFile? Then you need to define a model, and I'd say even if you have just one parameter - it's better to do that. For example:
public class SapEaiCallParameters {
public string XmlFile { get; set; }
}
// FromBody can be omitted in this case
public IHttpActionResult Post(SapEaiCallParameters parameters) {
}
And then you call it as you would expect, passing json:
const model = {xmlFile: 'Hello World!'};
const post_data = JSON.stringify(model);
Side note: don't do this:
return "{\"response\" : \" " + responseMsg + " \" , \"isNewActiveDirectoryUser\" : \" false \"}";
Instead, create another model, like this:
public class SapEaiCallResponse {
public string Response { get; set; }
public bool IsNewActiveDirectoryUser { get; set; }
}
And return it:
public IHttpActionResult Post(SapEaiCallParameters parameters) {
...
return Json(new SapEaiCallResponse {
Response = responseMsg,
IsNewActiveDirectoryUser = false,
});
}

Uploading Image to Node.js express server Swift

I am currently creating an app in which I will send an image from an iOS device to my expressjs server, using the multer middleware. I have the server side set up, yet I believe I have an error on the client side, because I cannot get my POST request to work correctly. Here is my swift code.
class func changeChannelImage(handle: String, imageURL: URL, completionHandler: #escaping (Int?, Error?) -> Void){
let baseURL = "http://10.0.0.220:3000/channel/channelImage?handle=\(handle)"
func createRequestBodyWith(parameters:[String:NSObject], boundary:String) -> Data{
var body = Data()
for (key, value) in parameters {
body.appendString(string: "--\(boundary)\r\n")
body.appendString(string: "Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString(string: "\(value)\r\n")
}
body.appendString(string: "--\(boundary)\r\n")
var mimetype = "image/png"
let defFileName = "channelImage.png"
print(imageURL.absoluteString)
do {
var data = try Data(contentsOf: imageURL) //Image file URL in device's directory
var image = UIImage(data: data)
let imageData = UIImagePNGRepresentation(image!)
body.appendString(string: "Content-Disposition: form-data; filename=\"\(defFileName)\"\r\n")
print("Content-Type: \(mimetype)\r\n\r\n")
body.appendString(string: "Content-Type: \(mimetype)\r\n\r\n")
body.append(imageData!)
body.appendString(string: "\r\n")
body.appendString(string: "--\(boundary)--\r\n")
}
catch {
print(error.localizedDescription)
}
return body
}
func generateBoundaryString() -> String {
return "Boundary-\(NSUUID().uuidString)"
}
var request = URLRequest(url: URL(string: baseURL)!)
request.httpMethod = "POST"
let boundary = generateBoundaryString()
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.httpBody = createRequestBodyWith(parameters: [:], boundary: generateBoundaryString())
let session = URLSession.shared
let task = session.dataTask(with: request) { (data, response, error) in
if error == nil {
let res = response as! HTTPURLResponse
let code = res.statusCode
if code != 200 {
print(String(data: data!, encoding: .utf8))
}
DispatchQueue.main.async {
completionHandler(code, nil)
}
}
else {
print(error!)
DispatchQueue.main.async {
completionHandler(0, error)
}
}
}
task.resume()
}
Does anyone have any idea why the image file cannot be read properly by the server? Any help would be appreciated.
Convert your image data into base64 String and then append it into body

TypeError: Cannot read property 'save' of null

I'm trying to PUT in some data to a small node server I've written.
The code on the server side is as follows:
router.route('/returnLockID').put(function(req, res){
mongoOp.findOne({
name: req.body.name
}, function(err, user) { if(err) {
response = {"error" : true,"message" : "Error fetching data"};
} else {
// we got data from Mongo.
// change it accordingly.
if(req.body.LockID !== undefined) {
// case where ID needs to be updated.
user.LockID = req.body.LockID;
}
// save the data
user.save(function(err){
if(err) {
response = {"error" : true,"message" : "Error updating data"};
} else {
response = {"error" : false,"message" : "Data is updated for "+req.body.name};
}
res.json(response);
})
}
});
})
I get the response:
{
"error": false,
"message": "Data is updated for nv942"
}
However, the data isn't updated. Can anyone see where I'm going wrong with the saving?
It all goes through fine when I PUT using postman, i can save, however, when I try PUT from iOS I get:
TypeError: Cannot read property 'save' of null
at /Users/NikhilVedi/Documents/FYP/Server/lockserver/routes/users.js:92:13
at Query.<anonymous> (/Users/NikhilVedi/Documents/FYP/Server/lockserver/node_modules/mongoose/lib/model.js:3407:16)
at /Users/NikhilVedi/Documents/FYP/Server/lockserver/node_modules/kareem/index.js:259:21
at /Users/NikhilVedi/Documents/FYP/Server/lockserver/node_modules/kareem/index.js:127:16
at _combinedTickCallback (internal/process/next_tick.js:67:7)
at process._tickCallback (internal/process/next_tick.js:98:9)
The swift code is:
#IBAction func setup(_ sender: Any) {
if (UserDefaults.standard.value(forKey: "userIP") == nil)
{
//make this a message box and stop the program crashing by assigning user defaults a value
UserDefaults.standard.set("localhost", forKey: "userIP")
print("Local host programatically set");
}
let u = UserDefaults.standard.value(forKey: "userIP")!
let name = UserDefaults.standard.value(forKey: "email")!
var request = URLRequest(url: URL(string: "http://\(u):3000/users/returnLockID")!)
request.httpMethod = "PUT"
let postString = "LockID=\(LockID.text!)name=\(name)"
print(postString)
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
let responseString = String(data: data, encoding: .utf8)
// print("responseString = \(responseString)")
if let data = responseString?.data(using: String.Encoding.utf8) {
let resString = JSON(data: data)
if resString["success"].stringValue == "true"
{
print("works");
}
else if resString["success"].stringValue == "false"
{
print("failed")
print(resString["message"].stringValue)
//dismiss window and set bool to true
UserDefaults.standard.set(true, forKey: "LockIDPresent")
self.dismiss(animated: true, completion: nil);
}
}
}
task.resume()
}
Thanks in advance!
One error I see, but not likely the fix to your issue is that you should be doing res.json(response); in the first if statement clause.
Another thing to notice is that you are calling save regardless of whether or not a req.body.LockID value is provided, so in cases where it isn't the user is saved without any modification. You might want to print out the value or req.body.LockID, as that might be the reason that the user email is not being updated.

Resources