How can I use path variables in grails controller? - grails

I have been trying to use a path variable in grails controller but I am not able to achieve it.
The intention behind is to validate the parameter submitted to the url which I need to make mandatory. I could not achieve it through RequestParam so I switched to PathVariable so that the url submitted without the required param should be filtered off by grails controller itself rather than me adding if/else checks for validity.
So, I can illustrate as below:
My URL is something as below:-
'<appcontext>/<controller>/<action>?<paramName>=<something>'
Now, to make 'paramName' mandatory I am not finding any way in Grails(Spring MVC provides #RequestParam annotation which can enable me for 'required' as true).
Another alternative I thought was to use path variables so that 'paramName' can be included in URL itself. So I tried like following:
'<appcontext>/<controller>/<action>/$paramName'
For validating the above URL I wrote specific mapping but some how it does not work too..
Following is the specific mapping I wrote:-
"/<controllerName>/<action>/$paramName" {
controller:<controller to take request>
action:<action to do task>
constraints {
paramName(nullable: false,empty:false, blank: false)
}
}
I tried to use spring annotation like #PathVariable and #RequestParam in controller as given below:-
def action(#PathVariable("paramName") String param){
//code goes here
}

If you name the method argument the same as the request parameter rename, Grails will take care of it for you...
// In UrlMappings.groovy
"/foo/$someVariable/$someOtherVariable" {
controller = 'demo'
action = 'magic'
}
Then in your controller:
// grails-app/controllers/com/demo/DemoController.groovy
class DemoController {
def magic(String someOtherVariable, String someVariable) {
// a request to /foo/jeff/brown will result in
// this action being invoked, someOtherVariable will be
// "brown" and someVariable will be "jeff"
}
}
I hope that helps.
EDIT:
Another option...
If for some reason you want different names for the method arguments you can explicitly map a method argument to a request parameter like this...
import grails.web.RequestParameter
class DemoController {
def magic(#RequestParameter('someVariable') String s1,
#RequestParameter('someOtherVariable') String s2) {
// a request to /foo/jeff/brown will result in
// this action being invoked, s2 will be
// "brown" and s1 will be "jeff"
}
}

Related

How to access url mapping parameters within a constraint custom validation?

In a grails 2.2.3 app I have the following url-mapping
import app.UserService
class UrlMappings {
UserService userService
static mappings = { ctx ->
"/users/$id/records/$school/detail" {
controller = 'user'
action = 'recordsDetail'
constraints {
id matches: /\d+/
school validator: { school, obj -> school in ctx.userService.getUserSchools(id) }
}
}
}
}
I need to implement a custom validation in school parameter. In order to do it I need to call userService.getUserSchools(id) method.
The current try give java.lang.NullPointerException with the message Cannot get property 'id' on null object I need to get access to id parameter within school custom validator. I tried id or obj.id but with no success.
Thanks for any advice
obj in this case is RegexUrlMapping which doen't allow to access id parameter, but you can use org.springframework.web.context.request.RequestContextHolder to access any request parameters inside validator closure as follows:
RequestContextHolder.requestAttributes.params.id
or just:
RequestContextHolder.requestAttributes.id

Grails data binding field exclusion

I am using Grails 2.5 and use Grails databinding in request methods.
For a basic example of the situation consider the following:
Domain class
class Product {
String field1
String privateField
}
Controller
class ProductController {
def update(Product productInstance) {
productInstance.save()
}
}
If I pass an existing Product to the controller like
{"id":3, "privateField":"newValue","field1":"whatever"}
the old value of privateField is overwritten. I want to enforce, that privateField is never bound from a request and avoid checking if the field is dirty.
Is there a mechanism in Grails to achieve this?
If I have to do the dirty check, how can I discard the new value and use the old one?
Pretty sure there's a "bindable" constraint.
http://grails.github.io/grails-doc/2.5.x/ref/Constraints/bindable.html
class Product {
String field1
String privateField
static constraints = {
privateField bindable: false
}
}
Should keep that field from binding automatically.
You can enforce which values are bound, but you'll need to change your method signature to get more control of the data binding process.
class ProductController {
def update() {
def productInstance = Product.get(params.id)
bindData(productInstance, params, [exclude: ['privateField']]
productInstance.save()
}
}

How to nest Grails controller paths

I know it is possible to do something like this:
Controller foo with action bar can be accessed by (1):
/appname/foo/bar
And it can be rewritten using URL mappings - e.g like this:
"/foobar/foo/$action"(controller: "foo")
And then access it via (2):
/appname/foobar/foo/bar
But it is still possible to access it via (1). This is of course because of the default URL mapping:
"/$controller/$action?/$id?"()
But I would rather not delete this, because this basically means I have to manually write mappings to every other controller/action that follows the default pattern.
It is possible to obtain url-patterns for specific controller/actions like (2) without using URL mappings? If not, is there an easy way to "exclude" controllers from the default mapping closure?
The Solution is to change the default mapping, in a way to exclude the whished special controller URL.
class UrlMappings {
static myExcludes = ["foo"]
static mappings = {
"/foobar/foo/$action"(controller: "foo") // your special Mapping
// the rewritten default mapping rule
"/$aController/$aAction?/$id?"{
controller = { (params.aController in UrlMappings.myExcludes) ? "error" : params.aController }
action = { (params.aController in UrlMappings.myExcludes) ? "notFound" : params.aAction }
constraints {
// apply constraints here
}
}
}
}
For the rewritten default rule you have to prevent usage of default variable names $controller and $action. Instead of error/notFound you are also able to redirect to other locations.
If you can make your rule breaking scenario more specific than the Grails default $controller/$action?$id? pattern, then the default can be left as is and will apply to everything outside of your exception pattern. I made a quick Person domain and performed a generate-all. Then I made a BreakRuleController just by itself.
class UrlMappings {
static mappings = {
"/b/$action?/$someVariable?"(controller: "breakRule")
"/$controller/$action?/$id?(.${format})?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
With this UrlMapping, if you access the URI "/b/foo/stackoverflow" it will print "Breaking Rules foo: stackoverflow". If you go to "/b", it will print "Breaking Rules index".
Then if you go to the standard Person URI's, all your default Grails scaffolding works fine also (create, edit, etc.) because that gets mapped to the typical "$controller/$action?/$id?" pattern.
class BreakRuleController {
def index() {
print "Breaking Rules index"
}
def foo(String someVariable) {
print "Breaking Rules foo: " + someVariable
}
}

Can non-trivial constructors call Future returning functions (how or alternatives)

Suppose you have:
class Schema {
Schema.fromText(String jsonString) {
...
}
}
In this constructor, assume there is an URL provided in the jsonString to download data and the only API to read an URL is one that returns a Future. Also, assume Schema is only a valid object when that URL data has been read and processed. Is it possible to even implement ...?
What you want to do is not possible with standard constructors.
Instead, try a static method that returns a new instance wrapped in a Future.
Something like:
class Schema {
Schema._fromApi(String apiResults) { ... }
static Future<Schema> build(String jsonString) {
return getContentsOfUrl(jsonString['url'])
.then((contents) => new Schema._fromApi(contents));
}
}

How to restrict properties for a domain being set from params

I've got several properties in my domain class. However, I only want few of them to be set via the params object. What is a good way to do this?
Example:
Domain
class Color {
String name
String shade //don't want this set by params
}
controller
class ColorController {
def save() {
json {
def c = new Color(params?.color)
c.save(flush: true)
//..more code
}
}
}
If someone sends a request like:
{"color":
{name: "red",
shade: "light"
}
}
then user can change the shade property. How can I stop this?
You could probably do one of a couple of things:
If it is many properties, create a transient beforeInsert() {} and/or transient beforeUpdate() {} method in your domain class and handle setting (or not) the properties.
If only a few, override the setters in the domain class.
Since Groovy makes me not want to mess with getters and setters unless I absolutely have to, I usually use the beforeInsert and beforeUpdate methods.
Grails provides a bindData method on the controller to give you fine grained control of data-binding. For your example you could write this as:
class ColorController {
def save() {
json {
def c = new Color()
bindData(c, params, [include: 'name'])
c.save(flush: true)
//..more code
}
}
}
In this case, only the 'name' field would be set on the c instance before attempting to save.
If you want to to additional validation on the incoming params, I would also suggest looking into using a Command Object for the data binding.

Resources