asp.net 2.0中的URL重写以及urlMappings问题http://www.master8.net/data/2006/0405/article_2619.htm


在asp.net2.0中的urlMappings倒是非常好用,可惜暂不支持正则表达式,不过,好在如果用IHttpModule的话

    不管什么样的请求都会先经过IHttpModule这样就为URL重写提供了一个好机会:

下面是我写的一个IHttpModule:
using System;
using System.Web;

public class ReWriteModule:IHttpModule
{
public ReWriteModule()
{
}
public override string ToString()
{
return this.GetType().ToString();
}


void IHttpModule.Dispose()
{

}
private static System.Xml.XmlDocument ruleDoc = null;
private static System.Xml.XmlDocument GetRuleConfig(System.Web.HttpContext app)
{
if (ruleDoc == null)
{
ruleDoc = new System.Xml.XmlDocument();
ruleDoc.Load(app.Server.MapPath("~/rule.xml"));
}
return ruleDoc;
}
public static string GetUrl(System.Web.HttpContext cxt,string path)
{

System.Xml.XmlDocument doc = GetRuleConfig(cxt);
System.Xml.XmlNodeList lst= doc.GetElementsByTagName("RewriterRule");
string pat="";
foreach (System.Xml.XmlNode nd in lst)
{
System.Xml.XmlNodeList sub = nd.ChildNodes[0].ChildNodes;
foreach(System.Xml.XmlNode chk in sub)
{
pat = "^" + chk.InnerText+"$";
System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex(pat, System.Text.RegularExpressions.RegexOptions.Compiled | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
if(reg.IsMatch(path))
{
return reg.Replace(path, nd.ChildNodes[1].InnerText);
}
}
}
return null;

}
void IHttpModule.Init(HttpApplication context)
{
context.BeginRequest += delegate(object sender, EventArgs e)
{


System.Web.HttpContext cxt = context.Context;

if (cxt.Request.ContentType != "image/pjpeg")
{
string type = cxt.Request.ContentType.ToLower();
string path = cxt.Request.Path;
string apppath = cxt.Request.ApplicationPath;
path = path.Remove(0, apppath.Length);
path = "~" + path;

string newUrl = GetUrl(cxt, path.TrimEnd().TrimStart());
if (newUrl != null)
{
cxt.Response.Filter = new ResponseFilter(cxt.Response.Filter,cxt.Request.Path);
cxt.Response.Write("请求的路径:" + path);
cxt.Response.Write("<BR>");
cxt.Response.Write("转向的目的URL:" + newUrl);
cxt.Response.Write("<BR>");
cxt.RewritePath(newUrl);

 

}//如果要求处理所有的请求时用到
//else
//{
// cxt.Response.Write(cxt.Request.Path + "<BR>");
// cxt.Response.Write("你请求的资源不存在或无权访问!");
// cxt.Response.Flush();
// cxt.Response.End();
//}
}

};
}


}由于一旦进行了URL重写,原先的WEBFORM中的Action会发生改变,容易造成:请求的资源不存在问题

具体怎么样?各位DX看看就清楚了!!!

所有才有了这个ResponseFilter了,实现如下,
public class ResponseFilter:System.IO.Stream
{
public ResponseFilter(System.IO.Stream sink,string _str)
{
_sink = sink;
//
// TODO: 在此处添加构造函数逻辑
//
this.str = _str;
}
private string str = "";
private System.IO.Stream _sink;
private long _position;
private System.Text.Encoding end=System.Text.Encoding.GetEncoding("GB18030");
private System.Text.StringBuilder oOutput = new System.Text.StringBuilder();
// The following members of Stream must be overriden.
public override bool CanRead
{
get { return true; }
}

public override bool CanSeek
{
get { return true; }
}

public override bool CanWrite
{
get { return true; }
}

public override long Length
{
get { return 0; }
}

public override long Position
{
get { return _position; }
set { _position = value; }
}

public override long Seek(long offset, System.IO.SeekOrigin direction)
{
return _sink.Seek(offset, direction);
}

public override void SetLength(long length)
{
_sink.SetLength(length);
}

public override void Close()
{
_sink.Close();
}

public override void Flush()
{
_sink.Flush();
}

public override int Read(byte[] buffer, int offset, int count)
{
return _sink.Read(buffer, offset, count);
}

// The Write method actually does the filtering.
public override void Write(byte[] buffer, int offset, int count)
{
string szBuffer = System.Text.UTF8Encoding.UTF8.GetString(buffer, offset, count);
string ap="action=/"";
int pos=-1;
if ((pos=szBuffer.IndexOf(ap) )!= -1)
{
int epos = szBuffer.IndexOf("/"", pos + ap.Length+1);
if (epos != -1)
{
szBuffer= szBuffer.Remove(pos + ap.Length, epos - pos - ap.Length);
}

szBuffer = szBuffer.Insert(pos + ap.Length, this.str);

byte[] data = System.Text.UTF8Encoding.UTF8.GetBytes(szBuffer);
_sink.Write(data, 0, data.Length);

}
else
{
oOutput.Append(szBuffer);
}

//下面的这一段可以用来修改<Head></head>之间的内容;
//Regex oEndFile = new Regex("</head>", RegexOptions.IgnoreCase|RegexOptions.Compiled);
//if (oEndFile.IsMatch(szBuffer))
//{
// //Append the last buffer of data
// //附加上缓冲区中的最后一部分数据
// oOutput.Append(szBuffer);
// //Get back the complete response for the client
// //传回完整的客户端返回数据
// string szCompleteBuffer = oOutput.ToString().ToLower();
// int ipos = szCompleteBuffer.IndexOf("<title>");
// int epos = szCompleteBuffer.IndexOf("</title>",ipos+7);
// string sp = szCompleteBuffer.Substring(ipos+7, epos - ipos );
// szCompleteBuffer = szCompleteBuffer.Remove(ipos+7,sp.Length-7);
// szCompleteBuffer = szCompleteBuffer.Insert(ipos + 7, "dhz");
// // szCompleteBuffer = szCompleteBuffer.Replace(sp, "dhz");
// //No match, so write out original data
// //没有匹配,因此写入源代码
// byte[] data = System.Text.UTF8Encoding.UTF8.GetBytes(szCompleteBuffer);
// _sink.Write(data, 0, data.Length);
//}
//else
//{
// oOutput.Append(szBuffer);
//}
}
}

//////而重候规则呢则是用xml文件配置如下;

当然在web.config通过自定义配置节做也可以的

<?xml version="1.0" encoding="utf-8" ?>
<Rules>
<RewriterRule>
<LookFors>
<LookFor>~/(/d{4})/(/d{2})/.html</LookFor>
<LookFor>~/(/d{4})/(/d{2})/</LookFor>
<LookFor>~/(/d{4})/(/d{2})</LookFor>
<LookFor>~/(/d{4})/(/d{2})/index.html</LookFor>
</LookFors>
<SendTo>~/Pro.aspx?year=$1&month=$2</SendTo>
</RewriterRule>
<RewriterRule>
<LookFors>
<LookFor>~/pc</LookFor>
</LookFors>
<SendTo>~/Test2.aspx</SendTo>
</RewriterRule>
</Rules>
//这个规则写的不好,如第一个就可以用一个正则表达式来做。但是一时不知道怎么写好,好像要用到什么反捕获组的概念,正在思考这个东东!!
来源: 站长吧 - http://www.master8.net/data/2006/0405/article_2619.htm
 

<think>根据问题描述,两个URL中,`/app/all` 可以正常访问,而 `/app/Transactional/kucunbianji` 返回404(未找到)。我们需要分析可能的原因。 ### 可能原因分析 1. **URL路径错误**:检查控制器的请求映射路径和方法上的路径组合。 2. **HTTP方法不匹配**:客户端请求的HTTP方法(如POST)与控制器中定义的方法不匹配。 3. **包扫描问题**:`TransactionalController` 所在的包可能没有被Spring Boot的组件扫描到。 4. **上下文路径(Context Path)配置**:如果配置了server.servlet.context-path,则URL前缀会改变。 5. **控制器未被加载**:检查是否因为某些原因(如缺少注解、依赖等)导致控制器未被Spring加载。 ### 详细检查步骤 #### 1. 检查URL路径 - `TransactionalController` 的类级别注解:`@RequestMapping("/app/Transactional")` - 方法级别注解:`@RequestMapping(value = "/kucunbianji", method = RequestMethod.POST)` - 因此,完整的URL路径应该是:`/app/Transactional/kucunbianji` #### 2. 检查HTTP方法 - 方法上指定了`method = RequestMethod.POST`,所以必须使用POST请求。 #### 3. 检查包扫描 - 确保`TransactionalController`位于主启动类(带有`@SpringBootApplication`注解的类)的子包中。如果不在,需要手动配置扫描包。 #### 4. 检查上下文路径 - 在`application.properties`或`application.yml`中检查是否有以下配置: ```properties server.servlet.context-path=/somepath ``` 如果有,则URL需要加上该上下文路径,例如:`http://192.168.1.11:8080/somepath/app/Transactional/kucunbianji` #### 5. 检查控制器是否加载 - 启动应用时,查看日志中是否有以下信息: ``` Mapped "{POST /app/Transactional/kucunbianji}" onto public ... ``` 如果没有,说明控制器未被加载。 ### 解决方案 #### 方案1:检查并修正包结构 确保`TransactionalController`在Spring Boot主应用的子包下。例如,主应用在`com.kucun`包,则控制器应该在`com.kucun.controller`或子包中。 #### 方案2:添加组件扫描 如果控制器不在主应用的子包中,可以在主应用类上添加组件扫描: ```java @SpringBootApplication @ComponentScan({"com.kucun.controller", "com.kucun"}) // 添加需要扫描的包 public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` #### 方案3:检查请求方法 确保使用POST方法请求,且Content-Type为`application/x-www-form-urlencoded`(因为使用了`@RequestParam`)。如果使用JSON格式,需要改为`@RequestBody`。 #### 方案4:检查上下文路径 在`application.properties`中,如果没有配置`server.servlet.context-path`,则忽略。如果配置了,则需要在URL前加上配置的路径。 #### 方案5:查看日志 启动应用后,访问`/actuator/beans`端点(需要添加Spring Boot Actuator依赖)或查看启动日志,确认`TransactionalController`是否被注册。 ### 示例代码修正 如果请求参数使用JSON格式,可以修改为: ```java @RequestMapping(value = "/kucunbianji", method = RequestMethod.POST) public Information requestMethodName(@RequestBody KucunEditRequest request) { // 使用一个对象来接收参数 bancaiService.updateKucunAndCreateJinhuo(request.getKucunId(), request.getShuliang(), request.getUserId(), request.getText()); return Information.NewSuccess(null); } // 内部类定义 public static class KucunEditRequest { private Integer kucunId; private Integer shuliang; private Integer userId; private String text; // getters and setters } ``` ### 测试方法 使用Postman或curl测试: ```bash curl -X POST http://192.168.1.11:8080/app/Transactional/kucunbianji \ -H "Content-Type: application/json" \ -d '{"kucunId":1, "shuliang":10, "userId":1, "text":"test"}' ``` ### 常见问题总结 - **404错误**:通常由于URL路径不正确或控制器未加载。 - **405错误**:HTTP方法不正确(如用GET访问POST接口)。 - **415错误**:请求的Content-Type不支持。 ### 最终建议 1. 检查日志,确认控制器映射的URL2. 使用正确的HTTP方法和Content-Type。 3. 确保控制器所在的包被扫描到。 §§相关问题§§: 1. Spring Boot中如何配置多个包路径的组件扫描? 2. 如何查看Spring Boot应用中的所有注册端点? 3. 使用@RequestParam和@RequestBody有什么区别? 4. 如何自定义Spring Boot的上下文路径?</think>### 404 错误分析与解决 #### 问题定位 `/app/all` 正常而 `/app/Transactional/kucunbianji` 报 404 错误,表明问题出在特定端点的路由配置上。以下是关键检查点: 1. **控制器扫描问题** ```java // 主启动类需确保扫描到控制器包 @SpringBootApplication(scanBasePackages = "com.kucun.controller") public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` - 检查 `scanBasePackages` 是否包含 `com.kucun.controller` 2. **路径大小写敏感** ```java @RequestMapping("/app/Transactional") // 首字母大写 ``` - HTTP 请求默认区分大小写:`/app/transactional` ≠ `/app/Transactional` - 解决方案: ```java @RequestMapping("/app/transactional") // 改为全小写 ``` 3. **HTTP 方法不匹配** ```java @RequestMapping(value = "/kucunbianji", method = RequestMethod.POST) ``` - 客户端必须使用 **POST** 请求 - 使用 GET 请求会返回 404 4. **上下文路径配置** ```properties # application.properties server.servlet.context-path=/api ``` - 实际 URL 会变为:`http://ip:8080/api/app/Transactional/kucunbianji` #### 诊断工具:端点检查 添加 Spring Boot Actuator 查看注册端点: ```gradle // build.gradle implementation 'org.springframework.boot:spring-boot-starter-actuator' ``` ```properties # application.properties management.endpoints.web.exposure.include=mappings ``` 访问 `http://192.168.1.11:8080/actuator/mappings` 查看所有注册路由: ```json { "handler": "public Information com.kucun.controller.TransactionalController.requestMethodName(...)", "predicate": "{POST /app/transactional/kucunbianji}", "details": { "handlerMethod": { "className": "com.kucun.controller.TransactionalController", "name": "requestMethodName", "descriptor": "(...)Lcom/kucun/data/entity/Information;" } } } ``` #### 修复方案 1. **统一路径大小写** ```java @RestController @RequestMapping("/app/transactional") // 全小写 public class TransactionalController { // ... } ``` 2. **添加备选路径** ```java @RequestMapping(path = { "/kucunbianji", "/kucun-bianji" // 添加备选路径 }, method = RequestMethod.POST) ``` 3. **调试日志配置** ```properties # application.properties logging.level.org.springframework.web=DEBUG logging.level.org.springframework.boot.autoconfigure=DEBUG ``` 4. **测试脚本** ```bash # 使用 curl 测试 curl -X POST http://192.168.1.11:8080/app/transactional/kucunbianji \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "kucunId=123&shuliang=10&userId=456&text=test" ``` #### 常见陷阱排查表 | 问题原因 | 检查点 | 解决方案 | |------------------------|--------------------------------|----------------------------| | 控制器未扫描 | 主启动类 `@SpringBootApplication` | 添加 `scanBasePackages` | | 路径大小写不匹配 | 浏览器地址栏 vs 代码注解 | 统一使用小写路径 | | HTTP 方法错误 | 客户端请求方法 vs `method=POST` | 使用 POSTMAN 发送 POST 请求 | | 缺少请求参数 | `@RequestParam` 参数缺失 | 提供所有必需参数 | | 上下文路径干扰 | `server.servlet.context-path` | 调整完整 URL 路径 | | 过滤器阻断请求 | 安全过滤器链 | 检查 Spring Security 配置 | ### 完整修复示例 ```java // 修复后的控制器 @RestController @RequestMapping("/app/transactional") // 全小写路径 public class TransactionalController { @Autowired private BancaiService bancaiService; // 添加备选路径和明确的消费类型 @PostMapping( path = {"/kucunbianji", "/inventory-edit"}, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE ) public Information editInventory( @RequestParam Integer kucunId, @RequestParam Integer shuliang, @RequestParam Integer userId, @RequestParam String text ) { bancaiService.updateKucunAndCreateJinhuo(kucunId, shuliang, userId, text); return Information.NewSuccess("库存更新成功"); } } ``` ### 验证步骤 1. 启动应用查看日志: ``` Mapped "{POST /app/transactional/kucunbianji}" onto public Information... Mapped "{POST /app/transactional/inventory-edit}" onto public Information... ``` 2. 使用 POSTMAN 测试: ```http POST /app/transactional/kucunbianji Content-Type: application/x-www-form-urlencoded kucunId=1001&shuliang=50&userId=2001&text=紧急补货 ``` 3. 预期响应: ```json { "success": true, "message": "库存更新成功", "data": null } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值