JSON encoded exceptions in TYPO3 extbase controller with JsonView - typo3

For my extbase-based TYPO3 CMS extension I created an ApiController with JsonView as view object. Returning values works like a charm, the correct header Content-type: application/json is set.
To return other responses like missing authorization messages or validation errors, I currently use:
$data = ["errors" => [
"status" => 401,
"message" => "Missing access token"
]];
$this->throwStatus($status, null, json_encode($data));
When I use $this->throwStatus() the header Content-type: text/html is set. Even if I manually set header("Content-type: application/json"); before using $this->throwStatus().
How can I create responses with the correct content type header?

Before you throw the status, try to set the headers in the response object:
$this->response->setHeader('Content-Type', 'application/json', true);
$this->response->sendHeaders();
If you are accessing your data through a dedicated pageType, you can set the header for this pageType in TypoScript:
myPageType.config.additionalHeaders {
10 {
header = Content-Type: application/json
replace = 1
}
}
I will add this to my post about the topic: https://usetypo3.com/json-view.html

Related

"Content-Type" and "Content-Encoding" headers in axios

I am using axios#0.21.1 and I want to validate the response headers.
I am unable validate the headers "Content-Type" and "Content-Encoding" from a GET response.
"Content-Type": No matter what content-type i pass in request, the content-type in response is always application/JSON.
Example Code Snippet:
if (<token is present>) {
request.headers = {
authorization : 'Bearer ${token}'
}
} else {
config.auth = {}
}
config.headers = Object.assign(config.header, {
'content-type': application/<custom content>,
'accept-encoding': 'gzip, deflate, br'
}
await axios.get(endPoint, config)
.then(response => {
return response
}*
When i am checking response.header, i see that content-type is showing as "application/json" instead of the custom type. But when i hit the same url in POSTMAN i could see that content-type is as expected.
Content-Encoding: I want to validate the content-encoding in the response, but what i learnt is axios does not return content-encoding header in the response and when i check their github, they are asking to use axios.interceptors. I tried using interceptors but still i am not seeing the header in response. But this header is present in response when i try in POSTMAN. There have been some solution say CORS needs to be enabled in server side. I am strictly asking it from QA point of view because we cannot enable CORS in server side.
Any help is highly appreciable.
Try:
axios.post(your-url, {
headers: {
'Content-Encoding': 'gzip'
}
})
or
axios.post(your-url, {
headers: {
'Accept-Encoding': 'gzip',
}
})
This is by design: https://axios-http.com/docs/req_config
I also ran into this and couldn't find a solution. Ended up using node-fetch instead.

Why is RestSharp posting form name/value pairs instead of JSON?

Why is RestSharp posting form name/value pairs instead of JSON when I have this line: `request.RequestFormat = DataFormat.Json;
var request = new RestRequest($"api/Users/{userId}/UpdateProperty", Method.PUT);
request.RequestFormat = DataFormat.Json;
request.AddObject(new { key = key, value = value });
Execute(request);
This results in the following http request (checked using Fiddler):
PUT /api/Users/c8c946f9-e1dd-49c6-9c7f-23572017058a/UpdateProperty HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 23
Accept-Encoding: gzip, deflate
key=Gender&value=Female
I was expecting the body to be JSON:
{
key: "Gender",
value: "Female"
}
What am I doing wrong?
Instead of the AddObject method, you want to use the AddJsonBody method. You probably also want to add the "Content-type" header with "application/json" value.
Basically something like this:
var request = new RestRequest($"api/Users/{userId}/UpdateProperty", Method.PUT);
request.AddHeader("Content-type", "application/json");
request.RequestFormat = DataFormat.Json;
request.AddJsonBody(new { key = key, value = value });
Execute(request);

Paypal UNSUPPORTED_MEDIA_TYPE

I am trying to get an access token from paypal's authorization api.
When I make post request to the api I get UNSUPPORTED_MEDIA_TYPE i.e. 415 response.
Below is the snippet that I used.
const auth = await fetch(PAYPAL_OAUTH_API, {
method: 'post',
headers: {
'Content-Type': 'application/json',
'Authorization': `Basic ${ basicAuth }`
},
body: JSON.stringify({"grant_type": "client_credentials"})
});
I have fixed my issue by setting Content-Type to application/x-www-form-urlencoded.
My guess is paypal accepts only application/x-www-form-urlencoded for authorization api.
I ran into same issue, and the solution is following (using Postman):
Select POST
Add Token into Authorization, type is Bearer-Token
Select Content-Type: application/json in headers
Use RAW as body, and in TEXT dropdown, select JSON (application/JSON)
Copy body as raw object and change info accordingly.
Step 4 and 5 are what solved the error, you must send raw json object.

Rest Service not seeing parameters from Grails Rest Client Builder

So I have a simple Grails UI which takes a few fields .. firstName, lastName etc. in a form. The controller calls a service method which then uses the Rest Client Builder plugin to call a REST service.
The rest service is not recognizing the parameters however.
Here is the simple rest call.
def resp = rest.post(baseUrl, params)
{
header 'Accept', 'application/json'
contentType "application/x-www-form-urlencoded"
}
Using version 2.0.1 of the plugin.
params looks like
[firstName:Kas, action:index, format:null, controller:myController, max:10]
Rest Service Method looks like ...
#POST
#Path("/employees")
#Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
#Consumes({MediaType.APPLICATION_FORM_URLENCODED})
public IdmResult createNewEmployee(#FormParam("firstName") String firstName) {
try {
if(firstName == null) return constructFailedIdmResult("First Name is a required field");
// Do some other stuff
}
}
Service responds with "First Name is a required field"
When I submit the Post from Postman it works fine. Successful request from Postman looks like
POST /idm/employees HTTP/1.1
Host: <ip>:<url>
Accept: application/json
firstName: Kas
Cache-Control: no-cache
Content-Type: application/x-www-form-urlencoded
Would like to figure how I can see the request that the plugin is constructing so I can compare differences, but ultimately I just need to know how to properly send the request from the plugin so that the Rest Service recognizes the form parameters.
Rest client should be using request body to POST:
def resp = rest.post(baseUrl) {
header 'Accept', 'application/json'
contentType "application/x-www-form-urlencoded"
json {
firstName = "Kas"
}
}
or simply,
def resp = rest.post(baseUrl) {
header 'Accept', 'application/json'
contentType "application/x-www-form-urlencoded"
json firstName: "Kas"
}
Refer docs for detail.
UPDATE:
Since producer is expecting request params as big query string instead of JSON, you might end up doing this instead:
def queryString = params.collect { k, v -> "$k=$v" }.join(/&/)
def resp = rest.post("$baseUrl?$queryString") {
header 'Accept', 'application/json'
contentType "application/x-www-form-urlencoded"
}
or just def resp = rest.post("$baseUrl?$queryString")
To pass the values cleanly in the request body, use a MultiValueMap and the (undocumented, from what I see) 'body()' method as per this answer. https://stackoverflow.com/a/21744515/17123

Is it possible in Sailsjs to build more complex models

I would like to have arrays or collections in my model, is this yet possible with waterline (mongoDB)? are there any alternatives around?
example:
{
name: Bundle,
col1 : {
name : anOtherModel,
subCol: {
text: aString,
...
}
},
col2 : {
name : anOtherModel,
subCol: {
text: aString,
...
}
}
}
to:
module.exports = {
attributes : {
name : {
type : 'STRING',
required : true
},
basicModules: {
type : 'ARRAY', // or 'COLLECTION'
required : false
}
}
};
I don't know if this is still an issue, but the trick is to neither POST as "form-data" nor "x-www-url-encoded". You have to POST the "raw" content:
Assume the situation:
http://www.example.com/mymodel
form-data
Your Header may look like this:
POST /mymodel/create HTTP/1.1
Host: www.example.com
Cache-Control: no-cache
----WebKitFormBoundaryE19zNvXGzXaLvS5C
Content-Disposition: form-data; name="basicModules"
[1,2,3,4]
----WebKitFormBoundaryE19zNvXGzXaLvS5C
the result is that a string "[1,2,3,4]" gets (type-)validated, which fails
x-www-url-encoded
In this case the Header is something like this:
POST /mymodel/create HTTP/1.1
Host: www.example.com
Cache-Control: no-cache
Content-Type: application/x-www-form-urlencoded
basicModules=%5B1%2C2%2C3%2C4%5D
which has exactly the same result as form-data. validation fails because of basicModules being the string "[1,2,3,4]"
raw
to make it work your Header has to look like this:
POST /mymodel/create HTTP/1.1
Host: www.example.com
Cache-Control: no-cache
{"basicModules":[1,2,3,4]}
which results in just exactly what you want, and type validation works.
so in the end, you can fill the most complex models that way in JSON. e.g.
POST /mymodel/create HTTP/1.1
Host: www.example.com
Cache-Control: no-cache
{"user": {
"name": {
"first":"John",
"last":"Doe"
},
"age":25,
"pets":[{
"name":"Garfield",
"type":"cat"
},
{
"name":"Rudolph",
"type":"reindeer"
}]
}
If you're looking for model associations, it's not there yet (look at this issue for proposed implementations) if you'd just like to have arrays of data stored in DB, you can have arrays as attribute (see the doc for reference on that). I haven't tested it but I guess it will serialize the array prior to saving it in the DB if it doesn't have a matching structure.