Configure Firebase Function to accept HAL+JSON as request body - firebase

When using HTTP-triggered Functions in Firebase, I'm unable to access a HAL+JSON payload from the HTTP request.
The request looks like:
POST /endpoint
Content-Type: application/hal+json
{ /* some payload */ }
In my function I'm accessing the payload using request.body, like this:
...
var myPayload = request.body;
...
The value of myPayload is:
{}
Instead, when the HTTP request contains Content-Type: application/json, the value of myPayload is the correct one:
{ /* some payload */ }
I believe it's related to the following: express.json()
Is there a way to configure express in Firebase to include application/hal+json? For example:
express.json({
type: [ 'application/json', 'application/hal+json' ]
});
Or to access the raw body myself?
Note: I don't want to create an express app within the function.

Please refer to the documentation for understanding how Cloud Functions automatically processes incoming requests.
Cloud Functions parses request body content types of application/json
and application/x-www-form-urlencoded according to the rules described
above. Plain text content types (text/plain) are passed through as
strings using UTF-8 as a default encoding (or a custom encoding
provided in the content-type header).
For other content types, the rawBody property contains the unparsed
bytes of the request body as a Buffer object.
Your "other content type" here probably means that your raw request body will be available from the Request object in the rawBody property.

Related

IntelliJ HTTP Client - using a variable set from one POST request in a following JSON body of another POST request

In HTTP client Editor on IntelliJ, I am trying to do this:
POST http://{{host}}/path-to-my-first-resource
Content-Type: application/json
{"field1":"false",
"field2":"test",
"field3":"test",
}
I got the result of the above POST with this:
> {%
client.global.set("my-first-returned-var", response.body.json.var1);
client.global.set("my-second-returned-var", response.body.json.var2);
client.global.set("my-third-returned-var", response.body.json.var3);
%}
The variables have been returned with success. Thus, I am trying to use those variables in a subsequent POST, like that:
POST http://{{host}}/path-to-my-second-resource
Content-Type: application/json
{"anotherfield1":"{{my-first-returned-var}}",
"justanother":"{{my-second-returned-var}}"
}
I tried to send the variables in the body with quotes and without quotes, but IntelliJ did not translate the variables inside double curlies. I can use the variables on HTPP Header, like:
GET https://my-request
Authorization: Bearer {{my-first-returned-var}}
But I could not use these variables in a JSON body.
The API that I'm trying to use is a Spring Boot REST Controller that use Jackson lib to deserialize the body from a request into a Java Object. The error message that Spring Boot return is something like that:
JSON parse error: Cannot deserialize instance of java.lang.String out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException
I know you asked this several months ago, but have you tried this:
client.global.set("my-first-returned-var", JSON.stringify(response.body.json.var1));

HTTP request header field "optdata"

So, I googled a lot but couldn't find a HTTP request header field called "optdata". I am working on allowing the user to watch DRM protected videos on chromecast device. I am following a document from drmtoday.com which says:
For Widevine, the metadata must be carried inside the “optdata” field,
inside the HTTP request header with name “dt-custom-data” or
“x-dt-custom-data”.
I understand that the header key name is "dt-custom-data" but couldn't find any reference explaining what exactly is optdata.
If I just encode the following data to base64 and pass it as 'dt-custom-data' header, the request fails saying that 'HTTP Status 412 - Precondition failed'.
{
"userId":"12345",
"sessionId":"RWFzdGVyZWdn",
"merchant":"a-merchant"
}
It just means that dt-custom-data and x-dt-custom-data headers support key/value pairs. optdata is a recognised key.
E.g.
dt-custom-data: optdata={ "userId":"12345", "sessionId":"RWFzdGVyZWdn", "merchant":"a-merchant" }

How do HTTP conversations flow?

I know that clients and servers most commonly communicate through HTTP forms. I know enough about the GET methods, but the little I do know about POST methods is that they're used to submit data to the server. However, when the server receives a POST method and processes the data, it sends a status code like 200 etc., but how does the server send more data? I know there's a body but what does it look like? Does it have parameters and values just like a POST method?
The format of the body is specified in the Content-Type header.
A commonly used Content-Type for form data submission is application/x-www-form-urlencoded. The body for such a request should look something like this:
key1=value1&key2=value+with+spaces
Where key1 and key2 are input names and value1 and value+with+spaces are the corresponding values. Note that key names and values are url encoded
Another common type is application/json, which means the request body should be interpreted as JSON data, for example:
{
"key": "value",
"another_key": "value"
}
So it's just data, and the Content-Type header tells the server how to interpret it.
Edit: A good way to see what's going on is create a form with method="post", and analyze the request with your browser developer tools when the form is submitted.

Angular: Custom headers are ignored by $http and $resource. Why?

I'm trying to access a REST service I don't control. First problem is that the service doesn't include a Access-Control-Allow-Origin header, which is a problem that, if I understand correctly, immediately limits me to JSONP.
Also, by default, this service sends XML rather than JSON, though it's capable of sending JSON. I think it should respond to my Accept header, the people responsible for the service say it looks at my Content-Type. That would mean I'd need to do a POST rather than a GET (though get makes more sense when I'm just getting some static data, right?).
Stubborn as I am, I'm trying my Accept header first. Since Angular only accepts JSON, I'd expect it to use the Accept: application/json header by default, but it doesn't, and it ignores my attempts to set it manually:
app.config(['$httpProvider', function($httpProvider){
console.log($httpProvider.defaults.headers.common);
delete $httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.defaults.headers.post['Accept'] = 'application/json, text/javascript';
$httpProvider.defaults.headers.post['Content-Type'] = 'application/json; charset=utf-8';
$httpProvider.defaults.headers.post['Access-Control-Max-Age'] = '1728000';
$httpProvider.defaults.headers.common['Access-Control-Max-Age'] = '1728000';
$httpProvider.defaults.headers.common['Accept'] = 'application/json, text/javascript';
$httpProvider.defaults.headers.common['Content-Type'] = 'application/json; charset=utf-8';
$httpProvider.defaults.useXDomain = true;
}]);
I do this again in the actual resource:
return $resource('http://foo.com/getStuff', {}, {
fetch: {
method:'JSONP',
params: params,
headers: {
'Accept':'application/json, text/javascript',
'Content-Type':'application/json; charset=utf-8'
},
isArray:false,
callback: 'JSON_CALLBACK'
}
});
But still, the request headers contain Accept: */*.
My question is: WHY? Why does Angular ignore my headers? And how do I get it to use the proper headers anyway?
And also: is there a way to use JSONP in a POST?
Edit: Originally I used Angular 1.0.7, but I just tried it with 1.2.3 and got the same results. Headers are ignored, yet everybody claims that this is the way to do it.
I also tried doing it directly with $http, rather than with $resource, with the same results.
Edit 2: Here's a JSFiddle. It's anonymized and doesn't use my real server, but using Firebug/developer tools, you can verify that it sends Accept: */* on both calls, despite my many attempts to set application/json headers. And that is my real problem here. On my real server, I'm getting an XML result because of that, despite my real server's ability to send JSON.
(Whether the real server supports jsonp is less relevant at the moment. This dummy server clearly doesn't, but that's okay. I just care about the headers.)
Edit 3: I've tried both solutions suggested below:
$http.defaults.headers.common['Accept'] = 'application/json, text/javascript';
$http.defaults.transformRequest.push(function (data, headersGetter) {
headersGetter().Accept = "application/json, text/javascript";
return data;
});
I've tried both statements separately. In the controller, and then in the service just before the http call itself. Still doesn't work.
Can someone give me a JsFiddle where this is shown to work?
Edit 4: I notice that when I use GET rather than JSONP, the Accept header is correct. But then the response is rejected because it doesn't have the correct header.
What kind of headers should a JSONP call have? Because there's a lot more headers in the JSONP call, but nothing that identifies it as JSONP. Does the server have to have explicit JSONP support for this to work? I suddenly realize I don't know nearly enough about jsonp.
I think your answer is here. According to the wiki, A JSONP call is executed through injection of a <script> tag to load the script from the host server, which responds by calling your callback, passing the data. A <script> tag generates a regular browser request (not an XmlHttpRequest), and the browser will send its own Accept header (it also sends its own User-Agent header, for example).
I would hope there is an easier client-side way to do this, but I think the only way may be the one suggested in the referenced post:
So, if you want to be able to set request headers for cross domain calls
you will have to setup a server side script on your domain that will
delegate the call to the remote domain (and set the respective
headers) and then send the AJAX request to your script.
EDIT: here is a (rejected) jQuery bug report about this same problem.
Some more background info:
In angular, callbacks are managed automagically, so if your say this:
$http({
method: "JSONP",
url: "http://headers.jsontest.com?callback=JSON_CALLBACK",
}).success(function(data) {
console.log('Return value:');
console.log(data);
}).error(function(data) {
console.log('Error!');
console.log(data);
})
a <script> tag will be created that looks more or less like this:
<script type="application/javascript"
src="http://headers.jsontest.com/?callback=angular.callbacks._1">
</script>
The content of the response to http://headers.jsontest.com/?callback=angular.callbacks._1 will be:
angular.callbacks._1({key1: "value1", key2: "value2"});
angular.callbacks._1 will contain your success function, and it will be called with the data.
While what you have is supposed to work according to the docs, my experience has been a bit different. To get around this issue, we did the following:
Create a "base controller" that gets added to the page either on the body or html tag.
In that controller, make the assignment using $http instead of $httpProvider. Because your base controller loads when the initial page loads, it is there for all other controllers and services that will run in your app.
I don't know why this works and the proscribed method does not, and I'd love to see an answer to your question that is better than this work-around, but at least this can get you moving forward with development again.
The following works for me - however, I do that during "runtime" with $http and I am not using $httpProvider during bootstrapping.
function SomeCtrl($http) {
$http.defaults.transformRequest.push(function (data, headersGetter) {
headersGetter().Accept = "application/json, text/javascript";
return data;
});
}
Edit
Here is a working jsFiddle version. Check the request which is done with Developer Tools/Firebug and see that "application/json, text/javascript" is requested.

text/html vs application/x-www-form-urlencoded and FormUrlEncodedMediaTypeFormatter

For a POST request, I got back a response in text/html format and the response body was containing the below info:
oauth_token=XXXXXXXXXXXXXXXXXXXXXXXXX&oauth_token_secret=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX&oauth_callback_confirmed=true
I made this request through System.Net.Http.HttpClient and I throught I could read the response with FormUrlEncodedMediaTypeFormatter as FormDataCollection but it turned out that FormUrlEncodedMediaTypeFormatter only supports application/x-www-form-urlencoded format by default. So, I worked around this with the following code:
using (OAuthHttpClient client = new OAuthHttpClient(creds)) {
var response = await client.PostAsync(requestUri, new EmptyContent());
var formatter = new FormUrlEncodedMediaTypeFormatter();
formatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
var result = await response.Content.ReadAsAsync<FormDataCollection>(new List<MediaTypeFormatter>() { formatter });
}
The question here is:
Is the response provider (in this case it is Twitter) doing it wrong by sending this response as text/html or should FormUrlEncodedMediaTypeFormatter support text/html type by default?
Your question is missing some key info i.e. what is the requestUri supposed to return by default, is it a Web API service or an external one etc. It seems it's not Web API because it's little odd that it returns "text/html".
But the fact that FormUrlEncodedMediaTypeFormatter doesn't support formatting back from text/html is absolutely fine. Because why would it? "application/x-www-form-urlencoded" is effectively a key-value dictionary and text/html is a rich media type.
In Web API, with the way content negotiation works, it looks at
Mediatype mappings (I assume not in place in your case)
Accept headers - looking at your request you don't set them
Request content type - again, looking at your request you don't set it so it's empty
Can the formatter serialize a given type
So if you make the request as you showed to any Web API action, it would return text/xml (if you didn't tweak conneg manually).
I agree with Filip that this is a fine work around to an incorrect content type header.
Henrik

Resources