springboot请求访问springboot接口,参数为另外一个url,url中含有# ,
springboot中获得参数时无法获得#和#后面部分;
例如
http://ip/SvgHome.html#/compiles
TokenData body = restTemplate.getForEntity(URL, TokenData.class).getBody();
assert body != null;
log.info(body.getResult());
String param=url+"&token="+body.getResult();
log.info(param);
modelAndView.setViewName("redirect:"+param);
return modelAndView;
实际跳转路径为http://ip:9143/SvgHome.html&token=aece153ba6b16a1b#/compiles
即把token部分拼接在中间了
查了写资料,说是#的缘故,但是不知道怎么处理,
上面那个问题不会的,解决下面这个也行,
springboot获得参数中包含#的部分,网上说配置tomcatconfig,但是我这个不管用
最近在项目中遇到了一个问题,需求变更要求,url中需要支持特殊字符,例如:“.”,“@”等。由于项目中包含了很多已经开放的接口,采用的是restful风格的,因此改接口方案直接被pass。在查找了很多资料,最终解决这个问题。
问题主要包括三个:
包含特殊url请求时,@ResponseBody 注解的请求,返回时会抛出异常
如何匹配包含特殊字符的url,例如/email/huang@gmail.com
采用注解方式自定义异常以json格式返回的,在包含特殊Url时,无法抛出自己想要的异常格式
注意:本文spring boot版本是基于1.5.16 (2.0以后可能有所不同)
问题一:包含特殊url请求时,@ResponseBody 注解的请求,返回时会抛出异常
package com.example.demo.controller;
import com.example.demo.domain.RestfulAPIResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
@author huangyichun
@date 2018/12/29
/
@RestController
public class EmailController {
@GetMapping("/user/{name}")
public RestfulAPIResponse queryUser(@PathVariable("name") String name) {
RestfulAPIResponse<String> restfulAPIResponse = new RestfulAPIResponse<>("requestId");
restfulAPIResponse.setRequestId("requestId");
restfulAPIResponse.setResult("name", name);
return restfulAPIResponse;
}
}
public class RestfulAPIResponse implements Serializable {
private String requestId;
private Map<String,R> result;
public RestfulAPIResponse(String requestId){
result = new HashMap<String,R>();
this.requestId = requestId;
}
public String getRequestId() {
return requestId;
}
public void setRequestId(String requestId) {
this.requestId = requestId;
}
public Map getResult() {
return result;
}
public void setResult(String key,R value) {
this.result.put(key,value);
}
public void setResultMap(Map<String, R> map) {
if (map != null && map.size() > 0) {
result.putAll(map);
}
}
}
请求 url : http://127.0.0.1:8080/user/huangyichun_@.com 返回值如下:
{
"timestamp": 1546072805982,
"status": 406,
"error": "Not Acceptable",
"exception": "org.springframework.web.HttpMediaTypeNotAcceptableException",
"message": "Could not find acceptable representation",
"path": "/user/huangyichun_@.com"
}
这个问题是由于使用外部Tomcat导致的,如果直接调用Spring boot的main方法启动的内部tomcat不会产生这个问题。
解决方法:添加如下配置,问题解决
package com.example.demo;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
/**
@author huangyichun
@date 2018/12/29
/
@Configuration
public class SpringServiceConfiguration extends WebMvcConfigurationSupport {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
// to avoid HttpMediaTypeNotAcceptableException on standalone tomcat
configurer.favorPathExtension(false);
}
}
调用url: 请求 url : http://127.0.0.1:8080/user/huangyichun_@.com 返回值如下:
{
"requestId": "requestId",
"result": {
"name": "huangyichun_@"
}
}
可以看出返回格式是正常了,但是发现返回的name和我们请求的name不太一样,这是因为Spring将url中的"."看做了分隔符,因此产生了问题二。
问题二:如何匹配包含特殊字符的url,例如/email/huang@gmail.com
解决方法如下:
package com.example.demo;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
/**
@author huangyichun
@date 2018/12/29
/
@Configuration
public class SpringServiceConfiguration extends WebMvcConfigurationSupport {
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
// to avoid HttpMediaTypeNotAcceptableException on standalone tomcat
configurer.favorPathExtension(false);
}
/**
{
"requestId": "requestId",
"result": {
"name": "huangyichun_@.com"
}
}
问题三:采用注解方式自定义异常以json格式返回的,在包含特殊Url时,无法抛出自己想要的异常格式
该场景出现的比较特殊,是在Controller方法中,使用RestTemplate调用其他服务,例如查询用户是否存在,结果为不存在,然后该服务抛出了一个not fount 404的json异常信息。项目中采用了注解方法进行异常处理,在返回时,Spring进行序列化后导致结果不是自己想要的格式。
解决思路:
让自定义异常处理类继承HandlerExceptionResolver,然后实现默认方法,对于抛出的异常进行最后的转换,结果符合自己需求。下面直接给出参考代码,和上面示例不是同一个项目,仅供参考
@ControllerAdvice
public class CustomExceptionHandler extends BaseController implements HandlerExceptionResolver {
private static Logger logger = LoggerFactory.getLogger(CustomExceptionHandler.class);
private ServiceError serviceError = new ServiceError();
@ExceptionHandler(HttpClientErrorException.class)
@ResponseBody
public OpenApiResponse httpClientErrorException(HttpServletRequest request, HttpServletResponse response, HttpClientErrorException ex){
logger.error("requestId:{} httpClientErrorException:{}",getRequestId(request),ex);
OpenApiResponse res = new OpenApiResponse(getRequestId(request));
String msg = ex.getResponseBodyAsString();
if(StringUtils.isNotBlank(msg)){
try{
JSONObject jsonObject = JSON.parseObject(msg);
int code = ex.getStatusCode().equals(HttpStatus.OK) ? 402 : ex.getStatusCode().value();
String status = jsonObject.getString("code");
String message = jsonObject.getString("message");
Map<String, String> detail = JsonUtils.jsonObject2Map(jsonObject.getJSONObject("details"));
serviceError.setCode(code);
serviceError.setMessage(StringUtils.isNotBlank(message) ? message : "内部调用错误");
serviceError.setStatus(StringUtils.isNotBlank(status) ? status : "INVOKE_ERROR");
if (detail != null) {
serviceError.setDetails(ArrayUtils.toArray(detail));
}
res.setError(serviceError);
response.setStatus(code);
return res;
}
catch (Exception e){
logger.error("解析错误,",e);
}
}
serviceError.setCode(500);
serviceError.setMessage("内部调用错误");
serviceError.setStatus("INNER_ERROR");
response.setStatus(500);
res.setError(serviceError);
return res;
}
@ExceptionHandler
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public OpenApiResponse defaultException(HttpServletRequest request,HttpServletResponse response, Exception e){
logger.error("requestId:{} spring捕获运行时异常:{}",getRequestId(request),e);
OpenApiResponse res = new OpenApiResponse(getRequestId(request));
ServiceError serviceError = new ServiceError();
serviceError.setCode(500);
serviceError.setMessage("内部错误");
serviceError.setStatus("INNER_ERROR");
res.setError(serviceError);
response.setStatus(500);
return res;
}
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
FastJsonJsonView fastJsonJsonView = new FastJsonJsonView();
Map<String,Object> map = new HashMap<>();
Map<String, Object> result = new HashMap<>();
map.put("requestId", getRequestId(request));
map.put("result", result);
map.put("error", serviceError);
fastJsonJsonView.setAttributesMap(map);
ModelAndView mv =new ModelAndView();
mv.setView(fastJsonJsonView);
return mv;
}
}
1人点赞
Spring boot
#
表示前端页面的一个锚点,一般可以用来表示标题或者段落的开头,这样浏览器发现 URL 有这个锚点,就会自动滚动页面来显示这个锚点表示的内容。
#
只在浏览器生效,无法携带到后端,你可以尝试把 #
后面的内容通过 query 信息带到另一个接口,或者直接使用 Spring 提供的 RedirectAttributes 也可以。
你的这个问题属于 URL 编码问题,请求路径传输过程中会经过 URL 编码,会对一些特殊参数进行处理,Http协议中,特殊字符诸如+?%#&=/等都会被当做转义字符处理,这样的话请求路径的参数就不对了,
如果前段发起请求,后端接受
前端:
JS中用encodeURIComponent编码两次
var url = "http://localhost:8080?data=" + encodeURIComponent(encodeURIComponent(param))
后端Java解码:
String data = request.getParameter("data");
data = java.net.URLDecoder.decode(data, "UTF-8");
如果后端发起请求
需要先对请求中含有特殊字符的参数进行编码把特殊字符替换掉,比如 # 替换为 %23 ,组装后再发起请求http://ip:9143/SvgHome.html&token=aece153ba6b16a1b%23 compiles
可以使用URLEncoder.encode(参数, "UTF-8");
接受时需要对特殊的参数进行解码,比如:result = URLDecoder.decode(token, "UTF-8");
你的问题其实并不是在方法上,而是在对特殊字符转换时,把 "/" 转换为 %2F 这里出了问题,我自己实验了好多次,这个 / 转不过去,转换成%2F之后发送请求都会没有消息,其他的特殊字符进行来回转换都没有问题,
你有时间可以主要针对找一下对于 / 转换请求的问题,如果没有时间,/ 不是必要的话,还是替换了吧,这样应该可以成功
看在辛苦了一上午的份上,采纳一下吧^-^,抱歉没能侧底解决问题
在线等
public void springbootRefresh(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
String resultUrl = "http://ip:9143/SvgHome.html&?token="+ URLEncoder.encode("aece153ba6b16a1b#/compiles", "UTF8");
response.setHeader("refresh", "1;URL=" + resultUrl);
}
因为#被浏览器占用为关键字了,#以及后面的内容根本不会被发送出去,所以不是你收不到而是浏览器根本就没发,哈哈哈。所以你要是想约定传点啥东西就得换别的标志,为啥不当做普通key-value参数传呢?
用encode把url转码然后请求试一下
参数是 body.getResult()
String encode = URLEncoder.encode(参数, "UTF-8");
String decode = URLDecoder.decode(encode, "UTF-8");