4

I am trying to execute a POST from my angular application to a .net Web API instance but the server returns null.

server

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

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

angular *note that I am using HttpClient instead of Http.. not sure if this is also the problem

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);}}}

config

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}
            );
        }
    }

With above setting, I don't gen any 404 server error in client side(by checking chrome's console) but it returns null in backend. But as I tried with Postman, it sends values properly with the same url. If I don't include [FromBody] inside of method in backend, I get an 404 server error in client side. Furthermore, messages says "NO HTTP resource was found that matches the request URI". Similar question to this seems solving the problem by having [FromBody] but I still get a null... I also suspected maybe my web config file(not the one in above) should contain some headers, so when I added some headers like content type to be json and etc then I get 500 server error in client side. At this point i am really confused and not sure what to proceed.

UPDATE1

Following server code returns the message but I am still getting the body as null.. no errors have been observed

[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());
            }
        }
1
  • How does this even compile? Your c# method returns type string and the only return statement is in the catch block. This code should not compile. Commented Nov 8, 2017 at 16:03

3 Answers 3

10

I see multiple reasons why you would get unexpected errors and null values in your code:

  1. (error) Your .net method callBcknd should not even compile as it can only return something if there is an Exception.
  2. (error) You should send json when sending data to your api controller the message body and the api controller method should accept an complex object and not a primitive type like string/int/bool etc.
  3. (warning) Your angular service should expose functionality and return either observables or promises that the component can then subscribe to. Do not expose the HttpClient directly.
  4. (warning) Your web api should return interface IHttpActionResult instead of the type directly. Then you can use the built in methods like Ok and Content and BadRequest to return status information along with data. See also Action Results in Web API 2
  5. (suggestion) Use Route and RoutePrefix as attributes instead of relying on the route config. This is more flexible and will allow you to also specify parameters to be included in the URL which will make for a more RESTful design. See also Attribute Routing in ASP.NET Web API 2
  6. (suggestion) Add CamelCasePropertyNamesContractResolver to resolve between camel and pascal casing between your front end and backend. See also Serialization using ContractResolver

This is a good example of how make calls to a Web API and how to structure your code.

Note that these code samples only show the relevant parts that were added or modified

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));
}

Notice the following:

  1. ApiModel represents the incoming object in the request. The angular call then sends {content: 'This is what I have sent'} which mirrors this type.
  2. IHttpActionResult is the response type for your Web API method
  3. You can return different types along with status information in the method CallBcknd
  4. Route and RoutePrefix were added to give more control over the uri path.
  5. The angular component and service have been split into 2 methods, the service returns an observable and the component calls the service methods and subcribes to the returning observable. When you extend this example you want to replace any with defined expected results using interfaces and the same is true for any incoming parameters you want to send.
Sign up to request clarification or add additional context in comments.

15 Comments

I don't see anything wrong in client side, my original code just works fine sending data. I tried with JSON and verified the Request payload and status code returns OK. Problem is that backend catches error once this JSON arrives and body returns null. Thanks for your suggestion, I will keep that in mind after solving this specific problem.
@ZlatkoLoa - You need to figure out what the error is on the server. Post the Exception details once you have them. Also status 404 is route not found, you probably do not have everything as I have it above. Status 500 is unhandled exception in your code.
I get an error HttpErrorResponse{headers: HttpHeaders, status : 500}
Again, you need to figure out the exception on the server. Debug your server side code or log your Exception so you can read the message, stack trace, and the type. The code I provided above actually returns the entire exception so you can debug on the client side.
@ZlatkoLoa - See my point #2 above. You should not take primitive types as parameters when using FromBody. Use a complex type like I did in my sample and define it outside of another class (do not nest it, there is no reason to).
|
2

A Tipical call to a API from "Angular"

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);

}

The code in 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 });;
   }
}

8 Comments

Why post twice? You could have edited your previous answer and added any extra info you wanted to.
@Eliseo what does second method do?
If you use a dbs and LINQ several methods use Async, see learn.microsoft.com/en-us/dotnet/csharp/programming-guide/….
Sorry, I forgot the IResult interface (it's NOT c #, it's an own interface that I regularly use)
ah, do you have any other suggestions?
|
1

Well, what you're posting would look something like

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

Whereas your controller expects:

"valuehere" (which is a valid json for string).

You need to change the c# code to have a model for your DTO:

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

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

}

4 Comments

I will try tomorrow but I still face with the same problem when I just try to send a string like 'data'
@ZlatkoLoa that is because by default i suppose HttpClient in ng serializes everything as json.
would it make difference if i use Http tho?
@ZlatkoLoa No. your problem really is in the c# side. i will see if i can craft the code that can post raw strings for you, but this is quite rarely done these days - purely because it is EASIER to post proper models than it is to post strings like you are trying to.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.