xml上传需显式启用plaintext支持并设置content-type: application/xml,servant不自动解析xml,须在handler中用xml-conduit等手动解析bytestring。

XML上传API必须显式启用ReqBody和PlainText支持
Servant默认不处理原始XML请求体,即使你写了ReqBody '[PlainText] ByteString,也会因缺少对应MimeRender和MimeUnrender实例而编译失败。关键不是“怎么写类型”,而是“怎么让Servant认出XML是合法的请求格式”。
- 在
cabal文件中添加servant-server和text依赖(xml-conduit可选,仅用于解析) - 在模块顶部导入:
import Servant import Servant.Server import Data.ByteString (ByteString) import Data.Text (Text)
- 必须手动为
XMLMIME类型提供MimeRender和MimeUnrender实例——哪怕你只收不返,MimeRender也得空实现
定义XML上传端点时用PlainText而非JSON或自定义MIME
别试图写ReqBody '[MimeRender XML ByteString] ByteString,这会报错:Servant要求MimeRender和MimeUnrender必须成对存在,且类型参数需匹配。最稳妥的做法是复用PlainText,它已内置text/plain、application/xml等常见MIME的实例(只要你在servant-server >= 0.19中)。
- API类型定义示例:
type UploadAPI = "upload" :> ReqBody '[PlainText] ByteString :> PostNoContent '[PlainText] NoContent
-
PlainText支持application/xml,但客户端必须显式设置Content-Type: application/xml,否则Servant按text/plainfallback,可能被中间件拦截 - 若需校验XML结构,应在handler里用
xml-conduit或hxt解析ByteString,而不是靠Servant自动解码
serve前必须调用setVerbose并检查Content-Type日志
XML上传失败时,Servant通常静默返回415 Unsupported Media Type,根本不会进你的handler。这不是bug,是MIME协商失败——意味着请求头没传对,或者PlainText没覆盖到你用的MIME。
- 启动server时加日志:
main = run 8080 $ serve (Proxy :: Proxy UploadAPI) server where server = uploadHandler :<|> emptyServer uploadHandler :: ByteString -> Handler NoContent uploadHandler xmlBS = liftIO (print ("Got XML bytes:", B.length xmlBS)) >> return NoContent - 用
curl测试必须带-H "Content-Type: application/xml",缺了就415 - 如果用
text/xml,需额外给PlainText加实例,application/xml才是开箱即用的
真正解析XML要绕过Servant的自动解码机制
Servant没有FromHttpApiData for XML的通用支持,也不推荐写FromXML类——XML schema千差万别,强行抽象反而增加耦合。直接收ByteString,在handler里解析最可控。
- 解析示例(用
xml-conduit):import Text.XML.Conduit import Data.Conduit.List (sinkList) import qualified Data.ByteString.Lazy as L <p>parseXML :: ByteString -> IO (Either String [Text]) parseXML bs = do let lbs = L.fromStrict bs res <- runConduit $ parseXMLConduit lbs .| sinkList return $ case res of Left e -> Left (show e) Right es -> Right (map (T.pack . show) es)
- 注意
ByteString是UTF-8编码,xml-conduit默认按UTF-8读;若XML声明含encoding="ISO-8859-1",需先转码再喂给解析器 - 别在handler里做重型XML验证(如XSD),容易阻塞线程;考虑用
async或移到后台任务
Servant对XML上传的支持本质是“放行原始字节+靠HTTP头驱动”,所有解析逻辑必须由你亲手接管。最容易卡住的地方不是语法,而是忘了设Content-Type头,或者误以为ReqBody能自动把XML转成Haskell record。










