ActionController::UnknownFormat when uploading image with Dropzone - ruby-on-rails

I have a new error regarding my Dropzone JS snippet. I use dropzone to upload images from an #Edit view.
One the images are created by another controller create action, I redirect to that very edit action.
This is where I get this new error :
Completed 406 Not Acceptable in 14ms (ActiveRecord: 1.1ms)
ActionController::UnknownFormat (PhotographesController#edit is missing a template for this request format and variant.
request.formats: ["application/json"]
request.variant: []):
actionpack (5.2.0) lib/action_controller/metal/implicit_render.rb:42:in `default_render'
actionpack (5.2.0) lib/action_controller/metal/basic_implicit_render.rb:6:in `block in send_action'
actionpack (5.2.0) lib/action_controller/metal/basic_implicit_render.rb:6:in `tap'
actionpack (5.2.0) lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'
actionpack (5.2.0) lib/abstract_controller/base.rb:194:in `process_action'
.....
It used to work perfectly in the past. But since I have set this snippet in place I have done quite a few things: installing gem "serviceworker-rails" and deferring the javascript..
I have undeferred the Javascript but the error is still here. Is it related to the serviceworker gem ?
EDIT
Dropzone snippet inside Photographes#edit view (inline)
<script>
// Dropzone = dynamic
var AUTH_TOKEN=$('meta[name="csrf-token"]').attr('content');
Dropzone.autoDiscover = false;
var myDropzone = new Dropzone("div#mydropzone",{
url: "<%= photographe_photographephotos_path(#photographe.hashed_id) %>",
autoProcessQueue: false,
autoDiscover: false,
uploadMultiple: true,
addRemoveLinks: true,
// clickable: false,
parallelUploads: 12,
maxFilesize: 5,
maxFiles: 12,
acceptedFiles: 'image/jpg, image/jpeg, image/png',
params:{
'authenticity_token': AUTH_TOKEN
},
successmultiple: function(data,response){
$('#msgBoard').append(response.message).addClass("alert alert-success");
$('#msgBoard').delay(2000).fadeOut();
$('#fileslist').val(response.filesList);
$('#photographedit').off('submit').submit();
}
});
$('#photographedit').submit(function(e){
if(myDropzone.getQueuedFiles().length > 0){
e.preventDefault();
myDropzone.processQueue();
}
});
</script>
Then Photographephotos#create
def create
#photographe = Photographe.find_by(hashed_id: params[:photographe_hashed_id])
if params[:file].present?
uploaded_pics = params[:file]
maximum_images=12
available_images = maximum_images - #photographe.photographephotos.count
n_keys = uploaded_pics.keys.first(available_images)
filtered_pics = uploaded_pics.slice(*n_keys)
filtered_pics.each do |index,pic|
#image = #photographe.photographephotos.new
#image.image = pic
#image.image_file_name = "Copyright" + #photographe.professionnel.first_name.to_s + #photographe.professionnel.last_name.to_s + ".JPG"
#image.save
end
end
redirect_to edit_photographe_path(#photographe.hashed_id)
end
The redirect at the end returns (should return) to the Photographes#edit view. What is funny if it used to work properly ...
I see that people used to have same problem with Jbuilder as per github. I have updated Jbuilder gem with no success.
Also the pictures are properly updated by paperclip. I get this error from logs when the redirection fails at the end of the images creation.

It seems like the controller is trying to render a template in a format that doesn't exist, maybe you can try specifying the format explicitly like this:
redirect_to edit_photographe_path(#photographe.hashed_id), :format => :html
This may happen if you received a request in a specific format and want to render a template in a different one.
I'm not sure if, in your case, the :format option should go inside the _path() or the redirect_to() method. If the above doesn't work try this:
redirect_to edit_photographe_path(#photographe.hashed_id, :format => :html)

Related

POSTing JSON body causes Rack::QueryParser::InvalidParameterError

I'm simply trying to POST a JSON content-type http request from one Rails server to another. Putting a % character in a value string in the JSON is causing the Rack::QueryParser::InvalidParameterError on the receiving server.
On Server A, I use the httparty gem to perform a POST request w/ a JSON body
obj = {"key":"<%= #results %>"}
resp = HTTParty.post('https://test-url.com/data',
:body => obj.to_json,
:options => { :headers => { 'Content-Type' => 'application/json', 'Accept' => 'application/json' } })
On Server B, I just parse the JSON from the request body
req_body = request.body.read()
req_body_json = JSON.parse req_body
# do work with json
This process works fine unless the obj = {"key":"<%= #results %>"} contains % chars, in which case this occurs on server B:
Started POST "/data" for 10.244.1.3 at 2018-01-02 19:27:04 +0000
F, [2018-01-02T19:27:04.245275 #1] FATAL -- :
F, [2018-01-02T19:27:04.245331 #1] FATAL -- : Rack::QueryParser::InvalidParameterError (invalid %-encoding ({"key":"\u003c%)):
F, [2018-01-02T19:27:04.245358 #1] FATAL -- :
F, [2018-01-02T19:27:04.245397 #1] FATAL -- : rack (2.0.3) lib/rack/query_parser.rb:72:in `rescue in parse_nested_query'
rack (2.0.3) lib/rack/query_parser.rb:60:in `parse_nested_query'
rack (2.0.3) lib/rack/request.rb:468:in `parse_query'
rack (2.0.3) lib/rack/request.rb:343:in `POST'
actionpack (5.1.4) lib/action_dispatch/http/request.rb:362:in `block (2 levels) in POST'
actionpack (5.1.4) lib/action_dispatch/http/parameters.rb:107:in `block in parse_formatted_parameters'
actionpack (5.1.4) lib/action_dispatch/http/parameters.rb:107:in `fetch'
actionpack (5.1.4) lib/action_dispatch/http/parameters.rb:107:in `parse_formatted_parameters'
actionpack (5.1.4) lib/action_dispatch/http/request.rb:361:in `block in POST'
rack (2.0.3) lib/rack/request.rb:57:in `fetch'
rack (2.0.3) lib/rack/request.rb:57:in `fetch_header'
actionpack (5.1.4) lib/action_dispatch/http/request.rb:360:in `POST'
...
When I change obj = {"key":"<%= #results %>"} to obj = {"key":"<= #results>"} the exception does not occur.
As far I know % is a valid in JSON, what am I missing here?
Considering they way how it's implemented here: https://github.com/jnunemaker/httparty/blob/38125db0d079d52a3b0da815150fa2135bb0110d/lib/httparty.rb#L559,
Your body and headers hash keys should be siblings, try the next:
resp = HTTParty.post('https://test-url.com/data',
:body => obj.to_json,
:headers => { 'Content-Type' => 'application/json',
'Accept' => 'application/json' }
)

Get "ActionView::MissingTemplate" error, when I post json to rails controller from angular

I use angularjs in my new ruby on rails project. I create my view with angularjs and back end server with ruby on rails.
Now, I have a form in angular part, when user complete the form and click on send, I post a json like below to server:
$scope.jsonList = {
"form1": {name:"John",lastName:"Smart"},
"form2": {job:"student",age:"18"}
};
I post this json to a test action in rails controller. I put javascript (angularjs) and server side code below:
angularController:
//Services
.factory('Test', function($resource){
return $resource('/api/test.json', {}, {
create: { method: 'post'}
});
})
//Angular Controller
$scope.testResponse = Test.create(angular.toJson($scope.jsonList));
and routes.rb:
namespace :api, defaults: {format: :json} do
scope module: :v1, constraints: ApiConstraints.new(version: 1, default: :true) do
match '/test',to:'dropdowns#test', via: 'post'
end
end
and dropdownsController:
class Api::V1::DropdownsController < Api::V1::BaseController
def test
end
end
I do all this, but when I post json to rails server, I recieve json, but I get below error:
Started POST "/api/test.json" for 127.0.0.1 at 2014-11-22 17:11:10 +0330
Processing by Api::V1::DropdownsController#test as JSON
Parameters: {"form1"=>{"name"=>"John", "lastName"=>"Smart"}, "form2"=>{"job"=>"student", "age"=>"18"}, "dropdown"=>{"form1"=>{"name"=>"John", "lastName"=>"Smart"}, "form2"=>{"job"=>"student", "age"=>"18"}}}
Completed 500 Internal Server Error in 1ms
ActionView::MissingTemplate (Missing template api/v1/dropdowns/test, api/v1/base/test with {:locale=>[:en], :formats=>[:json], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby, :coffee, :jbuilder]}. Searched in:
* "/Users/mgh/Documents/Maliat/sama/app/views"
):
actionview (4.1.6) lib/action_view/path_set.rb:46:in `find'
actionview (4.1.6) lib/action_view/lookup_context.rb:124:in `find'
actionview (4.1.6) lib/action_view/renderer/abstract_renderer.rb:18:in `find_template'
actionview (4.1.6) lib/action_view/renderer/template_renderer.rb:41:in `determine_template'
I can do any thing with json when I recieve in rails controller (like parse and puts on console and save in database with other controller), but at the end of controller, I get above error and I cannot post any response to angular part. Where is the problem?
How can I fix this error?
You are not explicitly rendering anything in the test method so rails is looking for a template to send back as a response. However, you don't have a json template in your api/v1/dropdowns/ folder (I think it will look for a file called test.json in your case).
You can solve this by either sending back a response explicitly, (check out this guide for more about rendering a response.
def test
render plain: "This is the response from 'test' method in DropDownsController"
end
or you want to return json of a model
def test
render json: #product
end
OR
You can put a template in your api/v1/dropdowns/ folder and rails will serve that instead. But the overall point is you need to send back a response.
For example:
api/v1/dropdowns/test.json.erb
{
"name" : "my name is greg or something"
}

Ruby On Rails:recieve json, parse and save in database

I want post a json to rails controller and then save each of json to database by separate controller.
For this, I post this json to rails controller:
jsonList = {
"table": {column1:"column1",column2:"column2"},
"record": {name:"record 1"}
};
and recieve below json in rails controller:
Started POST "/api/calculate.json" for 127.0.0.1 at 2014-11-19 00:52:13 +0330
Processing by Api::V1::CalculateController#create as JSON
Parameters: {"table"=>{"column1"=>"column1", "column2"=>"column2"}, "record"=>{"name"=>"record 1"}, "calculate"=>{"table"=>{"column1"=>"column1", "column2"=>"column2"}, "record"=>{"name"=>"record 1"}}}
But I get below error in rails server log before I do anything:
Completed 500 Internal Server Error in 2ms
ActionView::MissingTemplate (Missing template api/v1/calculate/create, api/v1/base/create with {:locale=>[:en], :formats=>[:json], :variants=>[], :handlers=>[:erb, :builder, :raw, :ruby, :coffee, :jbuilder]}. Searched in:
* "/Users/mgh/Documents/Maliat/sama/app/views"
):
actionview (4.1.6) lib/action_view/path_set.rb:46:in `find'
actionview (4.1.6) lib/action_view/lookup_context.rb:124:in `find'
actionview (4.1.6) lib/action_view/renderer/abstract_renderer.rb:18:in `find_template'
actionview (4.1.6) lib/action_view/renderer/template_renderer.rb:41:in `determine_template'
I search for this error, but don't find any usefull guide.
RailsController:
class Api::V1::CalculateController < Api::V1::BaseController
require 'json'
def create
....
end
end
I have another question. I want save each json to database by separate controller. For example I have a RecordController and use create action by below code:
def create
temp = Record.new(params[:record])
temp.save
end
and this type:
def create
temp = Record.create(params[:record])
end
but I get below error in server log:
ActiveModel::ForbiddenAttributesError (ActiveModel::ForbiddenAttributesError):
app/controllers/api/v1/calculate_controller.rb:6:in `create'
RecordController:
class Api::V1::RecordController < Api::V1::BaseController
require 'json'
def create
#data = Record.create(record_params)
#data.save
respond_with(#data)
end
private
def record_params
params.require(:record).permit(:name)
end
end
How can I fix this problems?
You need to review the use of strong parameters features in Rails. A similar question is also answered here: Active Model Forbidden attributes error
Why you wanna split JSON and send to two separate controllers instead of that create one controller method and parse your JSON and store into the database,
JSON format:
{
"table": {column1:"column1",column2:"column2"},
"record": {name:"record 1"}
};
Controller method:
def create_record_and_table
record = params[:record]
#record = Record.new(:name => record['name'])
#record.save
table = params[:table]
#table = Table.new(:column1 => table['column1'], :column2 => table['column2'])
#table.save
end

Rails wrong number of arguments (1 for 2) in controller

I'm trying to make a simple calculator that converts farenheit to celsius, using Ruby on Rails. I keep getting the error "Wrong number of arguments (1 for 2)" after I enter some input value. This app has been alot more difficult than it should be. I've been fixing errors as they come up, but can't figure this one out. Other posts I've read are much different and more elaborate than this simple app I'm trying to make. Thanks for any help.
The controller ...
class CalculatorController < ApplicationController
def calculate
#farenheit = params[:temperature]
unless #farenheit.blank?
#farenheit = Temperature.calculate({ :farenheit => #farenheit})
end
#celsius = (#farenheit - 32) * (5.0 / 9.0)
end
private
## Strong Parameters
def user_params
params.require(:farenheit)
end
end
The model...
class Temperature < ActiveRecord::Base
# attr_accessible :farenheit
validates_presence_of :farenheit
validates_numericality_of :farenheit
end
The view...
<h1>Temperature Calculator</h1>
<%= form_tag(calculator_calculate_path, method: "get", action: "calculate") do |form|%>
<p>Please enter a temperature in degrees Farenheit</p>
<%= text_field_tag 'temperature', #farenheit %></p>
<%= submit_tag 'Convert' %>
<h2>Result: </h2>
<h3> <% #celsius %> </h3>
<% end %>
The stack trace (first 10 lines)...
activerecord (4.1.6) lib/active_record/relation/calculations.rb:109:in `calculate'
C:in `calculate'
app/controllers/calculator_controller.rb:6:in `calculate'
actionpack (4.1.6) lib/action_controller/metal/implicit_render.rb:4:in `send_action'
actionpack (4.1.6) lib/abstract_controller/base.rb:189:in `process_action'
actionpack (4.1.6) lib/action_controller/metal/rendering.rb:10:in `process_action'
actionpack (4.1.6) lib/abstract_controller/callbacks.rb:20:in `block in process_action'
activesupport (4.1.6) lib/active_support/callbacks.rb:113:in `call'
activesupport (4.1.6) lib/active_support/callbacks.rb:113:in `call'
You've defined an instance method calculate in your CalculatorController, but calculate is also a class method defined in ActiveRecord. It is the ActiveRecord class method that you are calling when you say
#farenheit = Temperature.calculate({ :farenheit => #farenheit})
and that method requires at least two arguments. Here are the docs:
calculate(operation, column_name, options = {}) public
This calculates aggregate values in the given column. Methods for
count, sum, average, minimum, and maximum have been added as
shortcuts. Options such as :conditions, :order, :group, :having, and
:joins can be passed to customize the query.
See http://apidock.com/rails/ActiveRecord/Calculations/ClassMethods/calculate
I'm not sure why you're using the calculate class method though. Why not use only your calculation of #celcius? The following should get your calculate method working without an ArgumentError:
def calculate
#farenheit = params[:temperature]
unless #farenheit.blank?
#celsius = (#farenheit.to_i - 32) * (5.0 / 9.0)
end
end

rails - InvalidAuthenticityToken for json/xml requests

For some reason I'm getting an InvalidAuthenticityToken when making post requests to my application when using json or xml. My understanding is that rails should require an authenticity token only for html or js requests, and thus I shouldn't be encountering this error. The only solution I've found thus far is disabling protect_from_forgery for any action I'd like to access through the API, but this isn't ideal for obvious reasons. Thoughts?
def create
respond_to do |format|
format.html
format.json{
render :json => Object.create(:user => #current_user, :foo => params[:foo], :bar => params[:bar])
}
format.xml{
render :xml => Object.create(:user => #current_user, :foo => params[:foo], :bar => params[:bar])
}
end
end
and this is what I get in the logs whenever I pass a request to the action:
Processing FooController#create to json (for 127.0.0.1 at 2009-08-07 11:52:33) [POST]
Parameters: {"foo"=>"1", "api_key"=>"44a895ca30e95a3206f961fcd56011d364dff78e", "bar"=>"202"}
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
thin (1.2.2) lib/thin/connection.rb:76:in `pre_process'
thin (1.2.2) lib/thin/connection.rb:74:in `catch'
thin (1.2.2) lib/thin/connection.rb:74:in `pre_process'
thin (1.2.2) lib/thin/connection.rb:57:in `process'
thin (1.2.2) lib/thin/connection.rb:42:in `receive_data'
eventmachine (0.12.8) lib/eventmachine.rb:242:in `run_machine'
eventmachine (0.12.8) lib/eventmachine.rb:242:in `run'
thin (1.2.2) lib/thin/backends/base.rb:57:in `start'
thin (1.2.2) lib/thin/server.rb:156:in `start'
thin (1.2.2) lib/thin/controllers/controller.rb:80:in `start'
thin (1.2.2) lib/thin/runner.rb:174:in `send'
thin (1.2.2) lib/thin/runner.rb:174:in `run_command'
thin (1.2.2) lib/thin/runner.rb:140:in `run!'
thin (1.2.2) bin/thin:6
/opt/local/bin/thin:19:in `load'
/opt/local/bin/thin:19
With protect_from_forgery enabled, Rails requires an authenticity token for any non-GET requests. Rails will automatically include the authenticity token in forms created with the form helpers or links created with the AJAX helpers--so in normal cases, you won't have to think about it.
If you're not using the built-in Rails form or AJAX helpers (maybe you're doing unobstrusive JS or using a JS MVC framework), you'll have to set the token yourself on the client side and send it along with your data when submitting a POST request. You'd put a line like this in the <head> of your layout:
<%= javascript_tag "window._token = '#{form_authenticity_token}'" %>
Then your AJAX function would post the token with your other data (example with jQuery):
$.post(url, {
id: theId,
authenticity_token: window._token
});
I had a similar situation and the problem was that I was not sending through the right content type headers - I was requesting text/json and I should have been requesting application/json.
I used curl the following to test my application (modify as necessary):
curl -H "Content-Type: application/json" -d '{"person": {"last_name": "Lambie","first_name": "Matthew"}}' -X POST http://localhost:3000/people.json -i
Or you can save the JSON to a local file and call curl like this:
curl -H "Content-Type: application/json" -v -d #person.json -X POST http://localhost:3000/people.json -i
When I changed the content type headers to the right application/json all my troubles went away and I no longer needed to disable forgery protection.
This is the same as #user1756254's answer but in Rails 5 you need to use a bit more different syntax:
protect_from_forgery unless: -> { request.format.json? }
Source: http://api.rubyonrails.org/v5.0/classes/ActionController/RequestForgeryProtection.html
Adding up to andymism's answer you can use this to apply the default inclusion of the TOKEN in every POST request:
$(document).ajaxSend(function(event, request, settings) {
if ( settings.type == 'POST' || settings.type == 'post') {
settings.data = (settings.data ? settings.data + "&" : "")
+ "authenticity_token=" + encodeURIComponent( window._token );
}
});
Since Rails 4.2, we have another way is to avoid verify_authenticity_token using skip_before_filter in your Rails App:
skip_before_action :verify_authenticity_token, only: [:action1, :action2]
This will let curl to do its job.
Ruby on Rails 4.2 Release Notes: https://guiarails.com.br/4_2_release_notes.html
As long as the JavaScript lives on the website served by Rails (for example: a JS snippet; or React app managed via webpacker) you can use the the value in csrf_meta_tags included in application.html.erb by default.
In application.html.erb:
<html>
<head>
...
<%= csrf_meta_tags %>
...
Therefore in the HTML of your website:
<html>
<head>
<meta name="csrf-token" content="XZY">
Grab the token from the content property and use it in the request:
const token = document.head.querySelector('meta[name="csrf-token"]').content
const response = await fetch("/entities/1", {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ authenticity_token: token, entity: { name: "new name" } })
});
This is similar to #andrewle's answer but there's no need for an additional token.
To add to Fernando's answer, if your controller responds to both json and html, you can use:
skip_before_filter :verify_authenticity_token, if: :json_request?

Resources