Play提交Form表单汉字乱码

分享 未结
0 0 4 19
意式Espresso 2018-04-13发布
收藏 点赞

controller:

case class UserFormData(username: String, description: String, password: String)
@Singleton
class ApplicationController @Inject()(cc: ControllerComponents, userinfoService: UserinfoService) extends AbstractController(cc) {
  //定义表单映射
  val userForm = Form(
    mapping(
      "username" -> nonEmptyText,
      "description" -> nonEmptyText,
      "password" -> nonEmptyText
    )(UserFormData.apply)(UserFormData.unapply)
  )
  def add = Action.async{
    implicit request =>
      userForm.bindFromRequest.fold(
        hasErrors => Future.successful(BadRequest("No data")),
        data =>{
          //新建用户对象
          val newUser = Userinfo(0, data.username, data.description, data.password)
          //插入数据库
          userinfoService.add(newUser).map(res => Redirect("/users"))
        }
      )
  }
}

view:

<form role="form" action="@routes.ApplicationController.add()" method="post">
    <div class="form-group">
        <label>User name</label>
        <input type="text" class="form-control" name="username" placeholder="username">
    </div>
    <div class="form-group">
        <label>Description</label>
        <input type="text" class="form-control" name="description" placeholder="description"/>
    </div>
    <div class="form-group">
        <label>Password</label>
        <input type="password" class="form-control" name="password" placeholder="Password">
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
    <a href="/users" class="btn btn-info" class="btn btn-primary">Return</a>
</form>


结果范例:

表单填写加入汉字

结果:

似乎需要将POST的内容进行解码,小编有什么好的解决方法,万分感谢哈!

回帖
  • 2018-04-13

    出现上面的乱码可能有两个原因:

    1)Play的BodyParser在解析请求体时出现了乱码

    2)在插入数据库时出现了乱码

    如果是第 1)种情况,则需要在生成HTML页面时明确告知浏览器字符编码。有两种方式,一是在生成HTML页面时插入meta标签,告诉浏览器使用utf-8编码表单参数:

    <head>
      <meta charset="utf-8"/>
    ...
    

    二是在HTTP响应中加上Content-Type响应头,作用和meta标签相同,但优先级更高:

    Content-Type:text/html; charset=utf-8
    

    上面的设置解决了浏览器端的编码设置问题,接着我们再看看Play如何解析表单参数。

    如果没有特别指定,Play默认根据Content-Type请求头解析请求内容:

    /**
     * Guess the body content by checking the Content-Type header.
     */
    def anyContent(maxLength: Option[Long]): BodyParser[AnyContent] = BodyParser("anyContent") { request =>
      import play.core.Execution.Implicits.trampoline
    
      def maxLengthOrDefault = maxLength.fold(DefaultMaxTextLength)(_.toInt)
      def maxLengthOrDefaultLarge = maxLength.getOrElse(DefaultMaxDiskLength)
      val contentType: Option[String] = request.contentType.map(_.toLowerCase(Locale.ENGLISH))
      contentType match {
        case Some("text/plain") =>
          logger.trace("Parsing AnyContent as text")
          text(maxLengthOrDefault)(request).map(_.right.map(s => AnyContentAsText(s)))
    
        case Some("text/xml") | Some("application/xml") | Some(ApplicationXmlMatcher()) =>
          logger.trace("Parsing AnyContent as xml")
          xml(maxLengthOrDefault)(request).map(_.right.map(x => AnyContentAsXml(x)))
    
        case Some("text/json") | Some("application/json") =>
          logger.trace("Parsing AnyContent as json")
          json(maxLengthOrDefault)(request).map(_.right.map(j => AnyContentAsJson(j)))
    
        case Some("application/x-www-form-urlencoded") =>
          logger.trace("Parsing AnyContent as urlFormEncoded")
          formUrlEncoded(maxLengthOrDefault)(request).map(_.right.map(d => AnyContentAsFormUrlEncoded(d)))
    
        case Some("multipart/form-data") =>
          logger.trace("Parsing AnyContent as multipartFormData")
          multipartFormData(Multipart.handleFilePartAsTemporaryFile(temporaryFileCreator), maxLengthOrDefaultLarge).apply(request)
            .map(_.right.map(m => AnyContentAsMultipartFormData(m)))
    
        case _ =>
          logger.trace("Parsing AnyContent as raw")
          raw(DefaultMaxTextLength, maxLengthOrDefaultLarge)(request).map(_.right.map(r => AnyContentAsRaw(r)))
      }
    }
    

    用于解析请求参数的字符编码也是从Content-Type请求头中解析,如果未指定则默认使用utf-8:

    /**
     * Parse the body as Form url encoded without checking the Content-Type.
     *
     * @param maxLength Max length (in bytes) allowed or returns EntityTooLarge HTTP response.
     */
    def tolerantFormUrlEncoded(maxLength: Int): BodyParser[Map[String, Seq[String]]] =
      tolerantBodyParser("formUrlEncoded", maxLength, "Error parsing application/x-www-form-urlencoded") { (request, bytes) =>
        import play.core.parsers._
        val charset = request.charset.getOrElse("UTF-8")
        val urlEncodedString = bytes.decodeString("UTF-8")
        FormUrlEncodedParser.parse(urlEncodedString, charset)
      }
    

    通常表单提交时Content-Type内容如下:

    Content-Type:application/x-www-form-urlencoded
    

    所以Play会使用utf-8解析请求参数。由于前、后端使用相同字符编码,故可以解决乱码问题。

    如果是第 2)种情况,则需要先按照数据库的字符集进行编码,然后再执行插入操作。

    0 回复
  • 2018-04-13

    关于HTML字符编码设置可以参考这里:

    https://www.w3.org/International/questions/qa-html-encoding-declarations

    0 回复
  • 感谢小编,通过排除,确实是第二种情况,我通过日志发现只有在使用Slick进行数据库插入操作的时候出现乱码了,直接在mysql的client中使用SQL是可以直接连接的,后来查看了数据库状态:

    只有Server characterset是latin1,但又不想更改MySQL配置进行重启,最后通过在jdbc的url中添加`useUnicode=true&characterEncoding=UTF-8`就一切正常了,可以插入中文了。

    JDBC URL的参数的设置也很有用啊,最后再次感谢小编!

    1 回复
  • 2018-04-16

    不客气的哦!

    0 回复