Grails - URL mappings with empy-valued path param - grails

In my Grails 4 web-app defined 2 URL mappings
"/projects" ( controller: 'team', action: 'listProjects' )
"/projects/$projectid" ( controller: 'team', action: 'getProject' )
When, in the 2nd mapping, projectid param is erroneously passed as “” (empty string), the listProjects action is processed instead of getProject (the intended one).
Is there a way to do that? I'd actually want a "project not found" error to be returned in place of the list of projects.
Thanks in advance!

Related

Grails 2.5.5: Unable to access the request body of a PUT request?

I'm attempting to implement a RESTful API using Grails 2.5.5, and I'm running into a few issues.
It appears that Grails does not automatically map any methods for the corresponding HTTP methods, so I'm editing UrlMappings.groovy.
For example, take the following URLs:
GET /v1/1/persons/ <--- List of persons
POST /v1/1/persons/ <--- Create a new person
PUT /v1/1/persons/1234 <--- Edit person with ID of 1234
These are my url mappings:
"/v1/$appId/$controller/$action?/$id?(.$format)?" {
namespace = "v1"
}
"/v1/$appId/$controller"(action: "save", method: "POST") {
namespace = "v1"
}
"/v1/$appId/$controller/$id"(action: "update", method: "PUT") {
namespace = "v1"
}
So now, the first mapping will handle the GET request in my example urls as well as other generic urls.
The second mapping will handle the second url from my example urls.
And lastly, the third mapping handles the third url from my example urls.
The issue I'm facing now is that my command object isn't getting bound properly for my PUT request. The POST request works fine however.
These are my methods:
def save(MyCommand cmd) {
// works great
}
def update(MyCommand cmd) {
// cmd properties are null
// params.id is bound though. So I'm getting the path variable.
}
As you can see, the logic is very simple.
But I'm completely stumped as to why I can't get the request body in the PUT method.
Additional question: How can I get the above urls to work in addition to this url?:
/v1/1/persons/1234/status
I tried the following mapping, but it does not seem to work:
"/v1/$appId/$controller/$id/$action" {
namespace = "v1"
}
It feels like I'm stuck in this URLMappings hell!

Grails `respond` returns null

I want to send validation errors back to a different page (add), so I have this for my save action:
#Transactional(readOnly = false)
def save(AddDomainCommand command) {
if (command.validate() && session.isLoggedIn && session.publisher) {
// do some stuff
return redirect(controller: 'Widget', action: 'generate')
}
log.info("Validation failed for $command")
respond view: "add", model: [domain: command]
}
It errors with javax.servlet.ServletException: Could not resolve view with name 'save' in servlet with name 'grailsDispatcherServlet'
If I print the response from respond, I get null! So that explains why it's going to save, because that's the convention for the action's name.
I need it to go back to the view it came from (add.gsp), yet grails respond is null and thus defaulting to save.gsp.
Any ideas?
respond uses a different syntax and is used when you want to be able to support multiple client types based on mime type, e.g. JSON and/or XML for a REST client and HTML/GSP for a regular browser client. If you just want to use add.gsp to render HTML, use render:
render view: 'add', model: [domain: command]

Segment variable default values are not assigned as expected

I have the following routing in RouteConfig.cs:
routes.MapRoute(name: "",
url: "{controller}/{action}/InwDocSource{id}",
defaults: new { Controller = "InwardDocument", action = "AddDocument", id = "1" });
And in my controller :
public class InwardDocumentController : Controller
public ActionResult AddDcument(string id){
if(id=="1")
ViewBag.IdInWords="The received id is ONE";
else if(id=="2")
ViewBag.IdInWords="The received id is TWO";
else
ViewBag.IdInWords="The received id is SOMETHING ELSE";
return View("Index");// FYI the Index.cshtml is not strongly typed
}
}
So the above mapping will match any url with 3 segments, third of which should begin with "InwDocSource". And all the segment variables have their default values in case they're not passed from the browser. Lets assume that my root web site is www.mysite.net and the above url mapping is the only one in the RouteConfig.cs file
How come when I type just www.mysite.com the URL matches the pattern and I get "The received id is ONE"? I know that all the variables are defaulted to some values but the mapping says that the third segment must begin with InwDocSource.
The second is weirder though: If I enter www.mysite.com/InwardDocument/AddDocument/InwDocSource
I get "The received id is SOMETHING ELSE", so the default value of 1 does not get assigned to the third segment variable. Why's that?
I'll begin to answer in the reverse order
The route mapping says that it's going to match a url with 3 segments third of which must begin with InwDocSource. So when I pass www.mysite.com/InwardDocument/AddDocument/InwDocSource there are 3 segments and the third one does indeed begin with the static string InwDocSource, it does not have to continue or end with something else. :) So the value for the third segment becomes InwDocSource and that's why I get The received id is SOMETHING ELSE
When I pass just the domain part the routing configuration will still match the above mapping, because all three segments have their default values in case any of them are not passed explicitly.

Passing parameter to grails mapping

I have named mapping I'm trying to pass a parameter to. While I have googled/viewed many responses on this question when using the grails tag in a gsp:
<g:link mapping="accountDetails" params="[acctNumber:'8675309']">
I am trying to use g.link and not <g:link in my taglib and am getting an exception. This is the line in my tag lib:
String otherProfileLink = g.link(mapping:"browseProfile", params:"['profileId':'123']", "my link text")
In my URLMappings:
name browseProfile: "/browseProfile/$profileId" {controller = 'search'; action = 'show'}
The exception is:
Unable to create URL for mapping [/browseProfile/(*)] and parameters [{controller=message, action=index}]. Parameter [profileId] is required, but was not specified!
I've tried a few variations on the params value, with and without quotes, with/without closures. But so far no luck.
Have you tried this:
g.link(mapping:"browseProfile", params: [profileId:'123']) { "my link text" }

Grails "loses" custom URL mapping when following any links on the page

I have an application where users can browse maps in two ways (like thumbnails and in a list)
/map/browse
/map/list
Now, I would like to restrict these views to just show maps of a specific user, for example through
/user/3/browse
/user/3/list
So I created the mapping:
"/user/$userId/browse" {
controller = "map"
action = "browse"
}
"/user/$userId/list" {
controller = "map"
action = "list"
}
Now, I can go to /user/3/browse, but as soon as I click on a pagination link or change the pagination filters, the URL goes back to /map/browse.
Also, if I set the userId to null in the controller, I get the error:
Error 500: Error processing GroovyPageView: Error executing tag : Unable to create URL for mapping [/user/(*)/list] and parameters [["action":"list", "controller":"map", "max":20, "offset":0, "sort":"uploadDate", "order":"desc", "userId":null, "totalMaps":30]]. Parameter [userId] is required, but was not specified! at /views/map/browse.gsp:26
The pagination works as follows:
<div class="paginateButtons">
<g:paginate controller="map" action="browse" total="${mapInstanceTotal}"></g:paginate>
</div>
What can I do against that or what would be the correct way of implementing what I want?
I don't necessarily need to have that URL mapping, I only need a nice way of saying: "Display maps of only one user"
It seems that the problem is not at your URL mapping configuration ,but in your way to create link. I think it's better if you use Named URL Mapping : it's clearer than your approach now, and when create link for pagination you only need to specify the url name. For example:
In UrlMappings.groovy:
static mappings = {
name accountDetails: "/details/$acctNumber" {
controller = 'product'
action = 'accountDetails'
}
}
In view - gsp page:
<g:link mapping="accountDetails" params="[acctNumber:'8675309']">
Show Account
</g:link>

Resources