Web Api: recommended way to return json string

前端 未结 4 783
一个人的身影
一个人的身影 2020-12-10 15:57

I\'ve got a couple of services which already receive a json string (not an object) that must be returned to the client. Currently, I\'m creating the HttpResponseMessag

相关标签:
4条回答
  • 2020-12-10 16:25

    Set your Web Api to return JSON Format:

        public static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();
            // Force to ignore Request Content Type Header and reply only JSON
            config.Formatters.Clear();
            config.Formatters.Add(new JsonMediaTypeFormatter());
    
            var corsAttr = new EnableCorsAttribute("*", "*", "*");
            config.EnableCors(corsAttr);
        }
    

    and then return response like this:

            [HttpGet]
        [Route("{taskId}/list")]
        public IHttpActionResult GetTaskDocuments(string taskId)
        {
            var docs = repository.getTaskDocuments(taskId);
            if (docs != null)
            {
                return Ok(docs);
            }
            else
            {
                return Ok(new ResponseStatus() { Status = Constants.RESPONSE_FAIL, Message = repository.LastErrorMsg });
            }
        }
    

    Where ResponseStatus is next class:

     public class ResponseStatus
        {
            public string Status { get; set; }
            public string Message { get; set; }
        }
    
    0 讨论(0)
  • 2020-12-10 16:26

    Leave the response format to the content negotiation in order to be REST compliant, the client should decide what format it wants.

    In your web API action, you should just return Ok(your object) and web API will see what is the best format to return it.

    If you need to omit other formatters, then just remove the other MediaTypeFormatter objects just as Nkosi advised.

    0 讨论(0)
  • 2020-12-10 16:35

    Here is @Nkosi's answer translated to VB.Net.

    I am building a WebAPI2 backend to support TourOfHeroes.

    I imagine case where I have a 'heavy' object and I dont want to return all properties to all views. Imagine Hero had ~30 columns and I only need Id and Name for the Heroes.component.ts....

    I dont want to additionally create an object to represent each view just so I can JSON serialize them... DRY?

    So, here are the 2 classes that @NKosi posted. Not sure the shared class is necessary, I just implemented as @NKosi wrote it.

    Imports System.Net.Http
    Imports System.Web.Http
    Imports System.Net
    Imports System.Threading
    Imports System.Threading.Tasks
    
    Public Class JsonStringResultExtension
    
        Public Shared Function JSONString(Controller As ApiController,
                                                    Optional jsonContent As String = "",
                                                 Optional statusCode As HttpStatusCode = HttpStatusCode.OK) As CustomJsonStringResult
    
            Dim result As New CustomJsonStringResult(Controller.Request, statusCode, jsonContent)
            Return result
            End Function
    
      End Class
    
        Public Class CustomJsonStringResult
            Implements IHttpActionResult
    
        Private json As String
        Private statusCode As HttpStatusCode
        Private request As HttpRequestMessage
    
        Public Sub New(httpRequestMessage As HttpRequestMessage, Optional statusCode As HttpStatusCode = HttpStatusCode.OK, Optional json As String = "")
            Me.request = httpRequestMessage
            Me.json = json
            Me.statusCode = statusCode
        End Sub
    
        Public Function ExecuteAsync(cancellationToken As CancellationToken) As Task(Of HttpResponseMessage) Implements IHttpActionResult.ExecuteAsync
            Return Task.FromResult(Execute())
        End Function
    
        Private Function Execute() As HttpResponseMessage
            Dim response = request.CreateResponse(statusCode)
            response.Content = New StringContent(json, Encoding.UTF8, "application/json")
    
            Return response
        End Function
    
    
    End Class
    

    I will also add that you have to the following to webApiConfig.vb or you will get complaints that you cant parse the HTML

    config.Formatters.JsonFormatter.SupportedMediaTypes.Add(New MediaTypeHeaderValue("text/html"))
            config.Formatters.JsonFormatter.SupportedMediaTypes.Add(New MediaTypeHeaderValue("application/json"))
    

    Here is the webAPI controller call that supports the heroes.component.js Exactly like @Luis Abreu, my BOs are already returning JSON. I just want to return the correctly formed JSON as a string, as opposed to mapping my BO into another class here in the API for the sole purpose of serializing it...

     <HttpGet>
            <Route("")>
            Public Function GetAllHeroes() As CustomJsonStringResult
    
               'populate a list of heros however you want 
                dim ag as  new  HeroAg()
                Dim l As List(Of TOHbos.Hero) = ag.Items
    
    
    
                'This should be shoved down into a reusable class...ListToJSON
                Dim sb As New StringBuilder("")
                Dim first As Boolean = True
                sb.Append("[")
                For Each h As TOHbos.Hero In l
    
                    If first = True Then
                        first = False
                    Else
                        sb.Append(", ")
                    End If
                     'decide which fields to include and generate a JSON document
                     h.decideWhichFieldsGetSerializedForThisView()
                    sb.Append(h.JSON)
                Next
                sb.Append("]")
    
                Return JsonStringResultExtension.JSONString(Me, sb.ToString(), HttpStatusCode.OK)
    
            End Function
    

    The next problem I ran into was that I was getting this error from the angular app in the browser console

     Http failure during parsing for localhost:4300/api/heroes
    

    Using jsonLint.com ,i was able to figure out that my JSON was not quite correct. I wasn't putting the key names in double ticks, and i was putting values in single ticks. While this may be acceptable JSON, jsonLint complains, and I guess so does Angular rxjs

    0 讨论(0)
  • 2020-12-10 16:37

    Create custom implementation. The framework is extensible via the IHttpActionResult.

    The following creates a custom result and extension method...

    public static class JsonStringResultExtension {
       public static CustomJsonStringResult JsonString(this ApiController controller, string jsonContent, HttpStatusCode statusCode = HttpStatusCode.OK) {
            var result = new CustomJsonStringResult(controller.Request, statusCode, jsonContent);
            return result;
        }
    
        public class CustomJsonStringResult : IHttpActionResult {
            private string json;
            private HttpStatusCode statusCode;
            private HttpRequestMessage request;
    
            public CustomJsonStringResult(HttpRequestMessage httpRequestMessage, HttpStatusCode statusCode = HttpStatusCode.OK, string json = "") {
                this.request = httpRequestMessage;
                this.json = json;
                this.statusCode = statusCode;
            }
    
            public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) {
                return Task.FromResult(Execute());
            }
    
            private HttpResponseMessage Execute() {
                var response = request.CreateResponse(statusCode);
                response.Content = new StringContent(json, Encoding.UTF8, "application/json");
                return response;
            }
        }
    }
    

    ...that can then be applied to ApiController derived classes. Greatly simplifying previous calls to

    return this.JsonString(jsonUtilizadores); //defaults to 200 OK
    

    or with desired HTTP status code

    return this.JsonString(jsonUtilizadores, HttpStatusCode.BadRequest);
    
    0 讨论(0)
提交回复
热议问题