locked
Returning responses from Web Api RRS feed

  • Question

  • User-1458727574 posted

    My web api needs to return a response. Can't seem to get it right. Here is the code for the controller.

    //Controller Code
            // POST api/<controller>
            [HttpPost]
            //public IActionResult Post([FromBody] customersT customers)
            public string Post([FromBody] customersT customers)
            {
                SageHelper sageHelper;
    
                if(!ModelState.IsValid)
                {
                    return "Invalid XML"; //BadRequest(ModelState);
                }
                else
                {
                    sageHelper = new SageHelper(customers);
                }
    
    
                return sageHelper.ReturnValue; //Ok();
            }
    

    Here is the helper class that will do the post to Sage

            public static async Task CreateCustomer(string uri, customersT customers)
            {
                var customer = new
                {
                    CustomerNumber = customers.Items[0].custNo,
                    ShortName = customers.Items[0].custNo,
                    CustomerName = customers.Items[0].custAddresses[0].name,
                    GroupCode = "RTL"
                };
    
                await SendRequest(new HttpMethod("POST"), uri + @"AR/ARCustomers", customer);
            }
    
            public static async Task<object> SendRequest(HttpMethod method, string requestUri, object payload = null)
            {
                HttpContent content = null;
    
                string responsePayload = "";
                // Serialize the payload if one is present
                if (payload != null)
                {
                    var payloadString = JsonConvert.SerializeObject(payload);
                    content = new StringContent(payloadString, Encoding.UTF8, "application/json");
                }
    
                // Create the Web API client with the appropriate authentication
                using (var httpClientHandler = new HttpClientHandler { Credentials = new NetworkCredential("USER", "PASSWORD") })
                using (var httpClient = new HttpClient(httpClientHandler))
                {
    
                    // Create the Web API request
                    var request = new HttpRequestMessage(method, requestUri)
                    {
                        Content = content
                    };
    
                    // Send the Web API request
                    try
                    {
                        var response = await httpClient.SendAsync(request);
                        responsePayload = await response.Content.ReadAsStringAsync();
    
                        var statusNumber = (int)response.StatusCode;
    response.StatusCode);
    
                        if (statusNumber < 200 || statusNumber >= 300)
                        {
                            // Console.WriteLine(responsePayload);
                            throw new ApplicationException(statusNumber.ToString());
                        }
                    }
                    catch (Exception e)
                    {
                        Environment.Exit(0);
                    }
                }
    
                return string.IsNullOrWhiteSpace(responsePayload) ? null : JsonConvert.DeserializeObject(responsePayload);
            }
    

    When the HTTP post hits the controller, it sends the request off to the SageHelper object which processes the data and pushes into Sage. The SendRequest method returns a string which I actually don't want for this project. Where it gets the response.statusCode from the response object, I am trying to get that set into a public property of the SageHelper class

    public string ResponseCode { get; set; }

    Normally, I would just put ResponseCode = response.statusCode; in the SendRequest method but I can't see the property from that method.

    Monday, June 18, 2018 9:16 AM

All replies

  • User1120430333 posted

    https://stackoverflow.com/questions/10732644/best-practice-to-return-errors-in-asp-net-web-api

    The link should help you.

    You can use a HTTP code that not already reserved, like pick a 4XX number that's not in use and check for it on the client-side.

    https://en.wikipedia.org/wiki/List_of_HTTP_status_codes

    Monday, June 18, 2018 9:45 AM
  • User-1458727574 posted

    My ResponseCode property is in the SageHelp class. When I call the SendRequest method in SageHelper, how come that method can't see the ResponseCode property? If I could set the values there, the calling function would get the string and pass back to the client but for some reason I cannot set the property in the SageHelp class from a method in the same class.

    Monday, June 18, 2018 10:02 AM
  • User-1458727574 posted

    Ahh hang on. With multithreaded applications, .NET is designed not to send back stuff to the caller, including exceptions

    https://www.filipekberg.se/2012/09/20/avoid-shooting-yourself-in-the-foot-with-tasks-and-async/

    So, how do I push back to the CustomersController to say success of fail?

    Monday, June 18, 2018 10:36 AM
  • User475983607 posted

    AnyUserNameThatLetsMeIn

    So, how do I push back to the CustomersController to say success of fail?

    As far as I can tell the action invokes the SageHelper constructor but that's about it.  I don't see where the action actually invokes a request.

    I would expect the action to look more like this.

    //Controller Code
    // POST api/<controller>
    [HttpPost]
    //public IActionResult Post([FromBody] customersT customers)
    public string Post([FromBody] customersT customers)
    {
    	if(!ModelState.IsValid)
    	{
    		BadRequest(ModelState);
    	}
    
    	SageHelper sageHelper = new SageHelper()
    	return sageHelper.PostAsync<Customer>("api/Customer", customers)
    }

    It seems you are struggling with two technical issues.  1) using Sage.  2) writing a REST Client in .NET.

    Frankly, the helper class is not good and needs refactoring. 

    Start by reading how to use the HttpClient in .NET

    https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/calling-a-web-api-from-a-net-client

    If you are still using Core then read the latest docs and use DI rathat than instanciating in the action.

    Get HttpClient working in a console app.  Then simply move the logic to web api.  Web api is just an HTTP endpoint to invoke Sage requests.  Which brings me to my next point.  Are you sure that you need a REST service being that Sage is already a REST service?  All you're doing is wrapping Sage in another service layer. 

    Monday, June 18, 2018 11:15 AM
  • User-1458727574 posted

    As far as I can tell the action invokes the SageHelper constructor but that's about it.  I don't see where the action actually invokes a request.

    It seems you are struggling with two technical issues.  1) using Sage.  2) writing a REST Client in .NET.

    Frankly, the helper class is not good and needs refactoring. 

    Start by reading how to use the HttpClient in .NET

    https://docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/calling-a-web-api-from-a-net-client

    If you are still using Core then read the latest docs and use DI rathat than instanciating in the action.

    Get HttpClient working in a console app.  Then simply move the logic to web api.  Web api is just an HTTP endpoint to invoke Sage requests.  Which brings me to my next point.  Are you sure that you need a REST service being that Sage is already a REST service?  All you're doing is wrapping Sage in another service layer. 

    To answer your points above:

    The constructor is called and the data is passed to the constructor. It seemed a reasonable approach to me. Maybe it isn't the best way?

    Yes, I'm struggling because working for a small company and being pushed back into development after over 10 years of not doing development, the waters have somewhat changed a bit since I was doing it full time. I started here more on the consultancy side, but it seems I am getting pushed back again and I must admit, I don't want to, but the alternative is not having a job, so trial by fire, learn quick. I had familiarised myself with the Sage API which was (and has been for 20 years), using their DLLs to do the integration. That won't work as the DLLs aren't compatible with working with Web API and .NET Core. I found out about Sage's Web Api at the end of last week and having worked on one MVC project which was dumped on me prior to this one and my not knowing anything about MVC, I muddled my way through. So, yes I find out that Sage provides a RESTful API. I am not writing the client. The client (our customer) already has an application that pumps out XML. I need to have an API that picks that XML via a web service, that I'm currently writing, and pushes into Sage using it's Web API.

    Not sure what you mean by this: "...If you are still using Core then read the latest docs and use DI rathat than instanciating in the action...". Taking a guess, you would avoid using the SageHelper Class and put things in the controller? My thinking here was just to keep the code cleaner to move things out but maybe I didn't do it in the best way?

    I'm new to all this having not been coding for over 10 years and being rather dumped on but sometimes life doesn't go the way you plan it.

    I've read many, many articles about a lot of things. The major issue is when you don't know what to look for you never know what the right thing to read is. You just hope that someone can help get you started in the right direction and you can work it out from there with assistance from people far more versed in these things like you :)

    Not knowing a lot of the new terminology doesn't help. Having a boss that seems to think "you're a programmer, you know everything, can do everything without having to learn it first" really doesn't help either.

    Monday, June 18, 2018 11:40 AM
  • User475983607 posted

    The constructor is called and the data is passed to the constructor. It seemed a reasonable approach to me. Maybe it isn't the best way?

    My point is you have a constructor but no signs of implementation.  There's no indication in the code that you are making a request.

    The client (our customer) already has an application that pumps out XML. I need to have an API that picks that XML via a web service, that I'm currently writing, and pushes into Sage using it's Web API.

    That's my point. If you already have the XML then why are sending the XML to Web API.  Just send the XML directly to the Sage Rest API or maybe you have to convert the XML to an object first - not sure.  Can you explain the reasoning for creating a service?  

    Not sure what you mean by this: "...If you are still using Core then read the latest docs and use DI rathat than instanciating in the action...". Taking a guess, you would avoid using the SageHelper Class and put things in the controller? My thinking here was just to keep the code cleaner to move things out but maybe I didn't do it in the best way?

    DI is a major pattern in ASP Core.  Not using DI is like tying on hand behind your back.

    I've read many, many articles about a lot of things. The major issue is when you don't know what to look for you never know what the right thing to read is. You just hope that someone can help get you started in the right direction and you can work it out from there with assistance from people far more versed in these things like you :)

    My first question is do you really need a to create a service?  Is the only reason for the service to convert XML to an object?  If so, then rethink the approach.  It seems that Sage already provides a client so why not use the client directly?  Is this an architectural issue where you must build a service?

    Monday, June 18, 2018 11:54 AM
  • User-1458727574 posted

    AnyUserNameThatLetsMeIn

    The constructor is called and the data is passed to the constructor. It seemed a reasonable approach to me. Maybe it isn't the best way?

    My point is you have a constructor but no signs of implementation.  There's no indication in the code that you are making a request.

    Ahh I see. When I invoke the constructor, the XML body was being passed to it, so the thing ran from the constructor.

    AnyUserNameThatLetsMeIn

    The client (our customer) already has an application that pumps out XML. I need to have an API that picks that XML via a web service, that I'm currently writing, and pushes into Sage using it's Web API.

    That's my point. If you already have the XML then why are sending the XML to Web API.  Just send the XML directly to the Sage Rest API or maybe you have to convert the XML to an object first - not sure.  Can you explain the reasoning for creating a service?  

    I have been asked to do that. The customer has an application that produces XML but in a completely different format for what I need, so my service is designed for them to call with their existing application to pass XML which I need to then pull apart and construct the call to Sage using their API.

    AnyUserNameThatLetsMeIn

    Not sure what you mean by this: "...If you are still using Core then read the latest docs and use DI rathat than instanciating in the action...". Taking a guess, you would avoid using the SageHelper Class and put things in the controller? My thinking here was just to keep the code cleaner to move things out but maybe I didn't do it in the best way?

    DI is a major pattern in ASP Core.  Not using DI is like tying on hand behind your back.

    AnyUserNameThatLetsMeIn

    I've read many, many articles about a lot of things. The major issue is when you don't know what to look for you never know what the right thing to read is. You just hope that someone can help get you started in the right direction and you can work it out from there with assistance from people far more versed in these things like you :)

    My first question is do you really need a to create a service?  Is the only reason for the service to convert XML to an object?  If so, then rethink the approach.  It seems that Sage already provides a client so why not use the client directly?  Is this an architectural issue where you must build a service?

    Monday, June 18, 2018 12:38 PM
  • User475983607 posted

    Ahh I see. When I invoke the constructor, the XML body was being passed to it, so the thing ran from the constructor.

    The posted code does not show the constructor invoking a method.  

    I have been asked to do that. The customer has an application that produces XML but in a completely different format for what I need, so my service is designed for them to call with their existing application to pass XML which I need to then pull apart and construct the call to Sage using their API.

    Ok, so the Web APi stuff is just a mapper to the Sage thing.  I suggest that you write a very simple Console test application to get the HttpClient code working first.  Then move the HttpClient code to Web API. Again, the same simple known working code and make sure it is still working in Web API.

    Next, refactor HttpClient and use DI.  Test again.  That is assuming you are still using ASP Core.

    See the following.

    https://blogs.msdn.microsoft.com/webdev/2018/02/28/asp-net-core-2-1-preview1-introducing-httpclient-factory/

    Be sure to read the other 3 blogs in the series below.

    https://www.stevejgordon.co.uk/introduction-to-httpclientfactory-aspnetcore

    Monday, June 18, 2018 1:21 PM