'Rails not redirecting on PUT request, sending 406 back instead
I'm working on a bell schedule creator for a school project I'm working on. The bell schedule creation on the client is handled with a React component, and when it comes time to update, the component calls this fetch request:
fetch(`/bell-schedules/${this.state.updatingId}`, {
method: 'PUT',
mode: 'cors',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'Accept': 'text/html',
},
redirect: "follow",
referrerPolicy: 'no-referrer',
body: JSON.stringify({ name: this.state.name, schedule: json})})
On the server, these requests are handled by the BellSchedulesController, and the method for handling this type of route is coded as so:
# PUT /bell-schedules/:id
def update
set_bell_schedule
respond_to do |format|
if @schedule.update(name: params[:name], schedule: params[:schedule])
redirect_to action: 'index'
else
head 400
end
end
end
Index is a simple method that gets all the bell schedules and renders them in a table; that method works fine, other routes point to it and it renders normally. Unfortunately, when I send this PUT request, rather than getting a redirect to that page showing the table of bell schedules, I instead get back a 406 Not Acceptable. I also know that the bell schedule is actually updating, I've inspected the entry in the rails console.
How do I handle redirects in PUT requests? I'm also having an issue similar to this in a POST request, but I've found a hack around it there; that hack won't work in this one.
Solution 1:[1]
Googling your problem it seems to come from the respond_to ... |format| block, try getting rid of it and see if it helps. You don't seem to be using it correctly, you are not doing anything with the format and maybe that's why it's blowing up.
Solution 2:[2]
Edit: the issue appears to be two-fold - the use of respond_to and the redirect status. Updating my answer as follows:
- Wrap
redirect_toinformat.html. As @joel-blum suggested, removing therespond_toblock entirely also solves this issue; however, I still required the 303 status for this to work locally - Use a 303 (
:see_other) status code for the redirect
def update
set_bell_schedule
respond_to do |format|
if @schedule.update(name: params[:name], schedule: params[:schedule])
format.html { redirect_to action: 'index', status: :see_other }
else
head 400
end
end
end
Explanation
respond_to
When using respond_to in your controller actions, you need to specify what format(s) your responses are targeted for. In this case, your request is Accept: text/html, so you need to specify that as the target format. format.all or removing respond_to entirely would also work here, unless you need to return different responses based on the Accept header.
303 Redirect
Standard 301/302 redirects for dangerous HTTP methods (anything other than GET) generally won't be honored by the browser.
The browser will attempt to redirect to index using the same HTTP method that the request was initially made in: PUT. This will result in a 404. Setting the redirect status to 303 ensures that the browser performs the index redirect with a GET request.
A typical redirect response to a GET request indicates that the requested resource has moved (temporarily or permanently).
For PATCH/PUT/POST/DELETE, The browser has to assume that the resource was found at the expected location, but its data has changed since the initial request was made. The server is just informing the client that the response for this action exists at another location.
For the sanctity of the HTTP contract, a different status code is needed to indicate this situation. 303 informs the client of the location of this response location and suggests that it can be accessed with a GET request.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Joel Blum |
| Solution 2 |
