如何在Grails 3中实现TinyURL?求Grails 2同款插件的实现代码
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

