Wandering into uncharted waters, I've been having some trouble figuring out how to access an error message I made in my Rails API thru my React Frontend. I've been reading through a good amount of sites and so far I haven't really been able to figure out where i'm going wrong. Is it on the Rails side or the React side?
TLDR; I want my response error to be: "Username or Password does not match.", but I am getting: "Request failed with status code 422"
Rails Controller
class Api::V1::SessionsController < ApplicationController
def create
user = User.find_by(email: params["user"]["email"]).try(:authenticate, params["user"]["password"])
if user
session[:user_id] = user.id
render json: {
status: 200,
logged_in: true,
user: user
}
else
// how can I reach this error message?
render json: { status: "error", message: "Username or Password does not match." }, status: :unprocessable_entity
end
end
...
React Component
handleLogin = (e) => {
e.preventDefault();
axios
.post(
'http://localhost:3001/api/v1/sessions',
{
user: { email: this.state.email, password: this.state.password }
},
{ withCredentials: true }
)
.then((response) => {
if (response.data.logged_in) {
this.handleSuccessfulAuth(response.data);
}
})
.catch((error) => {
// returns login error Request failed with status code 422
console.log('login error', error.message);
});
};
Your message property could be accessible like this:
.catch(error => {
console.log('login error', error.response.data.message);
});
Related
I am trying to send a POST request from frontend using Axios to register a user.
handleSubmit(event) {
const { email, password, password_confirmation } = this.state;
let partnerCredentials = {
partner: {
email: email,
password: password,
password_confirmation: password_confirmation,
},
};
axios({
method: "post",
url: "http://localhost:3001/v1/registrations",
data: partnerCredentials,
headers: { "Content-Type": "multipart/form-data" },
})
.then(function (response) {
//handle success
console.log(response);
})
.catch(function (response) {
//handle error
console.log(response);
});
event.preventDefault();
}
Now, I get an error on the backend Rails API when my frontend is trying to the partner credentials:
Processing by V1::RegistrationsController#create as HTML
Parameters: {"{\"partner\":{\"email\":\"test#gmail.com\",\"password\":\"asdf\",\"password_confirmation\":\"asdf\"}}"=>"[FILTERED]"}
HTTP Origin header (http://localhost:3000) didn't match request.base_url (http://localhost:3001)
Completed 422 Unprocessable Entity in 2ms (ActiveRecord: 0.0ms | Allocations: 471)
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
...
Here is my controller:
module V1
class RegistrationsController < ApplicationController
protect_from_forgery with: :exception
skip_before_action :verfiy_authenticity_token, raise: false
def create
partner = Partner.create!(
email: params['partner']['email'],
password: params['partner']['password'],
password_confirmation: params['partner']['password_confirmation']
)
if partner
session[:partner_id] = partner.index
render json: {
status: created,
partner: partner
}
else
render json: { status: 500 }
end
end
private
def partner_params
params.require(:partner).permit(:email, :password, :password_confirmation)
end
end
end
Any help where I can look for to resolve this issue will be greatly appreciated. Thank you
I recently deployed my site on Heroku and Netlify and was having issues with Auth. My current issue (and hopefully last) is that upon login, rails is sending back a user instance instead of the object with information (i.e #User:0x000056205efbbad8). I get a token from my rails response and upon refresh am logged in but am not automatically logged in because of the user instance being returned instead of an object with user information.
This is my auth controller
class AuthController < ApplicationController
def login
user = User.find_by(username: params[:username])
if user && user.authenticate(params[:password])
secret = ENV["SECRET_KEY_BASE"]
token = JWT.encode({ user_id: user.id }, secret, 'HS256')
render json: { user: UserSerializer.new(user), token: token }
else
render json: { failure: "Invalid Username or Password" }
end
end
def signup
auth_params = params.permit(:username, :password, :email, :avatar)
if params[:avatar].instance_of?(String) || params[:avatar].nil?
user = User.create(auth_params)
render json: user
else
imageUploaded = Cloudinary::Uploader.upload(params[:avatar])
user_params_new = auth_params
user_params_new[:avatar] = imageUploaded["url"]
user = User.create(user_params_new)
if user.valid?
secret = ENV["SECRET_KEY_BASE"]
token = JWT.encode({ user_id: user.id }, secret, 'HS256')
render json: {user: user, token: token }, status: :created
else
render json: { error: user.errors.full_messages }, status: :unprocessable_entity
end
end
end
end
Here is my login function on my React frontend
function handleLogin(e) {
e.preventDefault()
fetch(`${process.env.REACT_APP_API_BASE_URL}/login`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(loginData)
})
.then(r => r.json())
.then(data => {
if (data.failure) {
Swal.fire({
icon: 'error',
title: 'Oops...',
text: 'Incorrect Username or Password!'
})
} else {
setCurrentUser(data.user)
setUserReviews(data.user.reviews)
setFavorites(data.user.favorites)
localStorage.setItem("token", data.token)
history.push("/festivals")
}
})
}
I so appreciate any help on this, thanks so much!
Link to github repo: https://github.com/connormul/festie-backend
https://github.com/connormul/festie-frontend
render json: { user: UserSerializer.new(user), token: token }
This doesn't look a correct use of serializer
try to change it to
render json: { user: UserSerializer.new(user).as_json, token: token }
I am trying to validate User inputs on server side in a Rails Application with React as view. Basically I make axios calls to the Rails API like this:
const Script = props => {
const [script, setScript] = useState({})
const [scene, setScene] = useState({})
const [loaded, setLoaded] = useState(false)
useEffect(() => {
const scriptID = props.match.params.id
const url = `/api/v1/scripts/${scriptID}`
axios.get(url)
.then( resp => {
setScript(resp.data)
setLoaded(true)
})
.catch(resp => console.log(resp))
}, [])
const handleChange = (e) => {
e.preventDefault()
setScene(Object.assign({}, scene, {[e.target.name]: e.target.value}))
}
const handleSubmit = (e) => {
e.preventDefault()
const csrfToken = document.querySelector('[name=csrf-token]').content
axios.defaults.headers.common['X-CSRF-TOKEN'] = csrfToken
const script_id = script.data.id
axios.post('/api/v1/scenes', {scene, script_id})
.then(resp => {
const included = [...script.included, resp.data.data]
setScript({...script, included})
})
.catch(err => {
console.log(err.response.data.error)
})
.finally(() => {
setScene({name: '', description: ''})
})
}
All data gets passed into a react component with a form.
return (
<div className="wrapper">
{
loaded &&
<Fragment>
.
.
.
<SceneForm
handleChange={handleChange}
handleSubmit={handleSubmit}
attributes={script.data.attributes}
scene={scene}
/>
</Fragment>
}
</div>
)
In this form I have a name field and the corresponding name in Rails has a validation uniqueness: true. everything works fine if I enter a valid (unique) name.
I tried to implement a validation but I am not happy with the outcome. (It works in general: my no_errors? method does what is is supposed to do and I get a 403 status) This is the controller part:
def create
scene = script.scenes.new(scene_params)
if no_error?(scene)
if scene.save
render json: SceneSerializer.new(scene).serialized_json
else
render json: { error: scene.errors.messages }, status: 422
# render json: { error: scene.errors.messages[:name] }, status: 423
end
else
render json: { error: "name must be unique" }, status: 403
end
end
.
.
.
private
def no_error?(scene)
Scene.where(name: scene.name, script_id: scene.script_id).empty?
end
If I enter an existing name I get a console.log like this:
screenshot
Here is my concern: I am not happy with my approach of error handling in general. I do not want to get the 403 message logged to the console (I do want to avoid this message in the first place).
My idea is to take the "simple form" approach: Make the border of my field red and post an error message under the field, without any console output...
And on a side note: Is 403 the correct status? I was thinking about 422 but wasn't sure...
Thank you for your ideas in advance!
403 is the wrong status code. What you need is to return a 422 (unprocessable entity). 403 is more about policy and what you are authorized to do.
Then when you deal with http request it's a standard to have a request and status code printed in browser console. Not sur to get your issue here.
If it's about to display the error you could just have a function that colorize (or whatever fireworks you want) your input if the status code response is a 422.
people so I have this problem when trying to do a post API call.
the situation is this:
I have a large application where students sign up so I can manage them and contact them through that student management app.
Now I have created a smaller app where students can do their assignments, lets call that homework app;
I am doing a post API call to the bigger app in the login section so students don't have to sign up again and they can use the same information in both applications.
I am getting an internal server error 500; in the terminal the error is:
TypeError - no implicit conversion of nil into String:
app/controllers/api/sessions_controller.rb:12:in `create'
this is the create action in sessions controller in the student management app:
def create
unless request.format == :json
sign_out
render status: 406, json: { message: "JSON requests only." } and return
end
resource = warden.authenticate!(auth_options)
if resource.blank?
render status: 401, json: { response: "Access denied." } and return
end
sign_in(resource_name, resource)
respond_with resource, location:
after_sign_in_path_for(resource) do |format|
format.json { render json:
{
success: true, jwt: current_token, response: "Authentication successful"
}
}
end
end
and most importantly the api call with a very simple html form:
ps: I am running the projects locally, thats why the url's are defined as localhost
<form class="login-form">
<input type="email" class="mail mail-input" placeholder=" Email"><br>
<input type="password" class="password pw-input" placeholder=" Password"><br>
<button class="login" type="button" name="button">Login</button>
</form>
<script>
var login = document.querySelector(".login")
login.addEventListener("click", function(){
var email = document.querySelector(".mail")
var password = document.querySelector(".password")
let loginValues = {
email: email.value,
password: password.value
}
$.post (
"http://localhost:4000/api/login.json",
{
api_user: {
email: email.value,
password: password.value
}
},
function (response){
console.log(response)
console.log('in');
window.location.replace("http://localhost:3000/")
}
)
})
</script>
Can you try replacing this:
api_user: {
email: email.value,
password: password.value
}
With this:
user: {
email: email.value,
password: password.value
}
Tell me if it works?
Update, based on comments:
I really have no idea what you are doing in the code above, the code is really messy... Try replacing this:
respond_with resource, location:
after_sign_in_path_for(resource) do |format|
format.json { render json:
{
success: true, jwt: current_token, response: "Authentication successful"
}
}
end
with this:
respond_with resource do |format|
format.json { render json:
{
success: true, jwt: current_token, response: "Authentication successful"
}
}
end
I need to upload an image of my user from my react app to Rails api only server.
My HTML input file
<input ref={(input) => this.profileImage_input = input}
type="file"
name="user-profile-image"
className="small-font-size"
onChange={(e) => this.handleChange(e)}/>
So this is my component upload function.
let profileUploadImage = this.profileImage_input.files;
let user_info = {};
user_info = {
username: this.username_input.value,
profile_image: profileUploadImage
};
this.props.update_user_with_image_server(user_info,this.props.user.user.id)
My actionCreator
export function update_user_with_image_server(user_info,user_id){
let formData = new FormData();
for(let key in user_info) {
if(user_info.hasOwnProperty(key)) {
formData.append(key, user_info[key]);
}
}
return function (dispatch) {
fetch(deafaultUrl + '/v1/users_profile/' + user_id,
{
headers: {
'Content-Type': 'multipart/form-data'
},
method: "PATCH",
body: formData
})
.then(function(response) {
if (response.status >= 400) {
throw new Error("Bad response from server");
}
return response.json();
})
.then(function(json){
});
}
}
My rails user controller
def update_with_image
user = current_user
if user.update_attributes(user_update_params)
# Handle a successful update.
render json: user, status: 200
else
render json: { errors: user.errors }, status: 422
end
end
private
def user_update_params
params.permit(:username,:profile_image)
end
So the problem is it seems like user_update_params didn't workout rails cannot read the data and update in correctly so how could i fix this?
Thanks!