You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 22:48:18