No route matches {:action=>"show", :controller=>"products"}, missing required keys: [:id] - rspec-rails

I have a rSpec Controller test which fails
the index route will have an :id which does not exist in an index route.
I've clean routes:
resources :products
This is the controller,
class ProductsController < ApplicationController
before_action :set_product, only: [:show]
skip_before_action :authorize_request, only: [:show, :index]
# GET /products
def index
# params here {"controller"=>"products", "action"=>"index", "product"=>{}}
#products = Product.all
render json: #products
end
ActionController::UrlGenerationError: No route matches {
:action=>"show",
:controller=>"products"},
missing required keys: [:id]
Why is "show" called? I did not passed any params to the controller:
This is the spec:
RSpec.describe "/products", type: :request do
describe " GET /index" do
it " renders a successful response " do
Fabricate.times(3, :product)
get "/products", headers: valid_headers
expect(response).to be_successful
end
end
end
When I change the routes from get "/products", to: 'products#index' and comment out resources :products then it passes the test
EDIT / PROBLEM FOUND :
I use include Rails.application.routes.url_helpers in my Product model, which caused this issue. I need it to generate URLs to retrieve my attachments. How else can I get the URLs of ActiveStorage ?

I solved the problem: I had include Rails.application.routes.url_helpers in my Product model which caused the problem:
class Product < ApplicationRecord
# include Rails.application.routes.url_helpers
end
commented out, all specs are passing now
But I still don't understand why I can't use it in my Model.
I wanna have it included to retrieve urls of my attachments:
def main_image_thumb_url
rails_blob_url(main_image, host: "localhost:3000")
end

Related

Why is this routing mismatched?

A page contains this route: <a href={{ path('app_gut_food_vector') }}> but triggers a not found by the #ParamConverter annotation error. The log shows this: Matched route "app_gut_show". . I cannot figure out how this could be. My question: How to get the correct route matched?
Pieces of the puzzle:
log entry:
INFO 23:53:50 request Matched route "app_gut_show".
{
"route": "app_gut_show",
"route_parameters": {
"_route": "app_gut_show",
"_controller": "App\\Controller\\GutController::show",
"id": "vector"
},
"request_uri": "http://diet/gut/vector",
"method": "GET"
}
template includes:
Click <a href={{ path('app_gut_food_vector') }}> here</a>
Controller includes:
#[Route('/vector', name: 'app_gut_food_vector', methods: ['GET', 'POST'])]
public function vector(Request $request, GutRepository $gutRepository, VectorService $vectorSvc)
{
...
}
debug:router includes:
app_gut_index GET ANY ANY /gut/
app_gut_new GET|POST ANY ANY /gut/new
app_gut_show GET ANY ANY /gut/{id}
app_gut_edit GET|POST ANY ANY /gut/{id}/edit
app_gut_delete POST ANY ANY /gut/{id}
app_gut_food_vector ANY ANY ANY /gut/vector
and the incorrectly matched route:
#[Route('/{id}', name: 'app_gut_show', methods: ['GET'])]
public function show(Gut $gut): Response
{
...
}
Note: removing the methods from the vector method does NOT prevent the mismatch.
Depends on what symfony version you use, you could try to define route priority.
If you use older version than 5.1, try to move your annotaion to yaml file and define /gut/vector route higher than /gut/{id} route
For higher version view Symfony doc
I think you're somehow messing with route orders.
As a matter of fact /gut/vector matches also /gut/{id} as regex are used to match routes.
I think you should declare your routes (so controller methods) in other order to avoid this kind of collisions.
Alternatively you can ask esplicitly for id to be an integer (if it is an integer; if it is a UUID, just to pick one random example, you should stick to UUID format).

App\\Entity\\Project object not found by the #ParamConverter annotation

Bit of an obscure one this. I'm reading that Symfony get's muddled when dealing with more than one route of a similar pattern. Here goes, this is what I've tried thus far:-
For starters, I hit the endpoint: https://127.0.0.1:8000/api/contracts/12345/new which returns the 404 error in full:-
{type: "https://tools.ietf.org/html/rfc2616#section-10", title: "An error occurred", status: 404,…}
class
:
"Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException"
detail
:
"App\\Entity\\Project object not found by the #ParamConverter annotation."
status
:
404
title
:
"An error occurred"
trace
:
[{namespace: "", short_class: "", class: "", type: "", function: "",…},…]
type
:
"https://tools.ietf.org/html/rfc2616#section-10"
Here's a snapshot of my URL patterns:-
docker-compose exec app bin/console debug:router
new_contract POST ANY ANY /api/contracts/{id}/new
api_edit_project POST ANY ANY /api/contracts/{id}/edit
They're very similar but I'm using the new endpoint from above. Here's the controller:-
/**
* #Route("/api")
*/
class ContractController extends BaseApiController
{
/**
* #Post ("/contracts/{id}/new", name="new_contract")
*/
public function postNewContractAction(){
// -- we don't hit this method at all
}
/**
* #Post ("/contracts/{id}/edit", name="api_edit_project")
*/
public function postEditContractAction(){}
}
Further to this, I've tried moving the controller methods around in terms of ordering, but this has no effect.
Any ideas?
As statet in the Symfony Documentation, receiving a 404 Error, when trying to fetch an object by it's id automatically using the paramConverter magic, this usually means there is no data for that id.
If no Post object is found, a 404 Response is generated;
I suspect there is no Project with id=12345.
Why are you asking for an {id} in the /new route, actually? To my understanding you would not have an ID in that case, yet.
I always try to set the parameters at last position in routes, as it may avoid route collisions.

django rest framework post method not allowed

I am creating an api and no idea why post method not allowed on any url.
views
class MessagesView(APIView):
permission_classes = (IsAuthenticated,)
def post(self, request):
serializer = MessageSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
chat.urls
urlpatterns = [
path("<str:pk>/", ChatDetail.as_view()),
path("messages/", MessagesView.as_view()),
]
response
{
"detail": "Method \"POST\" not allowed."
}
I am providing the token for the request, so isAuthenticated does not do anything wrong here.
Your first pattern will fire if you visit messages/. Indeed, its <str:pk> parameter matches any string (with at least one character and without slashes). But messages is thus also matched with this view.
What you can do is swap the places of the two urls, then calling messages/ will fire the correct view:
urlpatterns = [
# &downarrow; messages first
path('messages/', MessagesView.as_view()),
path('<str:pk>/', ChatDetail.as_view()),
]
If pk is an integer, you can further restrict the pk with the <int:…> path converter:
urlpatterns = [
path('messages/', MessagesView.as_view()),
path('<int:pk>/', ChatDetail.as_view()),
]

Is routes case-sensitive in Web API OData Service using ODataController?

i followed this to learn how ODataController works, everything is OK but when i changed the request uri
from
"localhost:49292/odata/Employees" //result: 200
to
"localhost:49292/odata/employees" //result: 404
to say one word: "odata" or "Odata" and "Employee" are all ok, but lowercase "employee" return 404. any explanation about this. Moreover, the routes in asp.net mvc is not case-sensitive afaik.
how about including a Route attribute and direct it to lower case. for Upper case web api will take care about it
[Route("odata/employees")]
add this on the top of the controller
if odata is common for every action then you can include [RoutePrefix] attribute
You can manually do it using the ODataModelBuilder instead of the ODataConventionModelBuilder
e.g
var builder = new ODataModelBuilder();
builder.EntitySet<Order>("Employees");
builder.EntitySet<Order>("employees");
this will work but your metadata will show 2 entity sets:
{
#odata.context: "http://localhost:62881/$metadata",
value: [
{
name: "Employees",
kind: "EntitySet",
url: "Employees"
},
{
name: "employees",
kind: "EntitySet",
url: "employees"
}
]
}
lowercase "employee" return 404.
I hope you probably didn't have the typo like that.
AFAIK, there is a case limitation on filter and properties. (You can vote there https://aspnetwebstack.codeplex.com/workitem/366 ) but not sure about the controller name..
You can create the REST server using web api without having oData as well..

Symfony 2 redirect route

I have the following route that works via a get:
CanopyAbcBundle_crud_success:
pattern: /crud/success/
defaults: { _controller: CanopyAbcBundle:Crud:success }
requirements:
_method: GET
Where Canopy is the namespace, the bundle is AbcBundle, controller Crud, action is success.
The following fails:
return $this->redirect($this->generateUrl('crud_success'));
Unable to generate a URL for the named route "crud_success" as such route does not exist.
500 Internal Server Error - RouteNotFoundException
How can I redirect with generateUrl()?
Clear your cache using php app/console cache:clear
return $this->redirect($this->generateUrl('CanopyAbcBundle_crud_success'));
If parameters are required pass like this:
return $this->redirect($this->generateUrl('CanopyAbcBundle_crud_success', array('param1' => $param1)), 301);
The first line of your YAML is the route name that should be used with the router component. You're trying to generate a URL for the wrong route name, yours is CanopyAbcBundle_crud_success, not crud_success.
Also, generateUrl() method does what it says: it generates a URL from route name and parameters (it they are passed). To return a 403 redirect response, you could either use $this->redirect($this->generateUrl('CanopyAbcBundle_crud_success')) which is built into the Controller base class, or you could return an instance of Symfony\Component\HttpFoundation\RedirectResponse like this:
public function yourAction()
{
return new RedirectResponse($this->generateUrl('CanopyAbcBundle_crud_success'));
}

Resources