16

In a WebAPI project, i have a controller that checks a status of a product, based on a value the user enters.

Lets say they enter "123" and the response should be "status": 1, AND a list of products. If they enter "321" the "status" is 0, AND a list of products.

My question is, how do i build such a string correct in a WebAPI controller.

[Route("{value:int}")]
public string GetProducts(int value)
{
    var json = "";
    var products = db.Products;
    if (products.Any())
    {
        foreach (var s in products)
        {
            ProductApi product = new ProductApi();
            product.Name = s.Name;
            json += JsonConvert.SerializeObject(supplier);
        }
    }

    var status = db.Status;
    if (status.Any())
    {
        json += "{status:1}";
    }
    else
    {
        json += "{status:0}";
    }

    return json;
}

public class ProductApi
{
    public string Name { get; set; }
}

Also, is this output/response considered valid?

[
    {
        "id":1,
        "name":"product name"
    },
    {
        "id":2,
        "name":"product name 2"
    },
    {
        "id":3,
        "name":"product name 3"
    }
]

{
    "status": 0
}
Red
  • 2,728
  • 1
  • 20
  • 22
brother
  • 7,651
  • 9
  • 34
  • 58
  • 2
    You should not create json by your own, you can return an object from this method and make JSON as default format for returned data. – Red Mar 11 '16 at 10:02
  • Could you make an example based on the code above? – brother Mar 11 '16 at 10:12
  • JObect is also a great tool: https://www.newtonsoft.com/json/help/html/CreatingLINQtoJSON.htm – yu yang Jian May 21 '18 at 05:59
  • Possible duplicate of [How to change default Web API 2 to JSON formatter?](https://stackoverflow.com/questions/25224824/how-to-change-default-web-api-2-to-json-formatter) – Liam Jun 19 '18 at 12:56

2 Answers2

37

So here are the changes for your post:

First, you should make your api return Json by default when you pass a text/html request (is this you are looking for?), adding this line to your WebApiConfig class:

config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));

Second, I changed the code to return a real object, impersonating your response:

public class ProductApiCollection
{   
    public ProductApi[] Products { get; set; }      
    public byte Status { get; set; }
}

public class ProductApi
{
    public string Name { get; set; }
}

Method body:

public ProductApiCollection Get()
{
    var result = new ProductApiCollection();
    var dbProducts = db.Products;
    var apiModels = dbProducts.Select(x => new ProductApi { Name = x.Name } ).ToArray();
    result.Products = apiModels;

    var status = db.Status.Any() ? 1 : 0;
    result.Status = status;

    return result;
}

This will results in the following example json:

{
  "Products": [
    {
      "Name": "Pork"
    },
    {
      "Name": "Beef"
    },
    {
      "Name": "Chicken"
    },
    {
      "Name": "Salad"
    }
  ],
  "Status": 1
}

I strongly advise you not to do manual formatting for such things, and rely on built-in and 3rd party libraries. Otherwise, you will be reinventing the things already available, tested and ready to work.

Red
  • 2,728
  • 1
  • 20
  • 22
  • Seems like what i am looking fore. Just one question though: How do i iterate through db.Products to add them, instead of the manual "new ProductApi" in your example? I need to wrap the new ProductApi in a ForEach basicly? – brother Mar 11 '16 at 10:40
  • @brother, I updated the answer with simple sample code using db, please take a look. Also consider that if you have a lot of products, this method will be very slow, so paging is a good thing to consider from the start. – Red Mar 11 '16 at 10:47
  • Thanks @raderick.. i got it working excatly as i wanted! :) – brother Mar 11 '16 at 10:57
  • 3
    Remember you can take advantage of anonymous types in any scenario where you are selecting data to be returned as JSON – Firegarden Jun 18 '17 at 07:58
4

Just as raderick mentioned, you don't need to create your own custom JSON infrastructure.

public class ProductApi
{
    public int Id {get;set;}
    public string Name { get; set; }
}

public class ResponseDTO
{
    public int Status {get;set;}
    public List<ProductApi> { get; set; }
}

And in your API action, return like this:

[Route("{value:int}")]
public ResponseDTO GetProducts(int value)
{
    ResponseDTO result = ...// construct response here 

    return result;
}
Tamas Ionut
  • 4,240
  • 5
  • 36
  • 59