问题
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 HttpResponseMessage
explicitly and setting its Content
property to the json string which the service receives:
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StringContent(jsonUtilizadores, Encoding.UTF8, "application/json");
return response;
Now, is there a better way of doing this with the new IHttpActionResult
? Using the Content
or Ok
method ends up wrapping the json string with quotes, which is not what I want.
Any feedback?
回答1:
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);
回答2:
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; }
}
回答3:
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.
回答4:
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
来源:https://stackoverflow.com/questions/40833162/web-api-recommended-way-to-return-json-string