Serenity框架客户端TypeScript调用服务端方法报404如何解决
404错误核心原因
- ASP.NET Core 内置的动作名约定默认会自动移除方法名的
Async后缀,你定义的服务方法名为CreateAsync,实际生成的动作路由名为Create,前端请求CreateAsync路径时匹配不到对应动作,直接返回404。 - 你手动给
ServiceEndpoint子类加了[Route]特性,若项目启动配置中未开启MapControllers(),自定义特性路由不会被注册,也会导致路径找不到。 - 额外问题:前端代码里写的
async: false已经被现代浏览器完全废弃,不会生效,还可能触发请求异常;服务端代码中用.Result阻塞异步调用,存在线程死锁风险,且最终返回Ok(1)完全丢弃了已经反序列化完成的MyObject数据,逻辑存在问题。
正确实现步骤
1. 修复服务端代码
首先移除手动加的[Route]特性(Serenity 会按照约定自动为继承ServiceEndpoint的控制器注册路由,规则为~/Services/[模块名]/[控制器名]/[动作名],手动加路由反而容易和默认规则冲突),修正异步调用写法和返回逻辑,给动作显式指定动作名避免后缀被截断:
namespace myNameSpace { // 移除自定义Route特性,用Serenity默认约定路由即可 public class MyObjectController : ServiceEndpoint { [HttpPost] [ActionName("CreateAsync")] // 显式指定动作名,避免Async后缀被框架自动移除 public async Task<IActionResult> CreateAsync() { var BaseUrl = "http://localhost:5000/"; MyObject myobject = new MyObject(); using (var client = new HttpClient()) { client.BaseAddress = new Uri(BaseUrl); client.DefaultRequestHeaders.Clear(); client.DefaultRequestHeaders.Authorization = new BasicAuthenticationHeaderValue(username, password); // 用await替代.Result,避免异步死锁 HttpResponseMessage Res = await client.GetAsync("api/SomeFunction"); if (Res.IsSuccessStatusCode) { var objResponse = await Res.Content.ReadAsStringAsync(); myobject = JsonConvert.DeserializeObject<MyObject>(objResponse); } } // 直接返回实际拿到的对象,不要返回无意义的1 return Ok(myobject); } public class MyObject{ public string firstProperty { get; set; } public string secondProperty { get; set; } } } }
然后检查项目启动配置(Program.cs/Startup.cs),确保已经注册控制器和Serenity服务端点:
// 省略其他配置 app.UseEndpoints(endpoints => { endpoints.MapControllers(); // 必须加,否则自定义控制器路由不会生效 endpoints.MapDynamicServiceEndpoints(); // 其他原有路由配置 });
2. 修正前端TypeScript调用代码
删除无效的async: false配置,若你项目开启了Serenity服务代理生成,直接引用生成的服务代理类调用即可,不需要手动拼路径;如果没有生成代理,再手动写路径,注意关闭Serenity默认的服务响应格式校验,因为你返回的是自定义对象不是标准ServiceResponse:
onClick: () => { Q.serviceCall({ // 路径和服务端配置的ActionName对应 url: Q.resolveUrl('~/Services/ModuleName/MyObject/CreateAsync'), request: {}, // 因为返回的不是Serenity标准ServiceResponse,加这个参数避免框架解析响应报错 expectsServiceResponse: false, onSuccess: (response) => { // 这里response就是反序列化后的MyObject对象 var result = response as MyObject; console.log(result.firstProperty, result.secondProperty); }, onError: (response) => { Q.notifyError("调用服务失败"); } }); }
如果不想加
[ActionName("CreateAsync")]特性,直接把服务端方法名改成Create去掉Async后缀,前端路径改成~/Services/ModuleName/MyObject/Create也可以,这是Serenity官方推荐的命名约定。
内容的提问来源于stack exchange,提问作者BlackCat
相关产品推荐
相关产品推荐

