第二节:如何正确使用WebApi和使用过程中的一些坑

本文深入探讨WebAPI的调用规则与传参方式,包括GET、POST、PUT、DELETE请求的具体实践,以及实体类、dynamic类型、[FromBody]和[FromUri]特性的正确使用方法。

一. 基本调用规则

1. 前提

  WebApi的默认路由规则为:routeTemplate: "api/{controller}/{id}", 下面为我们统一将它改为 routeTemplate: "api/{controller}/{action}/{id}",这样我们在调用的时候,还是通过拼接方法名来识别,不用考虑上面的坑别的规则了,这里我单纯的来探讨WebApi的传参和调用。

2. 基本的调用规则

  是什么请求,在方法上面标注什么特性,常见的有[HttpGet][HttpPost][HttpPut][HttpDelete]。

PS:方法名以Get、Post、Put、Delete开头,WebApi会自动默认这个请求就是Get、Post、Put、Delete请求,而如果你以其他名称开头而又不标注方法的请求方式,那么这个时候服务器虽然找到了这个方法,但是由于请求方式不确定,所以直接返回给你405——方法不被允许的错误。

 

二. Get请求规则

1. 标准用法:

  发起请求的方式都为 api/Second/CheckLogin?userName=admin&pwd=123456 这种类型,不管几个参数(1个或多个),接收的时候,如果是分参数接收,没有任何问题, 可以正常接收,但如果是通过实体类来接收,必须在前面加特性[FromUri],否则接收不到。

PS:当然也可以什么参数都不写,通过传统的Request或者Request.QueryString来接受。

2. 实战测试:

(1). 分别调用CheckLogin1、CheckLogin2、CheckLogin3方法, 发现前两个方法都能正常调用,CheckLogin3这个方法报错, 报:"Message":"出现错误","ExceptionMessage":"未将对象引用设置到对象的实例"。

(2). 调用CheckLogin4方法,报与上面相同的错误,证明dynamic类型也不能这么用。

(3). 调用CheckLogin5方法,能正常接收,表明可以通过传统的Request或者Request.QueryString来接受。

代码分享:

1   public class LoginModel
2     {
3         public string userName { get; set; }
4 
5         public string pwd { get; set; }
6     }
实体类
  1         #region 01-分参数接收
  2         /// <summary>
  3         /// Get api/Second/CheckLogin1?userName=admin&pwd=123456
  4         /// </summary>
  5         /// <param name="userName"></param>
  6         /// <param name="pwd"></param>
  7         /// <returns></returns>
  8         [HttpGet]
  9         public string CheckLogin1(string userName, string pwd)
 10         {
 11             if (userName == "admin" && pwd == "123456")
 12             {
 13                 return "ok";
 14             }
 15             else
 16             {
 17                 return "error";
 18             }
 19         }
 20         #endregion
 21 
 22         #region 02-用实体接收,加[FromUri]特性
 23         /// <summary>
 24         /// Get api/Second/CheckLogin2?userName=admin&pwd=123456
 25         /// </summary>
 26         /// <param name="model"></param>
 27         /// <returns></returns>
 28         [HttpGet]
 29         public string CheckLogin2([FromUri]LoginModel model)
 30         {
 31             if (model.userName == "admin" && model.pwd == "123456")
 32             {
 33                 return "ok";
 34             }
 35             else
 36             {
 37                 return "error";
 38             }
 39         }
 40         #endregion
 41 
 42         #region 03-用实体接收,不加[FromUri]特性
 43         /// <summary>
 44         /// Get api/Second/CheckLogin3?userName=admin&pwd=123456
 45         /// </summary>
 46         /// <param name="model"></param>
 47         /// <returns></returns>
 48         [HttpGet]
 49         public string CheckLogin3(LoginModel model)
 50         {
 51             if (model.userName == "admin" && model.pwd == "123456")
 52             {
 53                 return "ok";
 54             }
 55             else
 56             {
 57                 return "error";
 58             }
 59         }
 60         #endregion
 61 
 62         #region 04-用dynamic接收,加[FromUri]特性
 63         /// <summary>
 64         /// Get api/Second/CheckLogin4?userName=admin&pwd=123456
 65         /// </summary>
 66         /// <param name="model"></param>
 67         /// <returns></returns>
 68         [HttpGet]
 69         public string CheckLogin4([FromUri]dynamic model)
 70         {
 71             if (model.userName == "admin" && model.pwd == "123456")
 72             {
 73                 return "ok";
 74             }
 75             else
 76             {
 77                 return "error";
 78             }
 79         }
 80         #endregion
 81 
 82         #region 05-没有任何参数,直接用Request相关方法接收
 83         /// <summary>
 84         /// Get api/Second/CheckLogin5?userName=admin&pwd=123456
 85         /// </summary>
 86         /// <param name="model"></param>
 87         /// <returns></returns>
 88         [HttpGet]
 89         public string CheckLogin5()
 90         {
 91             var userName = HttpContext.Current.Request["userName"];
 92             var pwd = HttpContext.Current.Request.QueryString["pwd"];
 93             if (userName == "admin" && pwd == "123456")
 94             {
 95                 return "ok";
 96             }
 97             else
 98             {
 99                 return "error";
100             }
101         }
102         #endregion
服务器端代码
 1   //一.下面是Get请求的测试
 2             //1. 分参数接收,可以正常访问
 3             $("#getBtn1").click(function () {
 4                 $.ajax({ url: "/api/Second/CheckLogin1", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
 5             });
 6             //2. 用实体类接收,前面加[FromUrl],可以正常访问
 7             $("#getBtn2").click(function () {
 8                 $.ajax({ url: "/api/Second/CheckLogin2", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
 9             });
10             //3. 用实体类接收,前面什么不加,报错
11             // "Message":"出现错误","ExceptionMessage":"未将对象引用设置到对象的实例"
12             $("#getBtn3").click(function () {
13                 $.ajax({ url: "/api/Second/CheckLogin3", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
14             });
15             //4. 用dynamic接收,前面什么不加,报错
16             // "Message":"出现错误","ExceptionMessage":"未将对象引用设置到对象的实例"
17             $("#getBtn4").click(function () {
18                 $.ajax({ url: "/api/Second/CheckLogin4", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
19             });
20             //5. 后台直接用Request或者Request.QueryString,能正常接收
21             $("#getBtn5").click(function () {
22                 $.ajax({ url: "/api/Second/CheckLogin5", type: "get", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
23             });
前端JS代码

3. 特别说明:

  建议忘掉实战测试中的几种错误情况,记住标准用法即可。

 

三. Post请求规则

1.标准用法:

  参数一定要用“实体类”接收(即使一个参数也要封装实体类),客户端既可以用ContentType="application/x-www-form-urlencoded"提交表单,也可以用 ContentType ="application/json"提交, 模型类前面可以加[FromBody],但只能在一个参数前面加.

2.实战测试

(1). 当只有一个参数的情况,且加[FromBody]特性,不封装实体,如Register0,正常的键值对 { userName: "admin" }能访问通,但后台拿不到值,只有省掉键名{ "": "admin" },才能正常访问,且后台能拿到值。

(2). 多个参数的情况,后台分参数接收,如Register1,正常的键值对提交,无法访问,提示找不到匹配的资源。

(3). 一个或多个参数的情况,后台用实体接收,如Register2,且加[FromBody]特性,可以正常访问,并获取到请求值。

  ①:默认的post请求,即ContentType="application/x-www-form-urlencoded"的形式,可以正常请求。

  ②:将post请求指定为contentType: 'application/json',且传递的实体格式化成Json字符串,则可以正常请求。

(4). 一个或多个参数的情况,后台用dynamic接收,且加[FromBody]特性,需要分情况讨论。

  ①:默认的post请求,即ContentType="application/x-www-form-urlencoded"的形式,服务器报500错误。

  ②:将post请求指定为contentType: 'application/json',且传递的实体格式化成Json字符串,则可以正常请求。

阶段总结:后台用实体接收和用dynamic类型接收,用实体接收,无论是"application/x-www-form-urlencoded"还是"application/json"都能访问;如果用dynamic类型接收,只有"application/json"能访问, 且参数必须是序列化后的字符串

(5). 一个或多个参数的情况,没有参数,如Register4,通过Request或者Request.Form相关方法可以获取请求值。

 代码分享:

 1  #region 01-单个参数,加[FromBody]特性
 2         [HttpPost]
 3         public string Register0([FromBody]string userName)
 4         {
 5             if (userName == "admin")
 6             {
 7                 return "ok";
 8             }
 9             else
10             {
11                 return "error";
12             }
13         }
14         #endregion
15 
16         #region 02-多个参数,分参数接收
17         [HttpPost]
18         public string Register1(string userName, string pwd)
19         {
20             if (userName == "admin" && pwd == "123456")
21             {
22                 return "ok";
23             }
24             else
25             {
26                 return "error";
27             }
28         }
29         #endregion
30 
31         #region 03-用实体接收,加[FromBody]特性
32         [HttpPost]
33         public string Register2([FromBody]LoginModel model)
34         {
35             if (model.userName == "admin" && model.pwd == "123456")
36             {
37                 return "ok";
38             }
39             else
40             {
41                 return "error";
42             }
43         }
44         #endregion
45 
46         #region 04-用dynamic接收,加[FromBody]特性
47         [HttpPost]
48         public string Register3([FromBody]dynamic model)
49         {
50             if (model.userName == "admin" && model.pwd == "123456")
51             {
52                 return "ok";
53             }
54             else
55             {
56                 return "error";
57             }
58         }
59         #endregion
60 
61         #region 05-没有任何参数,直接用Request相关方法接收
62         [HttpPost]
63         public string Register4()
64         {
65             var userName = HttpContext.Current.Request["userName"];
66             var pwd = HttpContext.Current.Request.Form["pwd"];
67             if (userName == "admin" && pwd == "123456")
68             {
69                 return "ok";
70             }
71             else
72             {
73                 return "error";
74             }
75         } 
76         #endregion
77 
78 
79     }
服务器端代码
 1  //二.下面是Post请求的测试(默认情况下为:ContentType="application/x-www-form-urlencoded"提交表单的形式)
 2             //PS: { userName: "admin", pwd: "123456" } 这就是一个JSON对象,也可以叫实体
 3 
 4             //1. 一个参数的情况,后台分参数接收,且必须加[FromBody]特性
 5             $("#postBtn0").click(function () {
 6                 //1.1 正常拼接,可以访问通,但是拿不到userName的值
 7                 $.ajax({ url: "/api/Second/Register0", type: "Post", data: { userName: "admin" }, success: function (data) { alert(data); } });
 8                 //1.2 没有键,只有值,可以正常访问,能拿到userName的值
 9                 //$.ajax({ url: "/api/Second/Register0", type: "Post", data: { "": "admin" }, success: function (data) { alert(data); } });
10             });
11             //2. 多个参数的情况,后台分参数接收,正常的键值对提交,无法访问,找不到匹配的资源
12             $("#postBtn1").click(function () {
13                 //访问不通
14                 $.ajax({ url: "/api/Second/Register1", type: "Post", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
15             });
16             //3. 一个或多个参数的情况,后台用实体接收,且加[FromBody]特性,可以正常访问,并获取到请求值
17             $("#postBtn2").click(function () {
18                 //情况①,默认的post请求,即ContentType="application/x-www-form-urlencoded"的形式,可以正常请求
19                 //$.ajax({ url: "/api/Second/Register2", type: "Post", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
20 
21                 //情况②,将post请求指定为contentType: 'application/json',且传递的参数格式化成Json字符串,则可以正常访问
22                 $.ajax({ url: "/api/Second/Register2", type: "Post", contentType: 'application/json', data: JSON.stringify({ userName: "admin", pwd: "123456" }), success: function (data) { alert(data); } });
23 
24             });
25             //4. 一个或多个参数的情况,后台用dynamic接收,且加[FromBody]特性,需要分情况讨论
26             $("#postBtn3").click(function () {
27                 //情况①,默认的post请求,即ContentType="application/x-www-form-urlencoded"的形式,服务器报500错误
28                 //$.ajax({ url: "/api/Second/Register3", type: "Post", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
29 
30                 //情况②,将post请求指定为contentType: 'application/json',且传递的参数格式化成Json字符串,则可以正常访问
31                 $.ajax({ url: "/api/Second/Register3", type: "Post", contentType: 'application/json', data: JSON.stringify({ userName: "admin", pwd: "123456" }), success: function (data) { alert(data); } });
32             });
33             //5. 一个或多个参数的情况,没有参数,通过Request或者Request.Form相关方法可以获取请求值
34             $("#postBtn4").click(function () {
35                 //访问不通
36                 $.ajax({ url: "/api/Second/Register4", type: "Post", data: { userName: "admin", pwd: "123456" }, success: function (data) { alert(data); } });
37             });
前端JS代码

 

四. 总结

  Put和Delete请求与Post请求的规则相同,另外还有很多极端的情况,不探讨了,掌握正确的用法,直接去用最佳用法即可。

  总结:记住t请求和Post请求的标准用法以及基本的调用规则,其他坑爹的情况,可以统统忘记了,注意的是ajax的Get请求,加上一个当前时间或者随机数的参数,使用HttpClient 等需要禁用缓存。

 

 

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鹏飞)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 声     明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 

转载于:https://www.cnblogs.com/yaopengfei/p/10273118.html

<think>好的,我现在需要帮用户解决如何使用SpringBootVue实现上传Excel文件并解析法律条款数据存储到数据库的问题。首先,我得理清楚整个流程,然后分步骤思考每个部分需要做什么。 用户的需求很明确,上传Excel文件,解析其中的法律名称、关键词、发布部门、章节条款等信息,然后存储到数据库。我需要分别考虑前端后端的实现。 首先,后端部分使用SpringBoot。需要处理文件上传的接口,接收前端传来的Excel文件。然后解析Excel,这里可能需要用Apache POI或者EasyExcel。考虑到法律文件可能比较大,EasyExcel在内存占用上更有优势,所以可能推荐使用EasyExcel。然后需要将解析后的数据存储到数据库,所以需要设计对应的实体类,比如LawEntity,可能包含法律名称、关键词、发布部门等字段,章节条款可能作为另一个实体,比如ChapterSection,与LawEntity建立关联。需要创建Repository或者Mapper来进行数据库操作,使用JPA或者MyBatis-Plus。 然后是前端部分,使用Vue。需要实现一个文件上传的组件,可能用Element UI的Upload组件,设置上传的API地址为后端的接口。上传成功后,可能需要显示解析结果或提示信息。前端需要处理文件选择、上传进度、成功/失败的回调。 接下来要考虑前后端的数据交互格式,比如上传文件使用MultipartFile,后端返回统一的响应结构,包含成功与否的状态码信息。可能需要处理跨域问题,后端需要配置CORS。 然后是具体的代码实现步骤: 后端: 1. 添加依赖:Spring Web、Spring Data JPA、EasyExcel、MySQL驱动等。 2. 配置数据库连接JPA属性。 3. 创建实体类:Law(法律主表)、ChapterSection(章节条款子表),建立一对多关系。 4. 创建Repository接口,继承JpaRepository。 5. 创建Controller,处理上传请求,使用MultipartFile接收文件,调用EasyExcel读取数据。 6. 编写Excel监听器,继承AnalysisEventListener,在读取每一行时处理数据,保存到数据库。 7. 处理异常,比如文件格式错误、数据解析错误等,返回适当的错误信息。 前端: 1. 安装Element UI或其他UI库。 2. 创建上传组件,配置action指向后端的上传接口。 3. 处理上传前的文件类型校验,比如只允许xls或xlsx文件。 4. 上传过程中显示进度条或加载状态。 5. 上传成功后,显示成功提示,可能刷新数据列表。 6. 处理上传失败的情况,提示错误信息。 需要注意的点: - 文件上传的大小限制,SpringBoot默认有1MB的限制,需要在配置文件中调整spring.servlet.multipart.max-file-sizemax-request-size。 - 数据校验,比如Excel中的字段是否完整,是否有重复数据,如何处理重复(比如跳过或覆盖)。 - 事务管理,如果在解析过程中出现异常,是否需要回滚已保存的数据。 - 前端后端的跨域配置,确保Vue能正确访问到SpringBoot的接口。 - 安全性,比如文件上传接口是否需要身份验证,防止未授权上传。 现在,我需要将这些步骤转化为具体的代码示例。后端部分需要定义实体、Repository、Controller,以及使用EasyExcel的监听器。前端部分需要写Vue组件,使用axios发送请求。 可能遇到的: - EasyExcel的依赖版本问题,需要确保SpringBootEasyExcel版本兼容。 - 实体类的一对多关系配置是否正确,是否会导致循环引用问题,可能需要使用@JsonIgnore或者DTO来避免。 - 文件上传时,前端是否正确设置Content-Type为multipart/form-data。 - 数据库字段长度是否足够,比如法律名称可能较长,需要设置合适的长度。 现在,将这些思考整合成结构化的回答,分步骤讲解,并提供代码示例。同时,根据用户提供的引用,可能需要注意引用相关的内容,但用户提供的引用似乎当前问题关联不大,比如引用[1]关于Tika提取元数据,可能可以提到如果需要处理复杂的Excel格式或提取元数据可以使用Tika,但当前问题使用EasyExcel更合适,所以可能不需要引用。不过用户要求在回答中引用的段落末尾添加引用标识,需要检查是否有适用的地方。例如,在解析Excel部分,可以提到如果需要处理更复杂的文档格式,可以参考Tika的元数据提取方法[^1],但可能不太相关,所以可能不需要。不过按照用户的要求,可能需要适当添加引用,所以需要找到合适的位置。 最后,生成相关问题,比如如何优化文件上传性能,如何处理大数据量Excel,如何实现断点续传等。</think>### 使用SpringBootVue实现Excel文件上传与法律条款存储 以下是分步骤实现方案及代码示例: --- #### **一、后端实现(SpringBoot)** ##### 1. 添加依赖 ```xml <!-- pom.xml --> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.1.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies> ``` ##### 2. 实体类设计 ```java // Law.java (法律主表) @Entity public class Law { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String lawName; // 法律名称 private String keywords; // 关键词 private String publishDept; // 发布部门 @OneToMany(mappedBy = "law", cascade = CascadeType.ALL) private List<ChapterSection> chapters; // 章节条款 } // ChapterSection.java (章节条款) @Entity public class ChapterSection { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String chapter; // 章节名称 private String clause; // 条款内容 @ManyToOne @JoinColumn(name = "law_id") private Law law; } ``` ##### 3. 数据访问层 ```java public interface LawRepository extends JpaRepository<Law, Long> {} ``` ##### 4. Excel解析监听器 ```java public class LawDataListener extends AnalysisEventListener<LawExcelDTO> { private final LawRepository lawRepository; public LawDataListener(LawRepository lawRepository) { this.lawRepository = lawRepository; } @Override public void invoke(LawExcelDTO data, AnalysisContext context) { Law law = new Law(); law.setLawName(data.getLawName()); law.setKeywords(data.getKeywords()); law.setPublishDept(data.getPublishDept()); List<ChapterSection> chapters = data.getChapters().stream() .map(c -> new ChapterSection(c.getChapter(), c.getClause(), law)) .collect(Collectors.toList()); law.setChapters(chapters); lawRepository.save(law); } @Override public void doAfterAllAnalysed(AnalysisContext context) {} } // LawExcelDTO.java (Excel映射对象) public class LawExcelDTO { @ExcelProperty("法律名称") private String lawName; @ExcelProperty("关键词") private String keywords; @ExcelProperty("发布部门") private String publishDept; @ExcelProperty("章节条款") private List<ChapterSectionDTO> chapters; } ``` ##### 5. 文件上传接口 ```java @RestController public class FileUploadController { @PostMapping("/upload") public ResponseEntity<String> uploadExcel(@RequestParam("file") MultipartFile file) { try { EasyExcel.read(file.getInputStream(), LawExcelDTO.class, new LawDataListener(lawRepository)).sheet().doRead(); return ResponseEntity.ok("文件解析成功"); } catch (Exception e) { return ResponseEntity.status(500).body("解析失败: " + e.getMessage()); } } } ``` --- #### **二、前端实现(Vue + Element UI)** ##### 1. 文件上传组件 ```vue <template> <el-upload action="http://localhost:8080/upload" :before-upload="validateFile" :on-success="handleSuccess" :on-error="handleError" > <el-button type="primary">点击上传法律文件(.xlsx)</el-button> </el-upload> </template> <script> export default { methods: { validateFile(file) { const isExcel = file.type.includes('sheet') || file.name.endsWith('.xlsx'); if (!isExcel) { this.$message.error('仅支持Excel文件'); return false; } return true; }, handleSuccess(response) { this.$message.success(response.data); }, handleError() { this.$message.error('上传失败,请检查文件格式'); } } } </script> ``` --- #### **三、关键配置** ##### 1. 数据库配置(application.yml) ```yaml spring: datasource: url: jdbc:mysql://localhost:3306/law_db?useSSL=false username: root password: root jpa: hibernate: ddl-auto: update servlet: multipart: max-file-size: 50MB max-request-size: 50MB ``` ##### 2. 跨域配置(SpringBoot) ```java @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:8081") .allowedMethods("*"); } } ``` --- #### **四、扩展优化** - **数据校验**:在`LawExcelDTO`中添加`@NotBlank`注解,结合Hibernate Validator验证数据完整性[^2]。 - **批量插入**:使用JPA的`saveAll`方法优化批量插入性能。 - **异步处理**:通过`@Async`注解实现异步文件解析,避免阻塞请求线程[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值