actionSubmit のアクション名にパラメーターを追加してみます。
<my:actionSubmit action="deleteAdj" value="delete" params="${[deleteAdjIdx: idx]})"/>
これを行う良い方法が見つからなかったので、次のハックを思いつきました。誰かが改善点を知っていれば、私はそれを感謝します。
MyTagLib に追加:
import org.codehaus.groovy.grails.web.util.WebUtils
def actionSubmit = { attrs ->
String action = attrs.action ?: attrs.value
Map params = attrs.remove('params')
if (params) {
attrs.action = (action + WebUtils.toQueryString(params)).encodeAsURL()
}
out << g.actionSubmit(attrs)
}
パラメータを解凍し、フィルタで元のアクション名を復元します。
package com.example;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.web.filter.GenericFilterBean
import org.codehaus.groovy.grails.web.util.WebUtils
import com.example.util.MyWebUtils
import javax.servlet.http.HttpServletRequest;
/**
* Handles params packed into the MyTagLib.actionSubmit param name.
* This Filter needs to come before GrailsWebRequestFilter.
* <p/>
* Updating the GrailsParameterMap after the GrailsWebRequestFilter doesn't work,
* because the request given to DefaultUrlMappingInfo.checkDispatchAction()
* is the immutable request saved in the GrailsWebRequest, not the wrapped request
* that this filter passes on down the chain.
*/
public class ActionSubmitParamFilter extends GenericFilterBean {
final static QUERY_SEPARATOR = '?'
final static QUERY_SEPARATOR_REGEX = '[?]'
final static PARAM_SEPARATOR_REGEX = '[&]'
@Override
void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
def dispatchAction = request.parameterNames.find {it.startsWith(WebUtils.DISPATCH_ACTION_PARAMETER)}
def decoded = dispatchAction?.decodeURL()
if (decoded?.contains(QUERY_SEPARATOR)) {
def overrides = unpackParams(decoded)
overrides[dispatchAction] = null // deletes from the overridden request params
request = MyWebUtils.overrideParams((HttpServletRequest) request, overrides)
}
chain.doFilter(request, response);
}
private Map unpackParams(String decoded) {
String action, paramsPart
(action, paramsPart) = decoded.split(QUERY_SEPARATOR_REGEX)
assert action.startsWith(WebUtils.DISPATCH_ACTION_PARAMETER)
def packedParams = [:].withDefault {[]}
packedParams[action] << ''
for (param in paramsPart.split(PARAM_SEPARATOR_REGEX)) {
if (param.contains('=')) {
def (name, value) = param.split('=')
packedParams[name.decodeURL()] << value.decodeURL()
} else {
packedParams[param.decodeURL()] << ''
}
}
packedParams
}
}
リクエスト パラメータをオーバーライドしてフィルタを設定するユーティリティ メソッドを作成しました。
package com.example.util
import org.springframework.web.multipart.MultipartHttpServletRequest
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletRequestWrapper
import groovy.util.slurpersupport.GPathResult
import com.example.ActionSubmitParamFilter
class MyWebUtils {
static HttpServletRequest overrideParams(HttpServletRequest original, Map<String, List<String>> overrides) {
assert !(original instanceof MultipartHttpServletRequest) // if we get one of these, we'll need to use a special wrapper
def params = (Map<String, String[]>) new HashMap(original.getParameterMap())
overrides.each {k, v ->
if (v == null) {
params.remove(k) // significant for parameterNames enumeration and parameterMap
} else {
params[k] = v as String[]
}
}
params = Collections.unmodifiableMap(params)
new HttpServletRequestWrapper(original) {
@Override
String[] getParameterValues(String name) {
params[name]
}
@Override
String getParameter(String name) {
String[] values = params[name]
values ? values[0] : (values == null ? null : '')
}
@Override
Map getParameterMap() {
params
}
@Override
Enumeration getParameterNames() {
Collections.enumeration(params.keySet())
}
}
}
// used dynamically by _Events.eventWebXmlStart
static void prependActionSubmitParamFilter(GPathResult webXml) {
def filters = webXml.filter
def filterMappings = webXml.'filter-mapping'
def firstFilter = filters[0]
def firstFilterMapping = filterMappings[0]
firstFilter + {
filter {
'filter-name'('actionSubmitParam')
'filter-class'(ActionSubmitParamFilter.name)
}
}
firstFilterMapping + {
'filter-mapping' {
'filter-name'('actionSubmitParam')
'url-pattern'("/*")
'dispatcher'("FORWARD")
'dispatcher'("REQUEST")
}
}
}
}
最後に、_Events.groovy に以下を追加して、ControllersGrailsPlugin.doWithWebDescriptor を拡張して web.xml でフィルターを構成します。
eventWebXmlStart = {
pluginManager.getGrailsPlugin('controllers').with {
def originalClosure = instance.doWithWebDescriptor // extending
instance.doWithWebDescriptor = { GPathResult webXml ->
// static import fails after clean (before compile), so load dynamically
def webUtils = getClass().classLoader.loadClass('com.example.util.MyWebUtils')
webUtils.prependActionSubmitParamFilter(webXml)
// call super after (so above filter comes before GrailsWebRequestFilter in chain)
originalClosure.resolveStrategy = Closure.DELEGATE_FIRST
originalClosure.delegate = delegate
originalClosure(webXml)
}
}
}