重定向和转发
重定向
浏览器->服务器servlet1->浏览器->服务器servlet2->浏览器
private void response401(ServletRequest req, ServletResponse resp, String msg) {
try {
HttpServletResponse httpServletResponse = (HttpServletResponse) resp;
//通过url传参
httpServletResponse.sendRedirect("/selfmes/401?msg="+msg);
} catch (IOException e) {
LOGGER.error(e.getMessage());
}
}
springmvc中 redirect 目标有三种构建方式
- 使用 redirect: 前缀url方式构建目标url
- 使用 RedirectView 类型指定目标, 推荐使用这个,
- 使用 ModelAndView 类型指定目标, ModelAndView 视图名默认是forward, 所以对于redirect, 需要加上 redirect: 前缀
传参和取参方式:
- 传参: 以字符串的形式构建目标url, 可以使用 query variable的格式拼url. 取参: @RequestParam()来fetch
- 传参: redirectAttributes.addAttribute() 加的attr. 取参: @RequestParam()来fetch
- 传参: redirectAttributes.addFlashAttribute() 加的attr. 取参: @ModelAttribute()来fetch
Flash attribute的特点:
- addFlashAttribute() 可以是任意类型的数据(不局限在String等基本类型), addAttribute()只能加基本类型的参数.
- addFlashAttribute() 加的 attr, 不会出现在url 地址栏上,通过session,一旦fetch后, 就会自动清空, 非常适合 form 提交后 feedback Message.
/*
* redirect 目标有三种构建方式
* 1. 使用 redirect: 前缀url方式构建目标url
* 2. 使用 RedirectView 类型指定目标
* 3. 使用 ModelAndView 类型指定目标, ModelAndView 视图名默认是forward, 所以对于redirect, 需要加上 redirect: 前缀
* */
@RequestMapping("/noParamRedirect")
public RedirectView noParamTest() {
RedirectView redirectTarget = new RedirectView();
redirectTarget.setContextRelative(true);
redirectTarget.setUrl("noParamTarget");
return redirectTarget;
}
@RequestMapping("/noParamTarget")
public String redirectTarget() {
return "noParamTarget";
}
@RequestMapping("/withParamRedirect")
public RedirectView withParamRedirect(RedirectAttributes redirectAttributes) {
RedirectView redirectTarget = new RedirectView();
redirectTarget.setContextRelative(true);
redirectTarget.setUrl("withParamTarget");
redirectAttributes.addAttribute("param1", "value1");
redirectAttributes.addAttribute("param2", "value2");
return redirectTarget;
}
@RequestMapping("/withParamTarget")
public String withParamTarget(Model model, @RequestParam("param1") String param1,
@RequestParam("param2") String param2) {
model.addAttribute("param1", param1);
model.addAttribute("param2", param2);
return "withParamTarget";
}
@RequestMapping("/withFlashRedirect")
public RedirectView withFlashTest(RedirectAttributes redirectAttributes) {
RedirectView redirectTarget = new RedirectView();
redirectTarget.setContextRelative(true);
redirectTarget.setUrl("withFlashTarget");
redirectAttributes.addAttribute("param", "value");
redirectAttributes.addFlashAttribute("flashParam", "flashValue");
return redirectTarget;
}
/*
* redirectAttributes.addAttribute加的attr, 使用 @RequestParam()来fetch
* redirectAttributes.addFlashAttribute()加的attr, 使用 @ModelAttribute()来fetch
* */
@RequestMapping("/withFlashTarget")
public String withFlashTarget(Model model, @RequestParam("param") String param,
@ModelAttribute("flashParam") String flashParam) {
model.addAttribute("param", param);
model.addAttribute("flashParam", flashParam);
return "withFlashTarget";
}
@GetMapping("/input")
public String input() {
return "input";
}
/*
* form 提交后, 如果form数据有问题, 使用redirectAttributes.addFlashAttribute()加上 flash message.
* addFlashAttribute()可以是任意类型的数据(不局限在String等基本类型)
* addFlashAttribute() 加的 attr, 不会出现在url 地址栏上.
* addFlashAttribute() 加的 attr, 一旦fetch后, 就会自动清空, 非常适合 form 提交后 feedback Message.
* */
@PostMapping("/submit")
public RedirectView submit(RedirectAttributes redirectAttributes) {
boolean passed = false;
if (passed==false) {
RedirectView redirectTarget = new RedirectView();
redirectTarget.setContextRelative(true);
redirectTarget.setUrl("input");
redirectAttributes.addFlashAttribute("errorMessage", "some error information here");
return redirectTarget;
}else {
RedirectView redirectTarget = new RedirectView();
redirectTarget.setContextRelative(true);
redirectTarget.setUrl("inputOK");
return redirectTarget;
}
}
}
转发
浏览器->服务器servlet1->服务器servlet2->浏览器
private void response401(ServletRequest req, ServletResponse resp, String msg) {
try {
//通过request传参
req.setAttribute("msg",msg);
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/401");
requestDispatcher.forward(req, resp);
} catch (IOException e) {
LOGGER.error(e.getMessage());
}catch (ServletException e) {
LOGGER.error(e.getMessage());
}
}
在Spring MVC 中, 构建forward 目标有两种方式:
- 以字符串的形式构建目标url, url 需要加上 forward: 前缀
- 使用 ModelAndView 对象来设置转发的forward目标, viewName 可以省略 forward: 前缀, viewName 应该是目标url, 而不是目标视图的函数名. 传参方式:
- 以字符串的形式构建目标url, 可以使用 query variable的格式拼url
- 使用 ModelAndView 对象来增加 attribute Object, 其结果也是在拼接url. 取参的方式: 可以使用 @RequestParam 来取参
/*
* forward 示例: 以字符串的形式构建目标url, url 需要加上 forward: 前缀
* */
@RequestMapping("/forwardTest1")
public String forwardTest1() {
return "forward:/forwardTarget?param1=v1¶m2=v2";
}
/*
* forward 示例: 使用 ModelAndView() 设置转发的目标url
* */
@RequestMapping("/forwardTest2")
public ModelAndView forwardTest2() {
ModelAndView mav=new ModelAndView("/forwardTarget"); // 绝对路径OK
//ModelAndView mav=new ModelAndView("forwardTarget"); // 相对路径也OK
mav.addObject("param1", "value1");
mav.addObject("param2", "value2");
return mav ;
}
@RequestMapping("/forwardTarget")
public String forwardTargetView(Model model, @RequestParam("param1") String param1,
@RequestParam("param2") String param2) {
model.addAttribute("param1", param1);
model.addAttribute("param2", param2);
return "forwardTarget";
}
JAVA 的服务器重定向:使用forward()方法转发请求和使用 sendRedirect()方法重定向的区别
多文件上传
<form id="myform">
<table>
<tr>
<td>
<input type="file" id="file" name="file" />
</td>
<td>
<input type="text" id="sampleid" name="sampleid" />
</td>
</tr>
<tr>
<td>
<input type="file" id="file" name="file" />
</td>
<td>
<input type="text" id="sampleid" name="sampleid" />
</td>
</tr>
</table>
</form>
<input type="button" onclick="SubmitForm()" value="提交" />
<script type="text/javascript">
function SubmitForm() {
//获取表单中的数据
var file = document.getElementById('myform');
//FormDat对象
var formobj = new FormData(file);
//XMLHttpRequest对象
var xmlobj = new XMLHttpRequest();
//指定提交类型和选择要发送的地址
xmlobj.open('post', 'http://localhost:8080/selfProduct/file/upload');
// xmlobj.withCredentials = true; //设置传递cookie,如果不需要直接注释就好
// xmlobj.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
// xmlobj.setRequestHeader("X-Requested-With", "XMLHttpRequest");
//发送数据
xmlobj.send(formobj);
xmlobj.onload = function () {
alert(xmlobj.responseText);//获取后台返回的数据
}
}
//参考:
// var xmlHttp;
// function AjaxFunction(){
// createXMLHttpRequest();
// if(xmlHttp!=null){
// xmlHttp.onreadystatechange = callBack;
// xmlHttp.open("get/Post","URL",true/false);
// xmlHttp.send(null);
// }
// }
// //实例化XMLHttpRequest对象
// function createXMLHttpRequest(){
// if(window.XMLHttpRequest){
// xmlHttp = new XMLHttpRequest();
// }else if(window.ActiveXObject){
// xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
// }
// }
// //指定响应函数
// function callBack(){
// if(xmlHttp.readyState==4){
// if(xmlHttp.status==200){
// //do something with xmlHttp.responseText;
// xmlHttp.responseText;
// }
// }
// }
</script>
//后端接收参数 与前端form name标签相同,数组之间一一对应,未多次验证是否会出错
public Map<String, String> upload(@RequestParam("file") CommonsMultipartFile[] upfiles,
@RequestParam("sampleid") String[] sampleids) throws IOException {
...
}
//封装成对象传入:
@RequestMapping("upload1")
@ResponseBody
public void upload1(Files files) throws IOException {
...
}
public class Files {
private List<ModelFile> files;
//geter...seter...
}
public class ModelFile {
private CommonsMultipartFile file;
private String sampleid;
//geter...seter...
}
//前端
<form id="myform">
<table>
<tr>
<td>
<input type="file" id="file" name="files[0].file" />
</td>
<td>
<input type="text" id="sampleid" name="files[0].sampleid" />
</td>
</tr>
<tr>
<td>
<input type="file" id="file" name="files[1].file" />
</td>
<td>
<input type="text" id="sampleid" name="files[1].sampleid" />
</td>
</tr>
</table>
</form>
<input type="button" onclick="SubmitForm()" value="提交" />
自定义消息装换器
- 自定义注解
package com.self.common;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface UserNoToName {
}
- 继承至AbstractHttpMessageConverter类 实现对应方法
package com.self.common;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
public class MyMessageConverter extends AbstractHttpMessageConverter<Object> {
public final static Charset UTF8 = Charset.forName("UTF-8");
public MyMessageConverter() {
// 设置我们媒体类型
super(new MediaType("application", "json", UTF8));
}
// 标明本HttpMessageConverter处理所有类,过滤在这些
@Override
protected boolean supports(Class<?> aClass) {
return true;
}
//发起请求,反序列化时调用
@Override
protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
System.out.println("readInternal");
return null;
}
//放回数据,序列化时调用
@Override
protected void writeInternal(Object obj, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
System.out.println("writeInternal");
Class<? extends Object> clazz = obj.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
//自定义注解UserNoToName
if (field.isAnnotationPresent(UserNoToName.class)) {
try {
field.setAccessible(true);
String val = field.get(obj)==null?"":(String)field.get(obj);
// field.set(obj, "df1");
// field.set(obj, userMapper.findByLoginName(val).getDisplayName());
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
outputMessage.getBody().write(JSON.toJSONBytes(obj,
SerializerFeature.DisableCircularReferenceDetect,
SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteNullStringAsEmpty));
}
}
- 安监项目使用例子
package com.hlkj.basis.dao.repository.base;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
/**
* 目前只支持基本类型和Collection的实现类
* 一般使用BaseEntityRepository 的 findByIdIncludeFields方法
*/
@Target(value = { FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface IdConvertOther {
/** jpa查询类
* @return
*/
Class<?> springClass();
/** 返回类转换的字段
* 若为空""则为方法返回的obj
* @return
*/
String returnField();
/** 当前类查询的id字段
* 默认空则为当前字段,不为空的话只有当前字段为null才会生效
* @return
*/
String sourceField() default "";
/** 指定查询方法
* 默认使用BaseEntityRepository 的 findByIdIncludeFields查询
* 其余方法参数只支持id查询,一个参数
* @return
*/
String methodName() default "findByIdIncludeFields";
}
package com.hlkj.basis.dao.repository.base;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hlkj.commons.utils.ErrororUtil;
import com.hlkj.commons.utils.spring.SpringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import java.io.IOException;
import java.lang.reflect.*;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
/**
* @author 徐其伟
* @Description: 继承至jackson的消息装换器
* springboot2默认使用MappingJackson2HttpMessageConverter做json序列化转换器
* 使用继承对序列化之前进行增强,进行字段装换
* @date 19-4-12 下午3:06
*/
public class IdConvertMessageConverter extends MappingJackson2HttpMessageConverter {
private static Logger logger = LogManager.getLogger(IdConvertMessageConverter.class.getName());
public IdConvertMessageConverter() {
}
public IdConvertMessageConverter(ObjectMapper objectMapper) {
super(objectMapper);
}
//放回数据,序列化时调用
private void convertOther(Object res) throws Exception {
if (res == null) {
return;
}
Class<?> clazz = res.getClass();
if (isBasisType(clazz)) {
return;
}
if (Collection.class.isAssignableFrom(clazz)) {
for (Object o : (Collection) res) {
convertOther(o);
}
return;
}
if (Map.class.isAssignableFrom(clazz)) {
Map map = (Map) res;
for (Object o : map.keySet()) {
convertOther( map.get(o));
}
return;
}
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
field.setAccessible(true);
Object fieldValue = field.get(res);
if (field.isAnnotationPresent(IdConvertOther.class)) { //判断自定义注解
IdConvertOther convertOther = field.getAnnotation(IdConvertOther.class);
//获取装换 原先的id
Object val;
//sourceField为空则为当前字段
if (!"".equals(convertOther.sourceField())) {
//如果当前字段已经有值了就不再赋值
if (fieldValue != null) {
continue;
}
Field f = clazz.getDeclaredField(convertOther.sourceField());
f.setAccessible(true);
val = f.get(res);
} else {
val = fieldValue;
}
if (val == null) {
continue;
}
if (val instanceof Collection) {
Collection collection = (Collection) val.getClass().newInstance();
for (Object o : (Collection) val) {
collection.add(getTargetValue(convertOther, o));
}
field.set(res, collection);
} else {
field.set(res, getTargetValue(convertOther, val));
}
} else if (!isBasisType(field.getType())) { //不是八大基本类型
convertOther(fieldValue);
}
}
}
private Object getTargetValue(IdConvertOther convertOther, Object val) throws IllegalAccessException, InvocationTargetException, NoSuchFieldException {
if(!(val instanceof String)) {
return val;
}
Object springBean = SpringUtils.getBean(convertOther.springClass());
Object result;
if(springBean instanceof BaseEntityRepository) {
result = ((BaseEntityRepository) springBean).findByIdIncludeFields((String)val, convertOther.returnField());
} else {
Method method = getMethodByName(convertOther.methodName(), springBean.getClass());
result = method.invoke(springBean, val);
}
if (result != null) {
Object entity;
if (result instanceof Optional) {
Optional optional = (Optional) result;
entity = optional.orElse(null);
} else {
entity = result;
}
if (entity != null) {
if ("".equals(convertOther.returnField())){
val = entity;
} else {
Class<?> entityClass = entity.getClass();
Field targetField = entityClass.getDeclaredField(convertOther.returnField());
targetField.setAccessible(true);
val = targetField.get(entity);
}
}
}
return val;
}
/**
* 根据名称查找方法
* 参数为String 或者 Object的
* @param methodName
* @param cls
* @return
*/
private Method getMethodByName(String methodName, Class cls) {
for (Method m : cls.getMethods()) {
if (m.getName().equals(methodName) && m.getParameterTypes().length == 1) {
Class<?> type = m.getParameterTypes()[0];
if(type.equals(Object.class) || type.equals(String.class)) {
return m;
}
}
}
ErrororUtil.runThrow(this, "反射获取方法", "找不到方法");
return null;
}
/** 是否为八大基础类型及其扩展类
* @param clazz
* @return
*/
private boolean isBasisType(Class clazz) {
boolean b = true;
boolean basic = clazz.isPrimitive();
if(basic) {
return b;
}
switch (clazz.getTypeName()) {
case "java.lang.String":
break;
case "java.lang.Boolean":
break;
case "java.lang.Integer":
break;
case "java.lang.Double":
break;
case "java.lang.Long":
break;
case "java.lang.Float":
break;
case "java.lang.Short":
break;
case "java.lang.Byte":
break;
case "java.lang.Character":
break;
default:
b = false;
break;
}
return b;
}
@Override
protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
long l1 = System.currentTimeMillis();
try {
convertOther(object);
} catch (Exception e) {
logger.error("自定义转换字段发生错误");
logger.error(e);
}
System.out.println("writeInternal:" + (System.currentTimeMillis() - l1));
super.writeInternal(object, type, outputMessage);
}
}