【问题标题】:Angular Posting to .net Web APIAngular 发布到 .net Web API
【发布时间】:2018-04-20 16:36:29
【问题描述】:

我正在尝试从我的 Angular 应用程序执行 POST 到 .net Web API 实例,但服务器返回 null

服务器

    [HttpPost] 
    public string callBcknd([FromBody]string body)
    {
        try
        {
            Log.Info(string.Format("{0}", body));

        }
        catch(Exception ex)
        {
            return "error";
        }
    }
}

angular *请注意,我使用的是 HttpClient 而不是 Http.. 不确定这是否也是问题

callServer(){
    var test = { "name": "John" }
    let data = JSON.stringify(test);
    let headers = new HttpHeaders(); 
    headers.set('Content-Type', 'application/json');
    this.appService.http.post('http://localhost:3000/api/WebApI/callBcknd', 
                          test, 
                          {headers: headers})
  .subscribe(data => {console.log(data);}}}

配置

public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {

            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new {action = "GET", id = RouteParameter.Optional}
            );
        }
    }

通过上述设置,我不会在客户端生成任何 404 服务器错误(通过检查 chrome 的控制台),但它在后端返回 null。但是当我尝试使用 Postman 时,它会使用相同的 url 正确发送值。如果我在后端的方法中不包含 [FromBody],我会在客户端收到 404 服务器错误。此外,消息说“未找到与请求 URI 匹配的 HTTP 资源”。与此类似的问题似乎通过使用 [FromBody] 来解决问题,但我仍然得到一个空值......我还怀疑我的网络配置文件(不是上面的那个)应该包含一些标题,所以当我添加一些标题时,比如内容类型为 json 等然后我在客户端收到 500 服务器错误。在这一点上,我真的很困惑,不知道该怎么做。

更新1

以下服务器代码返回消息,但我仍然将正文设为 null.. 没有观察到错误

[HttpPost]
        public IHttpActionResult Callbcknd([FromBody] string body)
        {
            try
            {
                Log.Info(string.Format("called with data {0}", body));

                return Ok(new { Message = "It worked!" });
            }

            catch(Exception ex)
            {
                return base.Content(HttpStatusCode.InternalServerError, ex.ToString());
            }
        }

【问题讨论】:

  • 这如何编译?您的 c# 方法返回类型 string 并且唯一的返回语句在 catch 块中。此代码不应编译。

标签: c# angular asp.net-web-api


【解决方案1】:

我看到了导致代码中出现意外错误和空值的多种原因:

  1. (错误)您的 .net 方法 callBcknd 甚至不应该编译,因为它只能在出现异常时返回一些东西。
  2. (错误)您应该在向您的 api 控制器发送数据时发送 json,消息正文和 api 控制器方法应该接受一个复杂的对象,并且不是原始类型,如string/int /bool
  3. (警告)您的 Angular 服务应该公开功能并返回组件可以订阅的可观察对象或承诺。不要直接暴露HttpClient
  4. (警告)您的 web api 应该返回接口 IHttpActionResult 而不是直接返回类型。然后,您可以使用 Ok 和 Content 和 BadRequest 等内置方法返回状态信息以及数据。另见Action Results in Web API 2
  5. (建议)使用RouteRoutePrefix 作为属性,而不是依赖于路由配置。这更灵活,还允许您指定要包含在 URL 中的参数,这将有助于实现更 RESTful 的设计。另见Attribute Routing in ASP.NET Web API 2
  6. (建议)添加CamelCasePropertyNamesContractResolver 以解析前端和后端之间的骆驼和帕斯卡大小写。另见Serialization using ContractResolver

这是一个很好的例子,说明如何调用 Web API 以及如何构建代码。

请注意,这些代码示例仅显示了添加或修改的相关部分

WebApiConfig.cs

public static class WebApiConfig {
    public static void Register(HttpConfiguration config) {
        // add this to ensure that casing is converted between camel case (front end) and pascal case (c#/backend)
        var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
        json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

        config.MapHttpAttributeRoutes();
    }
}

ApiModel.cs

public class ApiModel {
    public string Content {get;set;}
}

WebApIController.cs

[RoutePrefix("api/WebApI")]
public class WebApIController : ApiController {

    [HttpPost] 
    [Route("callBcknd")]
    public IHttpActionResult CallBcknd([FromBody] ApiModel body)
    {
        try
        {
            Log.Info(string.Format("{0}", body.Content));
            return Ok(new {Message = "It worked!"});
        }
        catch(Exception ex)
        {
            // example of how to return error with content. I would not recommend actually returning the exception details to the client in a production setting
            return base.Content(HttpStatusCode.InternalServerError, ex.ToString());
        }
    }
}

application.service.ts

constructor(private httpClient: HttpClient){}

callServer(data: {content: string}) : Observable<any> {
    return this.httpClient.post('http://localhost:3000/api/WebApI/callBcknd', data);
}

application.component.ts

constructor(private myService: MyService){}

onDoSomething(){
    this.myService.callServer({content: 'This is what I have sent'})
        .subscribe(data => console.log("Succeeded, result = " + data), (err)=> console.error("Failed! " + err));
}

注意以下几点:

  1. ApiModel 代表请求中的传入对象。然后,角度调用发送 {content: 'This is what I have sent'},它反映了这种类型。
  2. IHttpActionResult 是您的 Web API 方法的响应类型
  3. 您可以在方法CallBcknd 中返回不同的类型以及状态信息
  4. 添加了 RouteRoutePrefix 以更好地控制 uri 路径。
  5. Angular 组件和服务被拆分为 2 个方法,服务返回一个 observable,组件调用服务方法并订阅返回的 observable。当您扩展此示例时,您希望使用接口将 any 替换为定义的预期结果,对于您要发送的任何传入参数也是如此。

【讨论】:

  • 我在客户端没有看到任何问题,我的原始代码可以正常发送数据。我尝试使用 JSON 并验证请求有效负载和状态代码返回 OK。问题是一旦此 JSON 到达并且正文返回 null,后端就会捕获错误。感谢您的建议,解决这个具体问题后我会记住这一点。
  • @ZlatkoLoa - 您需要找出服务器上的错误。获得异常详细信息后发布它们。状态 404 也是找不到路由,你可能没有我上面的所有东西。状态 500 是您的代码中未处理的异常。
  • 我收到一个错误 HttpErrorResponse{headers: HttpHeaders, status : 500}
  • 再次,您需要找出服务器上的异常。调试您的服务器端代码或记录您的异常,以便您可以阅读消息、堆栈跟踪和类型。我上面提供的代码实际上返回了整个异常,因此您可以在客户端进行调试。
  • @ZlatkoLoa - 请参阅上面的第 2 点。使用 FromBody 时不应将原始类型作为参数。使用我在示例中所做的复杂类型,并在另一个类外部定义它(不要嵌套它,没有理由)。
【解决方案2】:

从“Angular”对 API 的典型调用

update(data: string): Observable<IResponse> {
    console.log(data);
    let url = '...';
    let headers = new Headers({
        'Content-Type': 'application/json; charset=utf-8',
    });
    let options = new RequestOptions({ headers: headers })

    return this._http.post(url, data, options)
        .map((res: any) => {
            return res.json();
        })
        .catch(this.handleError);

}

API 中的代码

[HttpPost] 
public string callBcknd([FromBody]string body)
{
   try
   {
      Log.Info(string.Format("{0}", body));
      //You must return something
      return "Post Realized";
   }
   catch(Exception ex)
   {
      return "error";
   }
}

//I like call async


[HttpPost] 

public async Task<IActionResult>callBcknd([FromBody]string body)
{
   try
   {
      Log.Info(string.Format("{0}", body));
      //await "some action"
      //You can return OK("success") or an object
      return Ok(new { success = true, description = "callback sucesfully" });;
   }
   catch(Exception ex)
   {
            //You can return OK("error") or an object
      return Ok(new { success = false, description = ex.InnerException });;
   }
}

【讨论】:

  • 为什么要发两次?您可以编辑之前的答案并添加您想要的任何额外信息。
  • @Eliseo 第二种方法是做什么的?
  • 如果你使用一个dbs和LINQ几个方法使用Async,见docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…
  • 对不起,我忘记了IResult接口(不是c#,是我自己常用的接口)
  • 啊,你还有什么建议吗?
【解决方案3】:

嗯,你发布的内容看起来像

{"body":{// something here }}

而您的控制器期望:

"valuehere"(这是字符串的有效 json)。

您需要更改 c# 代码以获得 DTO 的模型:

public class PostedObject{
 public object Data {get;set;}
}

  [HttpPost] 
    public string callBcknd([FromBody]PostedObject body)
    {
    // do things

}

【讨论】:

  • 我明天会尝试,但是当我尝试发送像“数据”这样的字符串时,我仍然面临同样的问题
  • @ZlatkoLoa 这是因为默认情况下我认为 ng 中的HttpClient 将所有内容序列化为 json。
  • 如果我使用 Http 会有区别吗?
  • @ZlatkoLoa 不,您的问题确实出在 c# 方面。我会看看我是否可以编写可以为您发布原始字符串的代码,但是这些天很少这样做 - 纯粹是因为发布正确的模型比发布您想要的字符串更容易。
猜你喜欢
  • 2017-08-05
  • 1970-01-01
  • 2020-12-04
  • 2021-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-24
相关资源
最近更新 更多