Render JSON with Header

Render JSON with header

You can't set a header after render, because the response is sent. So change the headers after makes no sense and you get this error.

Try:

def my_action
response.headers['Access-Control-Allow-Origin'] = '*'
render(json: some_params)
end

UPDATE FOR RAILS 5

As pointed in the question How do you add a custom http header?, after rails 5 you should be setting the headers as following:

response.set_header('HEADER NAME', 'HEADER VALUE')

Rails render auth_token to API response header

Try the response.header["auth_token"]

    def create

if @user && @user.authenticate(params[:user][:password])
@user.sign_in_count += 1
@user.save!

render status: 200,
json: { success: true,
info: "Logged in sucessfully."}
response.headers["auth_token"] = @user.authentication_token

else
render status: :unprocessable_entity,
json: { success: false,
info: "Login failed." }
end
end

Render JSON with custom Content-Type in Gin

You can use c.Render with a custom renderer, that implements render.Renderer.

If the actual rendering is identical to JSON (HAL should be) you can embed render.JSON into your struct, so that the method Render(http.ResponseWriter) error comes for free, and then implement only the custom content type:

type HALJSON struct {
render.JSON
}

func (HALJSON) WriteContentType(w http.ResponseWriter) {
header := w.Header()
if val := header["Content-Type"]; len(val) == 0 {
header["Content-Type"] = []string{"application/hal+json"}
}
}

And then use it as such:

func MyHandler(c *gin.Context) {
// handler code...
c.Render(http.StatusOK, HALJSON{})
}

How to get Rails to respond with json based on headers

You also need to set the Accept header to Accept: application/json

If you're curious as to why, and the difference between the two headers, the answers to this post explain it best: https://webmasters.stackexchange.com/questions/31212/difference-between-the-accept-and-content-type-http-headers

Why Splash+Scrapy add html header to json response

Straightforward option for extracting the JSON inside the HTML would be to use XPath (or CSS selectors). Here's the documentation for Scrapy Selectors.

Something like this in scrapy.Request callback function (self.parse)

json_response = response.xpath('html/body/pre/text()').get()
json_response = json.loads(json_response)

Note that I didn't test the code so you might need to change it a little bit (if I typo'd the XPath or something).

Also, you might want to try downloading the page with i.e. curl or Scrapy shell and check if the HTML part is still in the response. If not, somehow using Splash might make the website return a response that has the HTML.


Update on why the HTML is not in the response when using curl:

One possibility is that the web server returns a different response when using a browser than when using curl. One reason for doing this is to make the JSON more readable for the user using the browser. I mean, trying to read through JSON is easier when it's properly formatted and not just everything on a single line :D

So, if this is the case, my guess would be that Splash passes some data to the server (i.e. User-Agent, being able to render JavaScript) that makes the server return a response with the HTML.

Skipping Splash and using just Scrapy Request for making the request could help (and also make the crawler a little bit faster).

Anyway, if the XPath works (and the small and only possible speed increase doesn't matter), go with the XPath.

Organize JSON data in HTML table with unique header rows for each data group

I tried to create a function which creates exactly the table from your example

Here my script/function:

OLD Function with the wrong Data but right ouput .-.

const filterData = function (data) {
const filtered = {};
data.forEach(element => { // going through all the elements
if (filtered[element.callRoot]) filtered[element.callRoot].push(element); // if callRoot already exists in filtered object
else filtered[element.callRoot] = [element]; // else create property with callRoot as name -> here we push all the objects with the same callRoot name
})
return filtered;
};

const foo = function (data, element) {
const filtered = filterData(data);

// starting to create Table

// creating static table element, here becomes the element argument useful
const table = document.createElement('table');
element.appendChild(table);

// now we start creating the tbodies, trs and tds with the filtered data

// so we go through our filtered object by looping through an array of the keys ('@S','@C','@W')
Object.keys(filtered).forEach(property => {

// for each property we will create the tbody and tr + th and append them
const tbody = document.createElement('tbody');
table.appendChild(tbody);
let tr = document.createElement('tr');
tbody.appendChild(tr);
const th = document.createElement('th');
th.setAttribute('colspan', 9); // adding colspan attribute
th.textContent = filtered[property][0].userDescription; // taking the description as th text
tr.appendChild(th);

// now we create the rest of the tbody

// since i dont know where the data for the table comes from i will use the defined data from your example html output

// creating tr for our header tds
tr = document.createElement('tr');
tbody.appendChild(tr);
const definedDataArrayHeaders = ['Month', 'Last', 'Change', 'High', 'Low']; // headers from your example
definedDataArrayHeaders.forEach(element => { // going through the headers
const td = document.createElement('td');
td.textContent = element; // writing the header inside the td
tr.appendChild(td);
});

const definedDataArrayTds = ['August 2022', "1265'2", "-2'4", "1275'2", "1261'4"]; // tds from your example
for (let i of filtered[property]) { // will loop through each object inside our filtered array -> each tr in our future table
const tr = document.createElement('tr'); // creating tr for each data
tbody.appendChild(tr);
definedDataArrayTds.forEach(element => { // looping through the td data
const td = document.createElement('td');
td.textContent = element; // writing the data inside the td
tr.appendChild(td);
})
}
})

}

foo(commodities, document.querySelector('div')); // calling the function with data and Element where the table should be appended to

FALSE OLD used data:

const commodities = [
{
actualSymbol: "@SF22",
callRoot: "@S",
open: 1.00,
close: 2.00,
userDescription: "SOYBEANS",
// Other data
},
{
actualSymbol: "@SH22",
callRoot: "@S",
open: 4.00,
close: 6.00,
userDescription: "SOYBEANS",
// Other data
},
{
actualSymbol: "@SK22",
callRoot: "@S",
open: 0.50,
close: 2.30,
userDescription: "SOYBEANS",
// Other data
},
{
actualSymbol: "@CH22",
callRoot: "@C",
open: 0.25,
close: 0.75,
userDescription: "CORN",
// Other data
},
{
actualSymbol: "@CK22",
callRoot: "@C",
open: 5.00,
close: 6.75,
userDescription: "CORN",
// Other data
},
{
actualSymbol: "@CN22",
callRoot: "@C",
open: 1.00,
close: 2.00,
userDescription: "CORN",
// Other data
},
{
actualSymbol: "@WH22",
callRoot: "@W",
open: 3.25,
close: 2.00,
userDescription: "WHEAT",
// Other data
},
{
actualSymbol: "@WK22",
callRoot: "@W",
open: 1.00,
close: 2.00,
userDescription: "WHEAT",
// Other data
},
{
actualSymbol: "@WN22",
callRoot: "@W",
open: 0.10,
close: 0.20,
userDescription: "WHEAT",
// Other data
}
];

But with the RIGHT example output this is the html output:

<div>
<table>
<tbody>
<tr>
<th colspan="9">SOYBEANS</th>
</tr>
<tr>
<td>Month</td>
<td>Last</td>
<td>Change</td>
<td>High</td>
<td>Low</td>
</tr>
<tr>
<td>August 2022</td>
<td>1265'2</td>
<td>-2'4</td>
<td>1275'2</td>
<td>1261'4</td>
</tr>
<tr>
<td>August 2022</td>
<td>1265'2</td>
<td>-2'4</td>
<td>1275'2</td>
<td>1261'4</td>
</tr>
<tr>
<td>August 2022</td>
<td>1265'2</td>
<td>-2'4</td>
<td>1275'2</td>
<td>1261'4</td>
</tr>
</tbody>
<tbody>
<tr>
<th colspan="9">CORN</th>
</tr>
<tr>
<td>Month</td>
<td>Last</td>
<td>Change</td>
<td>High</td>
<td>Low</td>
</tr>
<tr>
<td>August 2022</td>
<td>1265'2</td>
<td>-2'4</td>
<td>1275'2</td>
<td>1261'4</td>
</tr>
<tr>
<td>August 2022</td>
<td>1265'2</td>
<td>-2'4</td>
<td>1275'2</td>
<td>1261'4</td>
</tr>
<tr>
<td>August 2022</td>
<td>1265'2</td>
<td>-2'4</td>
<td>1275'2</td>
<td>1261'4</td>
</tr>
</tbody>
<tbody>
<tr>
<th colspan="9">WHEAT</th>
</tr>
<tr>
<td>Month</td>
<td>Last</td>
<td>Change</td>
<td>High</td>
<td>Low</td>
</tr>
<tr>
<td>August 2022</td>
<td>1265'2</td>
<td>-2'4</td>
<td>1275'2</td>
<td>1261'4</td>
</tr>
<tr>
<td>August 2022</td>
<td>1265'2</td>
<td>-2'4</td>
<td>1275'2</td>
<td>1261'4</td>
</tr>
<tr>
<td>August 2022</td>
<td>1265'2</td>
<td>-2'4</td>
<td>1275'2</td>
<td>1261'4</td>
</tr>
</tbody>
</table>
</div>

New Solution, hope this one is working right haha

Here the new version of the script, its definitely not good so just take it as a guideline to come up with your own solution

const filterData2 = function (data) {
const filtered = {};
const wantedData = ['month', 'change', 'last', 'high', 'low', 'settlePrice'];
data.forEach(element => { // going through all the elements
// now using userDescript as key since its a bit more precise
if (filtered[element.userDescription]) { // if userDescription already exists in filtered object
const piece = [];
// now we extract only the data we need for our html table:
Object.entries(element).forEach(array => {
if (wantedData.includes(array[0])) array[1]?.number ? piece.push([array[0], array[1].number]) : piece.push([array[0], array[1]]);
});
filtered[element.userDescription].push(piece);
}
else {// else create property with userDescription as name -> here we push all the objects with the same callRoot name
const piece = [];
filtered[element.userDescription] = [];
Object.entries(element).forEach(array => {
if (wantedData.includes(array[0])) array[1]?.number ? piece.push([array[0], array[1].number]) : piece.push([array[0], array[1]]);
});
filtered[element.userDescription].push(piece);
}
})
return filtered;
};

// needed since i structured my filtered data different
const getValue = (arr, string) => arr.find(pair => pair[0] === string);

const foo2 = function (data, element) {
const filtered = filterData2(data);

// starting to create Table

// creating static table element, here becomes the element argument useful
const table = document.createElement('table');
element.appendChild(table);

// now we start creating the tbodies, trs and tds with the filtered data

// so we go through our filtered object by looping through an array of the keys in example:('SOYBEANS','CORN','WHEAT')
Object.keys(filtered).forEach(key => {

// key will be either 'SOYBEANS', 'CORN', 'WHEAT' - in this example in exactly this order

// for each key we will create the tbody and tr + th and append them
const tbody = document.createElement('tbody');
table.appendChild(tbody);
let tr = document.createElement('tr'); // using let here since we are going to overwrite this variable later so we dont have to create a new one
tbody.appendChild(tr);
const th = document.createElement('th');
th.setAttribute('colspan', 9); // adding colspan attribute
th.textContent = key; // taking the key as a header
tr.appendChild(th);

// now we create the rest of the tbody

// now since we filtered the data so in our filtered variable are only the necessary values for our table except the Settle Date

// creating tr for our header tds
tr = document.createElement('tr');
tbody.appendChild(tr);
const definedDataArrayHeaders = ['MONTH', 'LAST', 'CHANGE', 'HIGH', 'LOW', 'SETTLE DATE', 'SETTLE PRICE', 'MORE']; // Headers for the Table, can be anything - keep in Mind the date must be coming from somewhere for these headers, just some are covered by the filtered Array
definedDataArrayHeaders.forEach(header => { // going through the headers
const td = document.createElement('td');
td.textContent = header; // writing the header inside the td
tr.appendChild(td); // tr we created outside, so they all come into the same row
});

const definedDataArrayTds = ['month', "last", "change", "high", "low", "missing", "settlePrice", "missing"]; // this array must contain the names of the values inside the filtered array and must be in the same order as "definedDataArrayHeaders" so the data is in the right column

// since the Settle date and more value arent in the data/filtered array i will take placeholders so the table still renders right

// this part is needed to be changed a lot, since the data is now structured completely different

for (let i of filtered[key]) { // will loop through each object inside our filtered array -> each tr in our future table
const tr = document.createElement('tr'); // creating tr for each data
tbody.appendChild(tr);
definedDataArrayTds.forEach(element => { // looping through the td data
const td = document.createElement('td');
td.textContent = getValue(i, element)?.[1] || 'PLACEHOLDER'; // writing the data inside the td
tr.appendChild(td);
})
}
})
}

foo2(data, document.querySelector('div'));

data used in this new example:

const data = [
{
"symbol": {
"description": "SOYBEANS January 2022"
},
"actualSymbol": "@SF22",
"high": {
"number": 1396
},
"low": {
"number": 1374.75
},
"close": null,
"settlePrice": {
"number": 1401.5
},
"last": {
"number": 1378
},
"change": {
"number": -23.5
},
"month": "Jan 22",
"callRoot": "@S",
"userDescription": "SOYBEANS",
},
{
"symbol": {
"description": "SOYBEANS March 2022"
},
"actualSymbol": "@SH22",
"high": {
"number": 1409.75
},
"low": {
"number": 1379
},
"close": null,
"settlePrice": {
"number": 1410.25
},
"last": {
"number": 1383.25
},
"change": {
"number": -27
},
"month": "Mar 22",
"callRoot": "@S",
"userDescription": "SOYBEANS",
},
{
"symbol": {
"description": "SOYBEANS May 2022",
},
"actualSymbol": "@SK22",
"high": {
"number": 1418
},
"low": {
"number": 1387.5
},
"close": null,
"settlePrice": {
"number": 1418.75
},
"last": {
"number": 1391.25
},
"change": {
"number": -27.5
},
"month": "May 22",
"callRoot": "@S",
"userDescription": "SOYBEANS",
},
{
"symbol": {
"description": "CORN March 2022",
},
"actualSymbol": "@CH22",
"high": {
"number": 608
},
"low": {
"number": 596.75
},
"close": null,
"settlePrice": {
"number": 606.75
},
"last": {
"number": 599.25
},
"change": {
"number": -7.5
},
"month": "Mar 22",
"callRoot": "@C",
"userDescription": "CORN",
},
{
"symbol": {
"description": "CORN May 2022",
},
"actualSymbol": "@CK22",
"high": {
"number": 608.5
},
"low": {
"number": 598.25
},
"close": null,
"settlePrice": {
"number": 607.75
},
"last": {
"number": 600.75
},
"change": {
"number": -7
},
"month": "May 22",
"callRoot": "@C",
"userDescription": "CORN",
},
{
"symbol": {
"description": "CORN July 2022",
},
"actualSymbol": "@CN22",
"high": {
"number": 605.25
},
"low": {
"number": 595.5
},
"close": null,
"settlePrice": {
"number": 604.5
},
"last": {
"number": 598
},
"change": {
"number": -6.5
},
"month": "Jul 22",
"callRoot": "@C",
"userDescription": "CORN",
},
{
"symbol": {
"description": "WHEAT March 2022",
},
"actualSymbol": "@WH22",
"high": {
"number": 767.5
},
"low": {
"number": 748
},
"close": null,
"settlePrice": {
"number": 758.5
},
"last": {
"number": 763.75
},
"change": {
"number": 5.25
},
"month": "Mar 22",
"callRoot": "@W",
"userDescription": "WHEAT",
},
{
"symbol": {
"description": "WHEAT May 2022",
},
"actualSymbol": "@WK22",
"high": {
"number": 768.5
},
"low": {
"number": 750
},
"close": null,
"settlePrice": {
"number": 760.5
},
"last": {
"number": 765.5
},
"change": {
"number": 5
},
"month": "May 22",
"callRoot": "@W",
"userDescription": "WHEAT",
},
{
"symbol": {
"description": "WHEAT July 2022",
},
"actualSymbol": "@WN22",
"high": {
"number": 763.25
},
"low": {
"number": 747.25
},
"close": null,
"settlePrice": {
"number": 757
},
"last": {
"number": 760.5
},
"change": {
"number": 3.5
},
"month": "Jul 22",
"callRoot": "@W",
"userDescription": "WHEAT",
}
];

Output with the above data example



Related Topics



Leave a reply



Submit