All Downloads are FREE. Search and download functionalities are using the official Maven repository.

grails.artefact.controller.RestResponder.groovy Maven / Gradle / Ivy

There is a newer version: 7.0.0-M1
Show newest version
/*
 * Copyright 2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package grails.artefact.controller

import groovy.transform.CompileDynamic
import grails.artefact.Controller
import grails.artefact.controller.support.ResponseRenderer
import grails.core.support.proxy.ProxyHandler
import grails.rest.Resource
import grails.rest.render.Renderer
import grails.rest.render.RendererRegistry
import grails.web.mime.MimeType
import groovy.transform.Generated
import org.grails.datastore.mapping.model.config.GormProperties
import org.grails.web.sitemesh.GroovyPageLayoutFinder
import org.grails.web.util.GrailsApplicationAttributes
import groovy.transform.CompileStatic
import groovy.transform.TypeCheckingMode

import javax.servlet.http.HttpServletResponse

import org.grails.plugins.web.rest.render.DefaultRendererRegistry
import org.grails.plugins.web.rest.render.ServletRenderContext
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpStatus
import org.springframework.validation.BeanPropertyBindingResult
import org.springframework.validation.Errors

/**
 *
 * @author Jeff Brown
 * @since 3.0
 *
 */
@CompileStatic
trait RestResponder {

    private String PROPERTY_RESPONSE_FORMATS = "responseFormats"

    private RendererRegistry rendererRegistry
    private ProxyHandler proxyHandler

    @Generated
    @Autowired(required = false)
    void setRendererRegistry(RendererRegistry rendererRegistry) {
        this.rendererRegistry = rendererRegistry
    }

    @Generated
    RendererRegistry getRendererRegistry() {
        return this.rendererRegistry
    }

    @Generated
    @Autowired(required = false)
    void setProxyHandler(ProxyHandler proxyHandler) {
        this.proxyHandler = proxyHandler
    }

    @Generated
    ProxyHandler getProxyHandler() {
        return this.proxyHandler
    }

    /**
     * Same as {@link RestResponder#respond(java.lang.Object, java.lang.Object, java.util.Map)}, but here to support Groovy named arguments
     */
    @Generated
    def respond(Map args, value) {
        internalRespond value, args
    }

    /**
     * The respond method will attempt to delivery an appropriate response for the
     * requested response format and Map value.
     *
     * If the value is null then a 404 will be returned. Otherwise the {@link RendererRegistry}
     * will be consulted for an appropriate response renderer for the requested response format.
     *
     * @param value The value
     * @return
     */
    @Generated
    def respond(Map value) {
        internalRespond value
    }

    /**
     * Same as {@link RestResponder#respond(java.util.Map)}, but here to support Groovy named arguments
     */
    @Generated
    def respond(Map namedArgs, Map value) {
        internalRespond value, namedArgs
    }

    /**
     * The respond method will attempt to delivery an appropriate response for the
     * requested response format and value.
     *
     * If the value is null then a 404 will be returned. Otherwise the {@link RendererRegistry}
     * will be consulted for an appropriate response renderer for the requested response format.
     *
     * @param value The value
     * @param args The arguments
     * @return
     */
    @Generated
    def respond(value, Map args = [:]) {
        internalRespond value, args
    }

    private internalRespond(value, Map args=[:]) {
        Integer statusCode
        if (args.status) {
            final statusValue = args.status
            if (statusValue instanceof Number) {
                statusCode = statusValue.intValue()
            } else {
                if (statusValue instanceof HttpStatus) {
                    statusCode = ((HttpStatus)statusValue).value()
                } else {
                    statusCode = statusValue.toString().toInteger()
                }
            }
        }
        if (value == null) {
            return callRender([status:statusCode ?: 404 ])
        }

        if (proxyHandler != null) {
            value = proxyHandler.unwrapIfProxy(value)
        }

        final webRequest = ((Controller)this).getWebRequest()
        List formats = calculateFormats(webRequest.actionName, value, args)
        final response = webRequest.getCurrentResponse()
        MimeType[] mimeTypes = getResponseFormat(response)
        def registry = rendererRegistry
        if (registry == null) {
            registry = new DefaultRendererRegistry()
            registry.initialize()
        }

        Renderer renderer = null

        for(MimeType mimeType in mimeTypes) {
            if (mimeType == MimeType.ALL && formats) {
                final allMimeTypes = MimeType.getConfiguredMimeTypes()
                final firstFormat = formats[0]
                mimeType = allMimeTypes.find { MimeType mt -> mt.extension == firstFormat}
                if(mimeType) {
                    webRequest.currentRequest.setAttribute(GrailsApplicationAttributes.RESPONSE_MIME_TYPE, mimeType)
                }
            }

            if (mimeType && formats.contains(mimeType.extension)) {
                Errors errors = value.hasProperty(GormProperties.ERRORS) ? getDomainErrors(value) : null


                if (errors && errors.hasErrors()) {
                    def target = errors instanceof BeanPropertyBindingResult ? errors.getTarget() : null
                    if (proxyHandler != null && target != null) {
                        target = proxyHandler.unwrapIfProxy(target)
                    }
                    Renderer errorsRenderer = registry.findContainerRenderer(mimeType, Errors.class, target)
                    if (errorsRenderer) {
                        final context = new ServletRenderContext(webRequest, [model: args.model])
                        if (args.view) {
                            context.viewName = args.view as String
                        }
                        if(statusCode != null) {
                            context.setStatus(HttpStatus.valueOf(statusCode))
                        }
                        errorsRenderer.render(errors, context)
                        if(context.wasWrittenTo() && !response.isCommitted()) {
                            response.flushBuffer()
                        }
                        return
                    }

                    return callRender([status: statusCode ?: 404 ])
                }

                final valueType = value.getClass()
                if (registry.isContainerType(valueType)) {
                    renderer = registry.findContainerRenderer(mimeType,valueType, value)
                    if (renderer == null) {
                        renderer = registry.findRenderer(mimeType, value)
                    }
                } else {
                    renderer = registry.findRenderer(mimeType, value)
                }
            }

            if(renderer) break
        }

        if (renderer) {
            final context = new ServletRenderContext(webRequest, args)
            if(statusCode != null) {
                context.setStatus(HttpStatus.valueOf(statusCode))
            }
            renderer.render(value, context)
            if(context.wasWrittenTo() && !response.isCommitted()) {
                response.flushBuffer()
            }
            return
        }
        callRender([status: statusCode ?: HttpStatus.NOT_ACCEPTABLE.value() ])
    }
    
    private callRender(Map args) {
        ((ResponseRenderer)this).render args
    }

    private List calculateFormats(String actionName, value, Map args) {
        if (args.formats) {
            return (List) args.formats
        }

        if (this.hasProperty(PROPERTY_RESPONSE_FORMATS)) {
            final responseFormatsProperty = this.getProperty(PROPERTY_RESPONSE_FORMATS)
            if (responseFormatsProperty instanceof List) {
                return (List) responseFormatsProperty
            }
            if ((responseFormatsProperty instanceof Map) && actionName) {
                Map responseFormatsMap = (Map) responseFormatsProperty

                final responseFormatsForAction = responseFormatsMap.get(actionName)
                if (responseFormatsForAction instanceof List) {
                    return (List) responseFormatsForAction
                }
                return getDefaultResponseFormats(value)
            }
            return getDefaultResponseFormats(value)
        }
        return getDefaultResponseFormats(value)
    }

    @CompileDynamic
    private MimeType[] getResponseFormat(HttpServletResponse response) {
        response.mimeTypesFormatAware
    }
    
    @CompileStatic(TypeCheckingMode.SKIP)
    private Errors getDomainErrors(object) {
        if (object instanceof Errors) {
            return object
        }
        final errors = object.errors
        if (errors instanceof Errors) {
            return errors
        }
        return null
    }
    
    private List getDefaultResponseFormats(value) {
        Resource resAnn = value != null ? value.getClass().getAnnotation(Resource) : null
        if (resAnn) {
            return resAnn.formats().toList()
        }
        return MimeType.getConfiguredMimeTypes().collect { MimeType mt -> mt.extension }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy