首页 文章 精选 留言 我的

精选列表

搜索[Spring Framework],共10000篇文章
优秀的个人博客,低调大师

Spring Boot(三):RestTemplate提交表单数据的三种方法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xmt1139057136/article/details/81460218 在REST接口的设计中,利用RestTemplate进行接口测试是种常见的方法,但在使用过程中,由于其方法参数众多,很多同学又混淆了表单提交与Payload提交方式的差别,而且接口设计与传统的浏览器使用的提交方式又有差异,经常出现各种各样的错误,如405错误,或者根本就得不到提交的数据,错误样例如下: Exception in thread "main" org.springframework.web.client.HttpClientErrorException: 405 Method Not Allowed at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:63) at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613) at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:531) 1. 用exchange方法提交 exchange既可以执行POST方法,还可以执行GET,所以应用最为广泛,使用方法如下: String url = "http://localhost/mirana-ee/app/login"; RestTemplate client = new RestTemplate(); HttpHeaders headers = new HttpHeaders(); // 请勿轻易改变此提交方式,大部分的情况下,提交方式都是表单提交 headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); // 封装参数,千万不要替换为Map与HashMap,否则参数无法传递 MultiValueMap<String, String> params= new LinkedMultiValueMap<String, String>(); // 也支持中文 params.add("username", "用户名"); params.add("password", "123456"); HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(params, headers); // 执行HTTP请求 ResponseEntity<String> response = client.exchange(url, HttpMethod.POST, requestEntity, String.class); // 输出结果 System.out.println(response.getBody()); 2. 用postForEntity进行提交 postForEntity是对exchange的简化,仅仅只需要减少HttpMethod.POST参数,如下: // 上面的代码完全一样 // 仅需替换exchange方法 ResponseEntity<String> response = client.postForEntity(url, requestEntity , String.class ); 3. 关于表单提交与Payload提交的差异 在Controller的方法参数中,如果将“@ModelAttribute”改为“@RequestBody”注解,则此时的提交方式为Payload方式提交,详细的差异请参见《 $.ajax使用总结(一):Form提交与Payload提交》,代码示例如下: // 请注意@RequestBody注解 @RequestMapping(value="/login", method=RequestMethod.POST, consumes="application/json") // 千万不要画蛇添足添加@ModelAttribute,否则会被其覆盖,如下 // public Account getAccount(@RequestBody@ModelAttribute Account account) public Account getAccount(@RequestBody Account account) { account.setVersion(new Date()); return account; } 再次强调一次,千万不要画蛇添足再次添加“@ModelAttribute”,因为其优先级比较高,所以系统会采用表单方式解析提交内容。 对于Payload方式,提交的内容一定要是String,且Header要设置为“application/json”,示例如下: // 请求地址 String url = "http://localhost/mirana-ee/app/login"; RestTemplate client = new RestTemplate(); // 一定要设置header HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON_UTF8); // 将提交的数据转换为String // 最好通过bean注入的方式获取ObjectMapper ObjectMapper mapper = new ObjectMapper(); Map<String, String> params= Maps.newHashMap(); params.put("username", "国米"); params.put("password", "123456"); String value = mapper.writeValueAsString(params); HttpEntity<String> requestEntity = new HttpEntity<String>(value, headers); // 执行HTTP请求 ResponseEntity<String> response = client.postForEntity(url, requestEntity , String.class ); System.out.println(response.getBody()); 如果内容不是以String方式提交,那么一定会出现以下错误: Exception in thread "main" org.springframework.web.client.HttpClientErrorException: 400 Bad Request at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:63) at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613) at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:407) 最后需要强调的是,通过@RequestBody是无法获取到请求参数,如将上面服务端的代码改为如下格式,则肯定得不到数据,但表单提交则相反。 @RequestMapping(value="/login", consumes="application/json", method=RequestMethod.POST) public Account getAccount(@RequestBody Account account, HttpServletRequest request) { // 肯定得不到参数值 System.out.println(request.getParameter("username")); account.setVersion(new Date()); return account; } 4. HttpEntity的结构 HttpEntity是对HTTP请求的封装,包含两部分,header与body,header用于设置请求头,而body则用于设置请求体,所以其的构造器如下: // value为请求体 // header为请求头 HttpEntity<String> requestEntity = new HttpEntity<String>(value, headers); 5. HttpEntity与uriVariables 在RestTemplate的使用中,HttpEntity用于传递具体的参数值,而uriVariables则用于格式化Http地址,而不是地址参数,正确的用法如下: // 在地址中加入格式化参数path String url = "http://localhost/mirana-ee/app/{path}"; // 准备格式化参数 Map<String, String> varParams = Maps.newHashMap(); varParams.put("path", "login"); // 其他代码略 // 格式化提交地址 ResponseEntity<String> response = client.postForEntity(url, requestEntity , String.class, varParams); 6. 关于HttpMessageConverter的说明 在网上的很多例子中,我发现很多人为了处理Payload提交,都添加了自定义的HttpMessageConverter,如下: // 完全没有必要 client.getMessageConverters().add(new MappingJackson2HttpMessageConverter()); client.getMessageConverters().add(new StringHttpMessageConverter()); 然后,经过我查看源码与调试发现,RestTemplate内置了7种HttpMessageConverter,如下: 1. org.springframework.http.converter.ByteArrayHttpMessageConverter 2. org.springframework.http.converter.StringHttpMessageConverter 3. org.springframework.http.converter.ResourceHttpMessageConverter 4. org.springframework.http.converter.xml.SourceHttpMessageConverter 5. org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter 6. org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter 7. org.springframework.http.converter.json.MappingJackson2HttpMessageConverter “` 结论 RestTemplate能大幅简化了提交表单数据的难度,并且附带了自动转换JSON数据的功能,但只有理解了HttpEntity的组成结构(header与body),且理解了与uriVariables之间的差异,才能真正掌握其用法。 感谢您的关注!可加QQ1群:135430763,QQ2群:454796847,QQ3群:187424846。QQ群进群密码:xttblog,想加微信群的朋友,可以微信搜索:xmtxtt,备注:“xttblog”,添加助理微信拉你进群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作可添加助理微信进行沟通!

优秀的个人博客,低调大师

如何用ACM简化你的Spring Cloud微服务环境配置管理

配置的环境属性 毫无疑问,在系统持续交付的过程中,系统最终运行环境的多样性及复杂性毫无疑问增加了我们在配置管理工作上的负担,有时候,甚至不夸张的说,配置就是因环境而生. 这在Eugen Paraschiv的博文 Configuration Must Be Environment Specific里有简单的阐述,在我的博文《现代应用架构中的配置管理面临的挑战》 的容器化、调度与配置管理小节也有深入的阐述。 如果要问,是什么导致了我们应用的构建物(artifact)在各个环境不能保持一样,有时候Docker无法轻易达成“Build Once, Run Anywhere!"的承诺,其答案往往就是环境配置的差异,为帮助你理解,举一些简单的例子: 在开发环境中将logLevel设置为DEBUG,在预发环境logLevel设置为INFO,生产环境里l

优秀的个人博客,低调大师

【java学习】spring mvc 公共dao的实现,定义基本的增删改查

接口类: package com.blog.db.dao; import com.blog.util.Pagination; import java.util.List; public interface PublicDao<T> { public void setMapper(Class t); public void setTableName(String tn); public List<T> queryList(Pagination pagination); public void deleteByID(int ID); public void add(T t,Object[] values,int[] types); public T queryById(int id); public T queryByField(String fieldname,String value); public void updateByID(T t,int ID,Object[] values,int[] types); } 实现类: package com.blog.db.dao.impl; import com.blog.db.dao.PublicDao; import com.blog.util.Pagination; import org.springframework.beans.factory.annotation.Value; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; import javax.annotation.Resource; import java.sql.Types; import java.util.List; /** * 公共dao实现 * @deprecated 提供常见的列表、插入、查询单个、更新数据服务 * @author ztf * */ @Repository("publicDao") public class PublicDaoimpl<T> implements PublicDao<T> { @Resource private JdbcTemplate jdbcTemplate; private String table_name = ""; //表名称 private List<String> fields; //表字段 @Value("${db_schema}") private String schema; //表模式 private RowMapper<T> mapper; /** * 设置Mapper映射 */ public void setMapper(Class t){ this.mapper = new BeanPropertyRowMapper<T>(t); } public RowMapper<T> getMapper(){ return mapper; } public void setTableName(String tn){ this.table_name = tn; } public String getTable_name() { return schema+"."+table_name; } public List<String> getFields(){ return this.fields; } public String getFieldsToString(){ String _fields; List<String> fields = getFields(); _fields = String.join(",",fields); return _fields; } /** * 查询列表 * @param pagination 传入公共util中定义的分页对象 * @return List<T> * */ public List<T> queryList(Pagination pagination) { List lists = null; String sql = "select * from ? ?"; lists = jdbcTemplate.queryForList( sql, getTable_name(), pagination.getSql(), getMapper() ); return lists; } /** * 根据ID删除某一个行 * @param ID 传入行id * */ public void deleteByID(int ID) { String sql = "delete from "+ getTable_name()+" where ID=?"; jdbcTemplate.update( sql, new Object[] { ID }, new int[] {Types.INTEGER} ); } /** * 插入数据 * @param t 传入操作的数据对象 * @param values 值的object[]对象 * @param types 值对象的类型 * */ public void add(T t,Object[] values,int[] types) { String sql = "insert into "+getTable_name()+"("+getFieldsToString()+") values(?)"; jdbcTemplate.update( sql, values, types ); } /** * 根据ID查询指定的行 * @param id 传入id * */ public T queryById(int id) { T t = null; String sql = "select * from "+getTable_name()+" where ID=?"; try{ t = jdbcTemplate.queryForObject( sql, new Object[] {id}, new int[] {Types.INTEGER}, getMapper() ); }catch(EmptyResultDataAccessException e){ t = null; } return t; } /** * 根据指定的fieldname查询指定值的单行数据 * @param fieldname 传入id * */ public T queryByField(String fieldname,String value) { T t = null; String sql = "select * from "+getTable_name()+" where "+fieldname+"=?"; try{ t = jdbcTemplate.queryForObject( sql, new Object[] {value}, new int[] {Types.VARCHAR}, getMapper() ); }catch(EmptyResultDataAccessException e){ t = null; } return t; } /** * 根据指定的id更新指定值的单行数据 * @param t 传入更新对象 * @param ID 传入指定的ID * @param values 值的object[]对象 * @param types 值对象的类型 int[] * */ public void updateByID(T t, int ID,Object[] values,int[] types) { String sql = "update "+getTable_name()+" set username=?,password=?,pic=?,describe=?,check_time=? where ID="+ID; jdbcTemplate.update( sql, values, types ); } } 核心的地方在:如何传递Mapper 这里我使用的是 private RowMapper<T> mapper; /** * 设置Mapper映射 */ public void setMapper(Class t){ this.mapper = new BeanPropertyRowMapper<T>(t); } public RowMapper<T> getMapper(){ return mapper; } 在调用Dao时传入 T类的.class对象 调用例如(部分代码): @Qualifier("publicDao") private PublicDao<User> ud; public User Sign(String username, String password) throws UnsupportedEncodingException { String md5_pass = MD5Encryption.getEncryption(password); ud.setTableName("users"); //指定查询表名 ud.setMapper(User.class); //指定映射class对象 User user = ud.queryByField("username",username); //根据字段 username查询值为xxx 可以实现基本表的增删改查,提升开发效率, 如果有特殊的Dao可以自定义一个专属Dao 再添加特殊方法======================================================== 转载请注明出处。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册