相关文章推荐
独立的汉堡包  ·  c++ 超时机制-掘金·  5 月前    · 

boot版本:2.6.4

这阵子做开发,用到了callback jsonp。

之前对这种方式返回的数据中文编码问题没有特别注意过,出现了乱码(想想确实啊,这个小问题应该早就注意到,惭愧惭愧)。因此,这里特别做了多个实验返回”text/html“数据,查看其编码情况,不为别的,就是为了加深记忆和理解。

首先是,咱们先只考虑在controller内部处理乱码问题,如何解决呢?

函数内部解决

response回写

首先是,直接采用response的writer回写数据

 public void sss(HttpServletRequest request, HttpServletResponse response) {
        String sss = "北京你好!-jfQQQQ";
        try {
//            response.setCharacterEncoding("utf-8");
//            response.setHeader("Content-Type", "text/html; charset=utf-8");
//            response.setHeader("Content-Type","text/html");
            PrintWriter writer = response.getWriter();
            writer.write(sss.toCharArray());
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();

页面上查看

响应信息查看

在案例一的基础上将文字内容进行utf8编码化再返回

public void sss(HttpServletRequest request, HttpServletResponse response) {
        String sss = "北京你好!-jfQQQQ";
        try {
            response.setCharacterEncoding("utf-8");
//            response.setHeader("Content-Type", "text/html; charset=utf-8");
//            response.setHeader("Content-Type","text/html");
            PrintWriter writer = response.getWriter();
            writer.write(sss.toCharArray());
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();

页面上查看

响应信息查看

可以看到,已经不是“ ?”号了。

案例二基础上,增加返回数据类型说明。 

  public void sss(HttpServletRequest request, HttpServletResponse response) {
        String sss = "北京你好!-jfQQQQ";
        try {
            response.setCharacterEncoding("utf-8");
//            response.setHeader("Content-Type", "text/html; charset=utf-8");
            response.setHeader("Content-Type","text/html");
            PrintWriter writer = response.getWriter();
            writer.write(sss.toCharArray());
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();

页面上查看

响应信息查看

可以看到,已经可以显示正常的中文了 。并且,springboot自动增加了“charset=utf8”字样。

所以,下边的代码与上方代码效果一致:

public void sss(HttpServletRequest request, HttpServletResponse response) {
        String sss = "北京你好!-jfQQQQ";
        try {
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-Type", "text/html; charset=utf-8");
//            response.setHeader("Content-Type","text/html");//springboot在返回时会自动增加“charset=utf8”字样,所以效果同上方代码
            PrintWriter writer = response.getWriter();
            writer.write(sss.toCharArray());
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();

在拼写“content-type”字样时会容易出现拼错了的情况,response API里有直接设置响应头的办法,就很省事:

public void sss(HttpServletRequest request, HttpServletResponse response) {
        String sss = "北京你好!-jfQQQQ";
        try {
            response.setCharacterEncoding("utf-8");
            response.setContentType("text/html; charset=utf-8");
//            response.setHeader("content-tYpE", "text/html; charset=utf-8");
//            response.setHeader("Content-Type","text/html");//springboot在返回时会自动增加“charset=utf8”字样,所以效果同上方代码
            PrintWriter writer = response.getWriter();
            writer.write(sss.toCharArray());
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();

这是一个错误的使用案例,“content-type”很容易写错,或者有时候由于理解不透彻导致拼写错误,包括在不明所以的情况下,不知道 大小写 以及 “-” 的影响。所以,值得仔细测试一下,并总结下来

    public void sss(HttpServletRequest request, HttpServletResponse response) {
        String sss = "北京你好!-jfQQQQ";
        try {
            response.setCharacterEncoding("utf-8");
            response.setHeader("contentType", "text/html; charset=utf-8");//contentType拼写不对,应该是Content-Type
            PrintWriter writer = response.getWriter();
            writer.write(sss.toCharArray());
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();

页面上查看:

响应头查看

原因是“Content-Type”写成了“contentType”。

于是,我又新奇的这样写:contenttype,效果与上面一样。

但是,这样写:content-type,就正确了,查看响应头:

 于是,我又这样写:content-tYpE,依旧是正确的可以的。

所以,只要把content和type之间用“-”连接,就行了,不用在乎大小写。

基于案例一,在函数上方增加produces配置

这里简化代码,只保留了增加produces配置的部分:

 @GetMapping(value = "/info",produces = "text/html;charset=utf-8")

同案例一,没有任何效果。

因为案例一的行为是自己的response的输出,脱离了spring,

通过类比,可以发现是由于response返回的header中没有指定content-type类型和编码导致的。

Springboot的注解使用案例

ok,上面是虽然在springboot下,但是根本没有使用springboot特性(除了案例六,不过虽然写了produces,但是实际上也是没用到的)。那么使用springboot的特性也是会有这种情况的。

    @ResponseBody
    public String findCurrentUserInfo2(HttpServletRequest request) {
        String sss = "北京你好!-jfQQQQ";
        return sss;

这也说明了当前springboot上下文默认使用的是ISO-8859-1编码。所以,前面的response的使用时也是由于这个原因,由此,这里抛出一个更好的解决办法:直接设定springboot的编码。

直接返回字节数组,考虑编码为utf-8.

    @ResponseBody
    public byte[] findCurrentUserInfo2(HttpServletRequest request) {
        String sss = "北京你好!-jfQQQQ";
        return sss.getBytes(StandardCharsets.UTF_8);

这种情况下,页面展示是乱码,是由于response header中没有指定编码,但是对于获取到数据的js代码来说,是可以自行解码使用的。 

就是在produces里增加了“text/html;charset=utf-8”设置

 @GetMapping(value = "/info",produces = "text/html;charset=utf-8")
    @ResponseBody
    public String findCurrentUserInfo2(HttpServletRequest request) {
        String sss = "北京你好!-jfQQQQ";
        return sss;//.getBytes(StandardCharsets.UTF_8);

直接设定springboot的编码

server.servlet.encoding.charset=UTF-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=truer
然后在此基础上,实验案例若干:

response直接写

public void sss(HttpServletRequest request, HttpServletResponse response) {
        String sss = "北京你好!-jfQQQQ";
        try {
//            response.setCharacterEncoding("utf-8");
//            response.setContentType("text/html; charset=utf-8");
//            response.setHeader("content-tYpE", "text/html; charset=utf-8");
//            response.setHeader("Content-Type","text/html");//springboot在返回时会自动增加“charset=utf8”字样,所以效果同上方代码
            PrintWriter writer = response.getWriter();
            writer.write(sss.toCharArray());
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
    @ResponseBody
    public String findCurrentUserInfo2(HttpServletRequest request) {
        String sss = "北京你好!-jfQQQQ";
        return sss;//.getBytes(StandardCharsets.UTF_8);

结论就是依旧不行:

并且可见原因是编码为ISO了!可是明明有调试源码,utf-8的properties设定的实体类有被创建,并且值就是utf-8啊!难道没用到那个类?(org.springframework.boot.web.servlet.server.Encoding) 

结合上面的案例,得出的结论是我们配置的properties并没有起到作用,那么我们就需要找到起到作用的类和方法,spring中转换消息的类一般都是“***Convertor”,再加上我们返回的是text/html,类似于string,所以自然是找到了org.springframework.http.converter.StringHttpMessageConverter类,果不其然,里面的编码默认是ISO:

 so,我们来调试一下,看看返回数据的时候是不是用到了它。

ok,确认了。

接下来,跟源码(org.springframework.http.converter.StringHttpMessageConverter中),返回数据时,会有一个判断响应头的逻辑,如果controller的函数返回时没有满足if中判断的条件:

 那么,就执行默认的响应头(调用到了org.springframework.http.converter.AbstractHttpMessageConverter中的addDefaultHeaders方法),如下图:

同时,由于我只配置了produces为“text/html”,所以上面的断点处,就走到了获取

his.getDefaultCharset();

这一句,要去获取默认的charset,我们看看它要去从哪里获取:

还是org.springframework.http.converter.AbstractHttpMessageConverter中,它自己存储了默认的编码值:

 这个defaultCharset是在创建时指定的,截图时是utf-8。

不过这个属性是在创建时指定的,我们看看这个属性是在哪里赋值的。

赋值是在创建时指定的(org.springframework.http.converter.AbstractHttpMessageConverter):

 上面的创建是在下图被调用的

然后上图是在下图被调用的

然后下图就是上图用到的默认编码值,于是走到迷宫尽头了,可以出宫了。

所以,关键问题就在于我们让springboot启动初始化时,将类的默认编码换为utf-8就行了,而那个类是StringHttpMessageConverter 。

所以,我们要这样自创建一个bean,设定它的类别,再注入到spring上下文中,替换其默认的bean。

@Bean
public HttpMessageConverter<String> responseBodyConverter() {
   StringHttpMessageConverter converter = new StringHttpMessageConverter(
         Charset.forName("UTF-8"));
   return converter;
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
   super.configureMessageConverters(converters);
   converters.add(responseBodyConverter());
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
   configurer.favorPathExtension(false);
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
	@Bean
	public HttpMessageConverter<String> responseBodyConverter() {
		StringHttpMessageConverter converter = new StringHttpMessageConverter(
				Charset.forName("UTF-8"));
		return converter;
	@Override
	public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
		super.configureMessageConverters(converters);
		converters.add(responseBodyConverter());
	@Override
	public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
		configurer.favorPathExtension(false);

用这种方式配置后,就可以不在controller中做特殊处理来使用了。

在做拦截器中,没有该注解,所以不会对返回的类做处理,所以返回的就是toString后的对象。所以在redsponse参数中,一定事先使用使用JSON.toJSONString(userResult)对类对象进行一... HttpServletRequest request =((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); HttpServletResponse response =((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getResponse(); 一般使用 HttpServletReque. HttpServletRequest request =((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); HttpServletResponse response =((ServletRequestAttributes)RequestContextHolder.getRe... import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServl @WebServlet("/wx") public class WxServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void. 1、通过Springboot程序可以发现,Springboot中控制器的形式和Springmvc中是一样的,因此在程序中使用jsp的内置对象也可以按照与Springmvc同样的方式进行。 1 package org.springboot.tentent.controller; 3 import java.util.HashMap; 4 import java.util.M... 2、支持跨域,不限制跨域(????????,最后还是采用了这个) 配置跨域支持 两个前段一个后台,如果用Nginx配置一下不同的域名转发一下也没有问题,但由于合作方基本不用Nginx,全靠slb拦截之类的骚操作,导致后端只能开放为允许跨域。 配置方式很简单,直接给贴个全的,感动不感动,-,- @Configuration public class WebMv @RequestMapping("/testServletAPI") public void testServletAPI(HttpServletRequest request, HttpServletResponse response,HttpSession session, Write r out) throws IOException{ 通常来说依赖注入的对象一旦创建完成后就不会在改变,因为Spring的默认行为创建的都是单例对象。和不是单例对象,它们都是根据HTTP请求进行创建的,一个HTTP请求对象一个request和response。但是通过@Autowired可以实现每当一个请求进来使用的都是当前的request或response对象,演示代码如下。 根据浏览器提交的账号密码返回登录成功或者失败 这一步本来应该通过访问数据库来实现,这里简化一下,直接在内存中进行校验 如果账号是 admin,密码是123, 就返回登录成功,否则返回登录失败 步骤1:先运行,看到效果,再学习步骤2:模仿和排错步骤3:LoginServlet步骤4:重启tomcat,提交数据 步骤1:先运行,看到效果,再学习 老规矩,先下载下载区(点击进入)的...