添加http返回类型枚举

This commit is contained in:
tursom 2019-12-15 01:52:06 +08:00
parent 85291d9f59
commit 1f9372ae68
6 changed files with 210 additions and 36 deletions

View File

@ -14,6 +14,7 @@ import cn.tursom.web.result.Text
import cn.tursom.web.router.impl.SimpleRouter import cn.tursom.web.router.impl.SimpleRouter
import cn.tursom.web.utils.Chunked import cn.tursom.web.utils.Chunked
import cn.tursom.web.utils.ContextType import cn.tursom.web.utils.ContextType
import cn.tursom.web.utils.NoReturnLog
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.File import java.io.File
import java.io.RandomAccessFile import java.io.RandomAccessFile
@ -55,7 +56,7 @@ open class RoutedHttpHandler(
} }
override fun handle(content: HttpContent) { override fun handle(content: HttpContent) {
log?.debug("{}: {} {}", content.clientIp, content.method, content.uri) log?.debug("{} {} {}", content.clientIp, content.method, content.uri)
if (content is MutableHttpContent) { if (content is MutableHttpContent) {
handle(content, getHandler(content, content.method, content.uri)) handle(content, getHandler(content, content.method, content.uri))
} else { } else {
@ -132,19 +133,20 @@ open class RoutedHttpHandler(
} }
fun addRouter(obj: Any, method: Method, route: String, router: Router<Pair<Any?, (HttpContent) -> Any?>>) { fun addRouter(obj: Any, method: Method, route: String, router: Router<Pair<Any?, (HttpContent) -> Any?>>) {
val doLog = method.doLog
router[safeRoute(route)] = obj to (if (method.parameterTypes.isEmpty()) { router[safeRoute(route)] = obj to (if (method.parameterTypes.isEmpty()) {
when { when {
method.getAnnotation(Html::class.java) != null -> { content -> method.getAnnotation(Html::class.java) != null -> { content ->
method(obj)?.let { result -> finishHtml(result, content) } method(obj)?.let { result -> finishHtml(result, content, doLog) }
} }
method.getAnnotation(Text::class.java) != null -> { content -> method.getAnnotation(Text::class.java) != null -> { content ->
method(obj)?.let { result -> finishText(result, content) } method(obj)?.let { result -> finishText(result, content, doLog) }
} }
method.getAnnotation(Json::class.java) != null -> { content -> method.getAnnotation(Json::class.java) != null -> { content ->
method(obj)?.let { result -> finishJson(result, content) } method(obj)?.let { result -> finishJson(result, content, doLog) }
} }
else -> { content -> else -> { content ->
method(obj)?.let { result -> autoReturn(method, result, content) } method(obj)?.let { result -> autoReturn(method, result, content, doLog) }
} }
} }
} else when (method.returnType) { } else when (method.returnType) {
@ -153,16 +155,16 @@ open class RoutedHttpHandler(
Unit::class.java -> { content -> method(obj, content) } Unit::class.java -> { content -> method(obj, content) }
else -> when { else -> when {
method.getAnnotation(Html::class.java) != null -> { content -> method.getAnnotation(Html::class.java) != null -> { content ->
method(obj, content)?.let { result -> finishHtml(result, content) } method(obj, content)?.let { result -> finishHtml(result, content, doLog) }
} }
method.getAnnotation(Text::class.java) != null -> { content -> method.getAnnotation(Text::class.java) != null -> { content ->
method(obj, content)?.let { result -> finishText(result, content) } method(obj, content)?.let { result -> finishText(result, content, doLog) }
} }
method.getAnnotation(Json::class.java) != null -> { content -> method.getAnnotation(Json::class.java) != null -> { content ->
method(obj, content)?.let { result -> finishJson(result, content) } method(obj, content)?.let { result -> finishJson(result, content, doLog) }
} }
else -> { content: HttpContent -> else -> { content: HttpContent ->
method(obj, content)?.let { result -> autoReturn(method, result, content) } method(obj, content)?.let { result -> autoReturn(method, result, content, doLog) }
} }
} }
}).let { }).let {
@ -273,6 +275,8 @@ open class RoutedHttpHandler(
null null
} }
val Method.doLog get() = getAnnotation(NoReturnLog::class.java) == null
fun <T> T.repeatUntil(state: (T) -> Boolean, block: (T) -> T): T { fun <T> T.repeatUntil(state: (T) -> Boolean, block: (T) -> T): T {
var result = this var result = this
while (state(result)) { while (state(result)) {
@ -288,15 +292,16 @@ open class RoutedHttpHandler(
if (it.endsWith('/')) it.dropLast(1) else it if (it.endsWith('/')) it.dropLast(1) else it
}.repeatUntil({ it.contains("//") }) { it.replace(slashRegex, "/") } }.repeatUntil({ it.contains("//") }) { it.replace(slashRegex, "/") }
fun autoReturn(method: Method, result: Any?, content: HttpContent) { fun autoReturn(method: Method, result: Any?, content: HttpContent, doLog: Boolean? = null) {
method.getAnnotation(ContextType::class.java)?.let { method.getAnnotation(ContextType::class.java)?.let {
content.autoContextType(it.type) content.setContextType(it.type.value)
log?.debug("{}: autoReturn context type auto set to {}({})", content.clientIp, it.type.key, it.type.value)
} }
autoReturn(result, content) autoReturn(result, content, doLog ?: method.doLog)
} }
fun autoReturn(result: Any?, content: HttpContent) { fun autoReturn(result: Any?, content: HttpContent, doLog: Boolean = true) {
log?.debug("{}: autoReturn: {}", content.clientIp, result) if (doLog) log?.debug("{}: autoReturn: {}", content.clientIp, result)
result ?: return result ?: return
when (result) { when (result) {
is String -> content.finishText(result.toByteArray()) is String -> content.finishText(result.toByteArray())
@ -315,8 +320,8 @@ open class RoutedHttpHandler(
} }
} }
fun finishHtml(result: Any?, content: HttpContent) { fun finishHtml(result: Any?, content: HttpContent, doLog: Boolean = true) {
log?.debug("{}: finishHtml: {}", content.clientIp, result) if (doLog) log?.debug("{}: finishHtml: {}", content.clientIp, result)
result ?: return result ?: return
when (result) { when (result) {
is ByteBuffer -> content.finishHtml(result) is ByteBuffer -> content.finishHtml(result)
@ -332,8 +337,8 @@ open class RoutedHttpHandler(
} }
} }
fun finishText(result: Any?, content: HttpContent) { fun finishText(result: Any?, content: HttpContent, doLog: Boolean = true) {
log?.debug("{}: finishText: {}", content.clientIp, result) if (doLog) log?.debug("{}: finishText: {}", content.clientIp, result)
result ?: return result ?: return
when (result) { when (result) {
is ByteBuffer -> content.finishText(result) is ByteBuffer -> content.finishText(result)
@ -349,8 +354,8 @@ open class RoutedHttpHandler(
} }
} }
fun finishJson(result: Any?, content: HttpContent) { fun finishJson(result: Any?, content: HttpContent, doLog: Boolean = true) {
log?.debug("{}: finishJson: {}", content.clientIp, result) if (doLog) log?.debug("{}: finishJson: {}", content.clientIp, result)
result ?: return result ?: return
when (result) { when (result) {
is ByteBuffer -> content.finishJson(result) is ByteBuffer -> content.finishJson(result)
@ -365,7 +370,7 @@ open class RoutedHttpHandler(
} }
else -> { else -> {
val json = json?.toJson(result) val json = json?.toJson(result)
log?.debug("{}: finishJson: generate json: {}", content.clientIp, json) if (doLog) log?.debug("{}: finishJson: generate json: {}", content.clientIp, json)
if (json != null) { if (json != null) {
content.finishJson(json.toByteArray()) content.finishJson(json.toByteArray())
} else { } else {

View File

@ -1,3 +1,3 @@
package cn.tursom.web.utils package cn.tursom.web.utils
annotation class ContextType(val type: String) annotation class ContextType(val type: ContextTypeEnum)

View File

@ -0,0 +1,160 @@
package cn.tursom.web.utils
import cn.tursom.core.datastruct.StringRadixTree
@Suppress("EnumEntryName", "unused", "SpellCheckingInspection")
enum class ContextTypeEnum(val key: String, val value: String) {
aac("aac", "audio/aac"),
abw("abw", "application/x-abiword"),
arc("arc", "application/x-freearc"),
avi("avi", "video/x-msvideo"),
azw("azw", "aapplication/vnd.amazon.ebook"),
bin("bin", "application/octet-stream"),
bmp("bmp", "image/bmp"),
bz("bz", "application/x-bzip"),
bz2("bz2", "application/x-bzip2"),
csh("csh", "application/x-csh"),
css("css", "itext/css; charset=UTF-8"),
csv("csv", "itext/csv; charset=UTF-8"),
doc("doc", "application/msword"),
docx("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
eot("eot", "application/vnd.ms-fontobject"),
epub("epub", "application/epub+zip"),
gif("gif", "image/gif"),
html("html", "text/html; charset=UTF-8"),
htm("htm", "text/html; charset=UTF-8"),
ico("ico", "image/vnd.microsoft.icon"),
ics("ics", "text/calendar; charset=UTF-8"),
jar("jar", "application/java-archive"),
jpeg("jpeg", "image/jpeg"),
jpg("jpg", "image/jpeg"),
js("js", "text/javascript; charset=UTF-8"),
json("json", "application/json; charset=UTF-8"),
jsonld("jsonld", "application/ld+json"),
mid("mid", "audio/midi"),
midi("midi", "audio/midi"),
mjs("mjs", "text/javascript"),
mp3("mp3", "audio/mpeg"),
mp4("mp4", "audio/mp4"),
mpeg("mpeg", "video/mpeg"),
mpkg("mpkg", "application/vnd.apple.installer+xml"),
odp("odp", "application/vnd.oasis.opendocument.presentation"),
ods("ods", "application/vnd.oasis.opendocument.spreadsheet"),
odt("odt", "application/vnd.oasis.opendocument.text"),
oga("oga", "audio/ogg"),
ogg("ogg", "application/ogg"),
ogv("ogv", "video/ogg"),
ogx("ogx", "application/ogg"),
otf("otf", "font/otf"),
png("png", "image/png"),
pdf("pdf", "application/pdf"),
ppt("ppt", "application/vnd.ms-powerpoint"),
pptx("pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"),
rar("rar", "application/x-rar-compressed"),
rtf("rtf", "application/rtf"),
sh("sh", "application/x-sh"),
svg("svg", "image/svg+xml"),
swf("swf", "application/x-shockwave-flash"),
tar("tar", "application/x-tar"),
tif("tif", "image/tiff"),
tiff("tiff", "image/tiff"),
ttf("ttf", "font/ttf"),
txt("txt", "text/plain; charset=UTF-8"),
vsd("vsd", "application/vnd.visio"),
wav("wav", "audio/wav"),
weba("weba", "audio/webm"),
webm("webm", "video/webm"),
webp("webp", "image/webp"),
woff("woff", "font/woff"),
woff2("woff2", "font/woff2"),
xhtml("xhtml", "application/xhtml+xml"),
xls("xls", "application/vnd.ms-excel"),
xlsx("xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"),
xml("xml", "text/xml; charset=UTF-8"),
xul("xul", "application/vnd.mozilla.xul+xml"),
zip("zip", "application/zip"),
`3gp`("3gp", "video/3gpp"),
`3g2`("3g2", "ivideo/3gpp2"),
`7z`("7z", "application/x-7z-compressed");
companion object {
private val router = StringRadixTree<ContextTypeEnum>()
init {
router["aac"] = aac
router["abw"] = abw
router["arc"] = arc
router["avi"] = avi
router["azw"] = azw
router["bin"] = bin
router["bmp"] = bmp
router["bz"] = bz
router["bz2"] = bz2
router["csh"] = csh
router["css"] = css
router["csv"] = csv
router["doc"] = doc
router["docx"] = docx
router["eot"] = eot
router["epub"] = epub
router["gif"] = gif
router["html"] = html
router["htm"] = htm
router["ico"] = ico
router["ics"] = ics
router["jar"] = jar
router["jpeg"] = jpeg
router["jpg"] = jpg
router["js"] = js
router["json"] = json
router["jsonld"] = jsonld
router["mid"] = mid
router["midi"] = midi
router["mjs"] = mjs
router["mp3"] = mp3
router["mp4"] = mp4
router["mpeg"] = mpeg
router["mpkg"] = mpkg
router["odp"] = odp
router["ods"] = ods
router["odt"] = odt
router["oga"] = oga
router["ogg"] = ogg
router["ogv"] = ogv
router["ogx"] = ogx
router["otf"] = otf
router["png"] = png
router["pdf"] = pdf
router["ppt"] = ppt
router["pptx"] = pptx
router["rar"] = rar
router["rtf"] = rtf
router["sh"] = sh
router["svg"] = svg
router["swf"] = swf
router["tar"] = tar
router["tif"] = tif
router["tiff"] = tiff
router["ttf"] = ttf
router["txt"] = txt
router["vsd"] = vsd
router["wav"] = wav
router["weba"] = weba
router["webm"] = webm
router["webp"] = webp
router["woff"] = woff
router["woff2"] = woff2
router["xhtml"] = xhtml
router["xls"] = xls
router["xlsx"] = xlsx
router["xml"] = xml
router["xul"] = xul
router["zip"] = zip
router["3gp"] = `3gp`
router["3g2"] = `3g2`
router["7z"] = `7z`
}
operator fun get(value: String) = router[value] ?: throw IllegalAccessException()
}
}

View File

@ -80,4 +80,6 @@ object ContextTypeMap {
router["3g2"] = "ivideo/3gpp2" router["3g2"] = "ivideo/3gpp2"
router["7z"] = "application/x-7z-compressed" router["7z"] = "application/x-7z-compressed"
} }
} }

View File

@ -0,0 +1,3 @@
package cn.tursom.web.utils
annotation class NoReturnLog

View File

@ -8,10 +8,10 @@ import cn.tursom.web.result.Json
import cn.tursom.web.result.Text import cn.tursom.web.result.Text
import cn.tursom.web.router.impl.SimpleRouter import cn.tursom.web.router.impl.SimpleRouter
import cn.tursom.web.utils.ContextType import cn.tursom.web.utils.ContextType
import cn.tursom.web.utils.NoReturnLog
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.lang.reflect.Method
import kotlin.reflect.KCallable import kotlin.reflect.KCallable
import kotlin.reflect.full.findAnnotation import kotlin.reflect.full.findAnnotation
import kotlin.reflect.jvm.jvmErasure import kotlin.reflect.jvm.jvmErasure
@ -26,7 +26,7 @@ open class AsyncRoutedHttpHandler(
protected val asyncRouterMap: HashMap<String, Router<Pair<Any?, suspend (HttpContent) -> Unit>>> = HashMap() protected val asyncRouterMap: HashMap<String, Router<Pair<Any?, suspend (HttpContent) -> Unit>>> = HashMap()
override fun handle(content: HttpContent) { override fun handle(content: HttpContent) {
log?.debug("{}: {} {}", content.clientIp, content.method, content.uri) log?.debug("{} {} {}", content.clientIp, content.method, content.uri)
if (content is MutableHttpContent) { if (content is MutableHttpContent) {
val handler = getAsyncHandler(content, content.method, content.uri) val handler = getAsyncHandler(content, content.method, content.uri)
if (handler != null) GlobalScope.launch { if (handler != null) GlobalScope.launch {
@ -103,19 +103,20 @@ open class AsyncRoutedHttpHandler(
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun addRouter(obj: Any, method: KCallable<*>, route: String, router: Router<Pair<Any?, suspend (HttpContent) -> Unit>>) { fun addRouter(obj: Any, method: KCallable<*>, route: String, router: Router<Pair<Any?, suspend (HttpContent) -> Unit>>) {
val doLog = method.doLog
router[safeRoute(route)] = if (method.parameters.size == 1) { router[safeRoute(route)] = if (method.parameters.size == 1) {
obj to when { obj to when {
method.findAnnotation<Html>() != null -> { content -> method.findAnnotation<Html>() != null -> { content ->
(method as suspend Any.() -> Any?)(obj)?.let { result -> finishHtml(result, content) } (method as suspend Any.() -> Any?)(obj)?.let { result -> finishHtml(result, content, doLog) }
} }
method.findAnnotation<Text>() != null -> { content -> method.findAnnotation<Text>() != null -> { content ->
(method as suspend Any.() -> Any?)(obj)?.let { result -> finishText(result, content) } (method as suspend Any.() -> Any?)(obj)?.let { result -> finishText(result, content, doLog) }
} }
method.findAnnotation<Json>() != null -> { content -> method.findAnnotation<Json>() != null -> { content ->
(method as suspend Any.() -> Any?)(obj)?.let { result -> finishJson(result, content) } (method as suspend Any.() -> Any?)(obj)?.let { result -> finishJson(result, content, doLog) }
} }
else -> { content -> else -> { content ->
(method as suspend Any.() -> Any?)(obj)?.let { result -> autoReturn(method, result, content) } (method as suspend Any.() -> Any?)(obj)?.let { result -> autoReturn(method, result, content, doLog) }
} }
} }
} else obj to when (method.returnType.jvmErasure.java) { } else obj to when (method.returnType.jvmErasure.java) {
@ -124,16 +125,16 @@ open class AsyncRoutedHttpHandler(
Unit::class.java -> { content -> (method as suspend Any.(HttpContent) -> Unit)(obj, content) } Unit::class.java -> { content -> (method as suspend Any.(HttpContent) -> Unit)(obj, content) }
else -> when { else -> when {
method.findAnnotation<Html>() != null -> { content -> method.findAnnotation<Html>() != null -> { content ->
(method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> finishHtml(result, content) } (method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> finishHtml(result, content, doLog) }
} }
method.findAnnotation<Text>() != null -> { content -> method.findAnnotation<Text>() != null -> { content ->
(method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> finishText(result, content) } (method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> finishText(result, content, doLog) }
} }
method.findAnnotation<Json>() != null -> { content -> method.findAnnotation<Json>() != null -> { content ->
(method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> finishJson(result, content) } (method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> finishJson(result, content, doLog) }
} }
else -> { content -> else -> { content ->
(method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> autoReturn(method, result, content) } (method as suspend Any.(HttpContent) -> Any?)(obj, content)?.let { result -> autoReturn(method, result, content, doLog) }
} }
} }
} }
@ -229,11 +230,14 @@ open class AsyncRoutedHttpHandler(
null null
} }
fun autoReturn(method: KCallable<*>, result: Any?, content: HttpContent) { val KCallable<*>.doLog get() = findAnnotation<NoReturnLog>() == null
fun autoReturn(method: KCallable<*>, result: Any?, content: HttpContent, doLog: Boolean? = null) {
method.findAnnotation<ContextType>()?.let { method.findAnnotation<ContextType>()?.let {
content.autoContextType(it.type) content.setContextType(it.type.value)
log?.debug("{}: autoReturn context type auto set to {}({})", content.clientIp, it.type.key, it.type.value)
} }
autoReturn(result, content) autoReturn(result, content, doLog ?: method.doLog)
} }
} }
} }