You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Grails 3中实现TinyURL?求Grails 2同款插件的实现代码

Implementing TinyURL in Grails 3 + Grails 2 Plugin-Like Code

Hey there! Let's break this down for you—first, how to build TinyURL functionality in Grails 3 from scratch (since old Grails 2 plugins might not be compatible anymore), then a look at what a similar Grails 2 plugin would have looked like.

Part 1: Grails 3 TinyURL Implementation

Step 1: Create the Domain Class

First, we need a domain to store original URLs and their corresponding unique short codes:

class ShortUrl {
    String originalUrl
    String shortCode
    Date dateCreated

    static constraints = {
        originalUrl url: true, blank: false
        shortCode blank: false, unique: true
    }

    static mapping = {
        dateCreated column: 'created_at'
    }
}

Step 2: Build a Service for Short Code Generation

Next, a service to handle generating unique short codes (we'll use Base62 encoding here for simplicity, but you could also use random alphanumeric strings):

import java.security.SecureRandom

class ShortUrlService {
    private static final String BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
    private static final int SHORT_CODE_LENGTH = 6

    String generateUniqueShortCode() {
        String code
        do {
            code = generateRandomShortCode()
        } while (ShortUrl.findByShortCode(code))
        return code
    }

    private String generateRandomShortCode() {
        SecureRandom random = new SecureRandom()
        StringBuilder sb = new StringBuilder(SHORT_CODE_LENGTH)
        for (int i = 0; i < SHORT_CODE_LENGTH; i++) {
            sb.append(BASE62_CHARS.charAt(random.nextInt(BASE62_CHARS.length())))
        }
        return sb.toString()
    }
}

Step 3: Create a Controller for Handling Requests

Now a controller to accept URL shortening requests and handle redirects:

class ShortUrlController {
    def shortUrlService

    // Action to generate a short URL
    def shorten() {
        if (request.method == 'POST') {
            String originalUrl = params.originalUrl
            if (!originalUrl) {
                render status: 400, text: "Original URL is required"
                return
            }

            ShortUrl existing = ShortUrl.findByOriginalUrl(originalUrl)
            if (existing) {
                render text: "${request.scheme}://${request.serverName}:${request.serverPort}/${existing.shortCode}"
                return
            }

            String shortCode = shortUrlService.generateUniqueShortCode()
            ShortUrl shortUrl = new ShortUrl(originalUrl: originalUrl, shortCode: shortCode)
            if (shortUrl.save(flush: true)) {
                render text: "${request.scheme}://${request.serverName}:${request.serverPort}/${shortCode}"
            } else {
                render status: 500, text: "Failed to create short URL: ${shortUrl.errors}"
            }
        }
    }

    // Action to redirect to the original URL
    def redirect(String code) {
        ShortUrl shortUrl = ShortUrl.findByShortCode(code)
        if (shortUrl) {
            redirect url: shortUrl.originalUrl, permanent: true
        } else {
            render status: 404, text: "Short URL not found"
        }
    }
}

Step 4: Update UrlMappings.groovy

Add these mappings to handle short code redirects directly:

class UrlMappings {
    static mappings = {
        "/$code"(controller: 'shortUrl', action: 'redirect')
        "/shorten"(controller: 'shortUrl', action: 'shorten')
        "/"(view:"/index")
        "500"(view:'/error')
    }
}

You can test this by sending a POST request to /shorten with an originalUrl parameter, then visiting the returned short URL to see the redirect in action.

Part 2: Grails 2 Plugin-Like Implementation

Back in Grails 2, a TinyURL plugin would have a similar core structure but with plugin-specific descriptors. Here's what that might have looked like:

1. Domain Class

class ShortUrl {
    String originalUrl
    String shortCode
    Date dateCreated

    static constraints = {
        originalUrl url: true, blank: false
        shortCode blank: false, unique: true
    }

    static mapping = {
        dateCreated column: 'created_at'
    }
}

2. Service Class

import java.security.SecureRandom

class ShortUrlService {
    static transactional = true // Default behavior in Grails 2
    private static final String BASE62_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
    private static final int SHORT_CODE_LENGTH = 6

    String generateUniqueShortCode() {
        String code
        do {
            code = generateRandomShortCode()
        } while (ShortUrl.findByShortCode(code))
        return code
    }

    private String generateRandomShortCode() {
        SecureRandom random = new SecureRandom()
        StringBuilder sb = new StringBuilder(SHORT_CODE_LENGTH)
        for (int i = 0; i < SHORT_CODE_LENGTH; i++) {
            sb.append(BASE62_CHARS.charAt(random.nextInt(BASE62_CHARS.length())))
        }
        return sb.toString()
    }
}

3. Controller

class ShortUrlController {
    def shortUrlService

    def shorten() {
        if (request.method == 'POST') {
            String originalUrl = params.originalUrl
            if (!originalUrl) {
                render status: 400, text: "Original URL is required"
                return
            }

            ShortUrl existing = ShortUrl.findByOriginalUrl(originalUrl)
            if (existing) {
                render text: "${request.scheme}://${request.serverName}:${request.serverPort}/${existing.shortCode}"
                return
            }

            String shortCode = shortUrlService.generateUniqueShortCode()
            ShortUrl shortUrl = new ShortUrl(originalUrl: originalUrl, shortCode: shortCode)
            if (shortUrl.save(flush: true)) {
                render text: "${request.scheme}://${request.serverName}:${request.serverPort}/${shortCode}"
            } else {
                render status: 500, text: "Failed to create short URL: ${shortUrl.errors}"
            }
        }
    }

    def redirect(String code) {
        ShortUrl shortUrl = ShortUrl.findByShortCode(code)
        if (shortUrl) {
            redirect url: shortUrl.originalUrl, permanent: true
        } else {
            render status: 404, text: "Short URL not found"
        }
    }
}

4. Plugin Descriptor (ShortUrlGrailsPlugin.groovy)

This is what defined it as a Grails 2 plugin:

class ShortUrlGrailsPlugin {
    def version = "0.1"
    def grailsVersion = "2.0 > *"
    def title = "TinyURL Plugin"
    def author = "Plugin Author"
    def authorEmail = "author@example.com"
    def description = '''Implements TinyURL-style URL shortening for Grails 2 applications'''

    def doWithSpring = {
        // Optional: Register custom beans here
    }

    def doWithApplicationContext = { ctx ->
        // Post-initialization logic if needed
    }

    def doWithWebDescriptor = { xml ->
        // Modify web.xml (e.g., add filters) if required
    }

    def onChange = { event ->
        // Handle hot-reloading changes during development
    }

    def onConfigChange = { event ->
        // Adjust behavior if plugin config changes
    }

    def onShutdown = { event ->
        // Cleanup tasks on app shutdown
    }
}

5. Plugin UrlMappings

class UrlMappings {
    static mappings = {
        "/$code"(controller: 'shortUrl', action: 'redirect')
        "/shorten"(controller: 'shortUrl', action: 'shorten')
    }
}

This plugin would be packaged and installed via the app's BuildConfig.groovy in Grails 2.


内容的提问来源于stack exchange,提问作者Kapil Arora

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 07:42:12