I am using OData together with JQuery. My problem here is that I need to retrieve the Header as I need the Header value. Do you guys know the syntax?
My codes are as follow:
OData.request( {headers: { "X-CSRF-Token" :"Fetch" }, requestUri:queryserviceUriNew, user: uname, password: pword },
function (data, response)
{
//Success Callback (received data is a Feed):
alert("Retrieved.");
alert(response);
// This is the object that hold the response, I need to capture the header value for "x-csrf-token"
}
Thank you experts!
In case there's someone else looking for the answer, the syntax is:
var header_value;
// variable = variable_hold_httpResponse.headers['header_item_that_you_want_to_get'];
header_value = response.headers['x-csrf-token'];
Cheers!
Related
I am very new to this, so please bear with me-- I have currently have an operational google apps script on the backend of a google sheet that is generated from Google Form answers. I am essentially setting up a ticket form in google forms that will trigger the data in the corresponding sheet to be sent via api call to our ticketing system. It works great, but I am trying to optimize it currently. The goal is to take the json response I get using:
Logger.log(response.getContentText());
which provides me the following info:
Aug 9, 2020, 11:44:40 AM Info {"_url":"https://testticketingsystem.com/REST/2.0/ticket/123456","type":"ticket","id":"123456"}
and send another API call to send data to that new ticket.
Here's a code snippet:
var payload = {
"Subject": String(su),
"Content": String(as),
"Requestor": String(em),
"Queue": String(qu),
"CustomFields": {"CustomField1": String(vn), "CustomField2": String(vb), "CustomField3":
String(vg), "CustomField4": String(av), "CustomField5": String(ov), "CustomField6":
String(sd)}
}
var options = {
'method': 'post',
"contentType" : "application/json",
'payload': JSON.stringify(payload),
'muteHttpExceptions': true
}
var url = "https://testticketingsystem.com/REST/2.0/ticket?token=****************";
var response = UrlFetchApp.fetch(url,options);
Logger.log(response.getContentText());
} catch (error) {
Logger.log(error.toString());
}
}
After the ticket is created, how do I script the use of that ID number as a variable into my next api call?
Thank you!
UrlFetchApp.fetch returns a HTTPResponse, and if you expect JSON then you should be able to just use JSON.parse() to create an object from the text. (The JSON object is a standard JavaScript global object like Math; it is not Google Apps Script specific.)
If all goes well, you should just be able to use
var response = UrlFetchApp.fetch(url,options);
var data = JSON.parse(response.getContentText());
var id = data.id;
and then use that id for your next fetch().
Notes
If your literal response is indeed
Aug 9, 2020, 11:44:40 AM Info {"_url":"https://testticketingsystem.com/REST/2.0/ticket/123456","type":"ticket","id":"123456"}
you will run into trouble as everything until the { is invalid JSON (use a linter if you need to check yourself). But I'm assuming that was added by the console when you logged JSON, and not in the actual response itself.
JSON.parse() throws an error with invalid JSON, so you can use try/catch if needed.
You can also check the headers before you try to JSON.parse().
Here's an example that checks and handles issues, should they arise.
var type = response.getHeaders()["Content-Type"];
var text = response.getContentText();
if (type === "application/json") {
try {
var data = JSON.parse(text);
} catch (error) {
return Logger.log("Invalid JSON: " + response.getContentText(text));
}
} else {
return Logger.log("expected JSON, but got response of type: " + type);
}
// if we get to this line, data is an object we can use
As it will become quickly apparent, I have never seriously written a webserver before
Here is the current scenario:
Clients make requests to webserver, asking to save some data
Server looks at payload, and makes 2 checks
a. Is this client banned from saving data?
b. Does the payload of this data pass a language filter?
Server responds with success, or one of those 2 errors
My endpoint is written with Express in TypeScript
class ChatRequest {
public uid: string;
public message: string;
}
export const register = (app: express.Application, deps: dependencies.IDependencies) => {
app.post("/sendChat", (req: express.Request, res: express.Response) => {
transformAndValidate(ChatRequest, req.body)
.then((sendGlobalChatRequest: SendGlobalChatRequest) => {
const payload = {
message: sendGlobalChatRequest.message,
uid: sendGlobalChatRequest.uid
};
//Check if uid is banned here
//Check if payload passes language filter here
//Save Payload here
res.sendStatus(200);
}, (err) => {
deps.logger.error(err);
res.sendStatus(503);
});
});
I have been using this article for reference:
https://hackernoon.com/the-request-sent-bad-data-whats-the-response-94088bd290a
But I think my conclusion is that they are discussing something slightly different.
So from my understanding, I can just make up HTTP codes...
so I could just do res.sendStatus(499); if the uid is banned, and maybe res.sendStatus(498); if the payload doesn't pass language filter
Then my client can just read the Int statusCode and quickly determine the failure.
But even though I think I can do that, and it would work, it doesn't seem right?
Should I instead be using a standard HTTP Response Code? https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
And then add in the body of the response, a String or something that my client can parse to determine the error?
The String parsing seems way harder to maintain, but technically seems more "legal" if that makes sense?
What is the best way for me to have a client determine the type of server-side error?
I decided to return 400 with a JSON mapping errors to bools
if (isProfane(message)) {
res.status(400).json({messageContentBlocked: true});
}
In this way the client can receive multiple errors for the request at once, and it's more explicit
And in case anyone is googling around, I am using RxSwift/RxCocoa
Here is how I handle the error on the client:
extension Error {
var chatMessageBlockedURLError: Bool {
guard let rxCocoaURLError = self as? RxCocoaURLError else {return false}
switch rxCocoaURLError {
case let .httpRequestFailed(response, data):
guard response.statusCode == 400, let data = data else {return false}
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .millisecondsSince1970
guard let errors = try? decoder.decode([String:Bool].self, from: data) else {return false}
return errors["messageContentBlocked"] == true
default:
return false
}
}
}
My API doesn't return any response in body on POST,PATCH, etc for some resources.
How are we supposed to handle this case in data provider ?
I tried to return an empty data object but unfortunately I get the error :
"The response to 'CREATE' must be like { data: ... }, but the received response does not have a 'data' key. The dataProvider is probably wrong for 'CREATE'."
I don't know how your dataProvider looks like or what the response of your API for POST/PUT requests is, but the problem is probably located in your dataProvider's convertHTTPResponseToREST as this is responsible for handling your API's responses.
Try the following:
case CREATE:
return { data: { ...params.data, id: json.id } };
If this doesn't work either, then try this one:
case CREATE:
return { data: json };
I've got a dnn module that I've created that has no problem retrieving data from the server using the WebAPI. However, when I try to perform an update, I continue to get errors no matter what I try. Viewing in Firebug, I can the data being posted:
!(http://www.virtual-essentials.com/personal/firebug1.png)
But, when I debug, I see nothing but default (null.setnull) values (bool = false, int = 0, string= empty).
Then, of course the response is no good and results in this:
!(http://www.virtual-essentials.com/personal/firebug2.png)
I've tried every combination of things I can think of, but cannot get this figured out and I'm going nuts.
Here is the ajax call:
//U//operation - update
self.updateTestimonial = function (objItem) {
var method = "UpdateTestimonial";
self.UpdatePublishStatus(objItem);
$.ajax({
type: "POST",
contentType: "application/json",
url: sf.getServiceRoot('VirtualEssentials/Testimonials') + "TestimonialWeb/" + method,
beforeSend: sf.setModuleHeaders,
data: ko.toJSON({ item: new TestimonialInfoDTO(objItem.TestimonialID(), objItem.IsPublished())}),
success: function (data) {
self.userAlertText(objItem.Testimonial() + ' changes have been saved');
//self.getTestimonials(); //get the new data after updates
},
error: function (xhr, status, error) {
self.userAlertText('OOPS! Error updating ' + objItem.Testimonial + '<br />' + (error));
//put the status back
self.UpdatePublishStatus(objItem);
}
});
};
Here is the WebAPI Controller call:
[HttpPost]
[DnnModuleAuthorize(AccessLevel = SecurityAccessLevel.Edit)]
public System.Net.Http.HttpResponseMessage UpdateTestimonial(TestimonialInfoDTO item)
{
try
{
Controller.TestimonialController objController = new Controller.TestimonialController();
objController.PublishTestimonial(item.TestimonialID, item.IsPublished);
}
catch (HttpResponseException ex)
{
return ex.Response;
}
}
Any help on this is sincerely appreciated!!
It never fails that you find a solution as soon as you ask for help. I pulled the source code for the core Journal module and saw that they were posting the post data a bit differently. I modified the json string that I was sending and it all went through. I thought since the parameter for the method was:
UpdateTestimonial(TestimonialInfoDTO item)
that it was expecting:
{"item":{"TestimonialID":14,"IsPublished":false}}
but, it turns out what it really wants is:
{"TestimonialID":14,"IsPublished":false}
Anyhow, here is what I did to make the changes; I just changed the way I built the data to post.
var data = new TestimonialInfoDTO(objItem.TestimonialID(), objItem.IsPublished());
$.ajax({
...
data: ko.toJSON(data),
I hope this helps someone. There isn't a lot of Dnn WebAPI documentation out at this point.
Thanks!
Briana
I'd like to use custom headers to provide some more information about the response data. Is it possible to get the headers in a response from a dojo datagrid hooked up to a jsonRest object via an object store (dojo 1.7)? I see this is possible when you are making the XHR request, but in this case it is being made by the grid.
The API provides an event for a response error which returns the response object:
on(this.grid, 'FetchError', function (response, req) {
var header = response.xhr.getAllResponseHeaders();
});
using this I am successfully able to access my custom response headers. However, there doesn't appear to be a way to get the response object when the request is successful. I have been using the undocumented private event _onFetchComplete with aspect after, however, this does not allow access to the response object, just the response values
aspect.after(this.grid, '_onFetchComplete', function (response, request)
{
///unable to get headers, response is the returned values
}, true);
Edit:
I managed to get something working, but I suspect it is very over engineered and someone with a better understanding could come up with a simpler solution. I ended up adding aspect around to allow me to get hold of the deferred object in the rest store which is returned to the object store. Here I added a new function to the deffered to return the headers. I then hooked in to the onFetch of the object store using dojo hitch (because I needed the results in the current scope). It seems messy to me
aspect.around(restStore, "query", function (original) {
return function (method, args) {
var def = original.call(this, method, args);
def.headers = deferred1.then(function () {
var hd = def.ioArgs.xhr.getResponseHeader("myHeader");
return hd;
});
return def;
};
});
aspect.after(objectStore, 'onFetch', lang.hitch(this, function (response) {
response.headers.then(lang.hitch(this, function (evt) {
var headerResult = evt;
}));
}), true);
Is there a better way?
I solved this today after reading this post, thought I'd feed back.
dojo/store/JsonRest solves it also but my code ended up slightly different.
var MyStore = declare(JsonRest, {
query: function () {
var results = this.inherited(arguments);
console.log('Results: ', results);
results.response.then(function (res) {
var myheader = res.xhr.getResponseHeader('My-Header');
doSomethingWith(myheader);
});
return results;
}
});
So you override the normal query() function, let it execute and return its promise, and attach your own listener to its 'response' member resolving, in which you can access the xhr object that has the headers. This ought to let you interpret the JsonRest result while fitting nicely into the chain of the query() all invokers.
One word of warning, this code is modified for posting here, and actually inherited from another intermediary class that also overrode query(), but the basics here are pretty sound.
If what you want is to get info from the server, also a custom key-value in the cookie can be a solution, that was my case, first I was looking for a custom response header but I couldn't make it work so I did the cookie way getting the info after the grid data is fetched:
dojo.connect(grid, "_onFetchComplete", function (){
doSomethingWith(dojo.cookie("My-Key"));
});
This is useful for example to present a SUM(field) for all rows in a paginated datagrid, and not only those included in the current page. In the server you can fetch the COUNT and the SUM, the COUNT will be sent in the Content-Range header and the SUM can be sent in the cookie.