Ok() vs Ok(null) - asp.net-mvc

What's the difference between Ok() vs Ok(null)?
The Ok(null) returns status code 204 with no body but header, therefore we have to change our code in this way:
[HttpGet]
public IActionResult GetTest(string test)
{
MyClass result = GetMyClass(test)
if(result == null) return Ok();
return Ok(result);
}

I suggest you use NoContent (HttpStatus 204).
That means the request successfully executed, but didn't return a value or object, and it's an official status code for this purpose
See the following example:
[HttpGet()]
public IActionResult GetTest(string test)
{
var result = GetMyClass(test)
return result != null
? Ok(result)
: NoContent();
}
Hope you'll find this useful.

Official documentation says
Ok() Creates a OkResult object that produces an empty Status200OK
response.
Ok(Object) Creates an OkObjectResult object that produces an
Status200OK response.
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controllerbase.ok?view=aspnetcore-2.1
When you pass null , it will creates an OkObjectResult of null , So you are getting 204 status code.

HTTP status code 204 is a valid "success" result that lets the recipient know that there is no content. This actually seems rather appropriate, but still it may not be supported everywhere.
Either your could 'teach' (or program) your recipient to handle it correctly, or you need to apply your workaround.
If you need this workaround in multiple places then you could use a base class to overload Ok() (or more precisely, redefine it) to always do what you need, like this:
public abstract class MyBaseApiController : ApiController
{
public new IHttpActionResult Ok<T>(T content)
{
if (content == null) return base.Ok();
return base.Ok(content);
}
}

Related

how can i return 404 http status from dropwizard

new to dropwizard! is there anyway that I can manually return different http status codes from the apis? basically something similar to this!
#GET
#Timed
public MyObject getMyObject(#QueryParam("id") Optional<String> id) {
MyObj myObj = myDao.getMyObject(id)
if (myObj == null) {
//return status.NOT_FOUND; // or something similar
// or more probably
// getResponseObjectFromSomewhere.setStatus(mystatus)
}
return myObj;
}
It's as simple as throwing a WebApplicationException.
#GET
#Timed
public MyObject getMyObject(#QueryParam("id") Optional<String> id) {
MyObject myObj = myDao.getMyObject(id)
if (myObj == null) {
throw new WebApplicationException(404);
}
return myObj;
}
As you get further along you may want to put together custom exceptions which you can read more about here.
I would recommend using the JAX-RS Response object instead of returning your actual domain object in the response. It serves as an excellent standard for including metadata with your response object and provides a nice builder for handling status codes, headers, customer content types, etc.
//import javax.ws.rs.core.Response above
#GET
#Timed
public Response getMyObject(#QueryParam("id") Optional<String> id) {
MyObject myObj = myDao.getMyObject(id)
if (myObj == null) {
//you can also provide messaging or other metadata if it is useful
return Response.status(Response.Status.NOT_FOUND).build()
}
return Response.ok(myObj).build();
}
The simplest way is to return an Optional<MyObject>. Dropwizard will automatically throw a 404 when your result is Optional.absent() or Optional.empty() if you use the dropwizard-java8 bundle.
Just do:
#GET
#Timed
public Optional<MyObject> getMyObject(#QueryParam("id") Optional<String> id) {
Optional<MyObject> myObjOptional = myDao.getMyObject(id)
return myObjOptional;
}
Obviously you need to update your DAO according by returning Optional.fromNullable(get(id)) for Guava or Optional.ofNullable(get(id)) for Java8 bundle.
There is no need to play around with custom Response objects unless you want to return a custom status code outside of 200 and 404

Change returned object value after action execution in Web API

In ASP.Net Web API, the action returned object will be converted to XML or JSON automatically - is there a way to add additional process to the returned object before it gets converted?
What I want to achieve is to wrap returned object into a generic APIResponse (another class) type which has an object Property called Data that will be assigned with whatever the original returned object is.
for example:
public Books[] FindBooks(string keyword)
{
..
return new Books[] {new Book(){Name="ASP.NET"}};
}
This will return JSON of book array by default, however I want to wrap the result into an object called APIResponse, so the returned object becomes:
new APIResponse(){
Data = //the Book[] array return from the action method
}
By doing this, I will be able to keep the freedom of returning normal business objects in Web API however always return the same object type in JSON format when the front-end Ajax requests.
I believe it can be done in a way however I'm not familiar with the Web API life cycle, can any way give some guide on this?
Thank you.
I fixed it by creating a custom MediaTypeFormatter however simply inheriting from JSON formatter which have already got all what I need, here is the very simple code I added, which resolved all issues I have!!!
public class APIResponseMediaFomatter : JsonMediaTypeFormatter
{
public override Task WriteToStreamAsync(Type type, object value, System.IO.Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
{
ResponseMessage wrappedValue = null;
if (type != typeof(ResponseMessage) || (value != null && value.GetType() != typeof(ResponseMessage)))
wrappedValue = new ResponseMessage(value);
return base.WriteToStreamAsync(typeof(ResponseMessage), wrappedValue, writeStream, content, transportContext);
}
}
Cheers!
Interestingly, Web API already works exactly how you describe. It already has generic request and response classes that can hold your object payload. Just do the following,
public HttpResponseMessage FindBooks(string keyword)
{
...
var books = new Books[] {new Book(){Name="ASP.NET"}};
var content = new ObjectContent<Book[]>(books,new JsonMediaTypeFormatter());
return new HttpResponseMessage { Content = content);
}
There is no need to re-invent your own generic response object that can hold metadata and data, HTTP has already done that for you.
Why dont you return your wrapper object APIResponse from WebAPI
public APIResponse FindBooks(string keyword)
{
var books = new Books[] {new Book(){Name="ASP.NET"}};
return new APIResponse {Data= books };
}
Just use an action filter, and modify the response content inside it:
public class ApiResponseWrapperActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
// Check that the response is an object content
var objectContent = actionExecutedContext.Response.Content as ObjectContent;
if (objectContent != null)
{
// Wrap the returned value in the ApiResponse
objectContent.Value = new ApiResponse() { Data = objectContent.Value };
}
}
Apply the filter to the whole API (in global config) or a whole controller (attribute applied to the controller class) or to the desired methods (attribute in each method).
If you're returning something that it's not an object (a custom response) it will skip the wrapping.

How should I design MVC to conditionally return JSON or pretty HTML?

I have data that will either be consumed by a human or a web service. The REST url is:
http://host.com/movies/detail/1
http://host.com/movies/detail/The Shawshank Redemption (1994)
What convention should I follow to conditionally return JSON or HTML? Should I add a parameter such as "?json" or should I look at the client headers,.. some variation of both?
If I do a variation of both, if a conflict is found which takes precedent?
Check whether the Request is Ajax. You may use the Request.IsAjaxRequest() method which returns true/false.
public ActionResult details(string id)
{
var movieViewModel=movieService.GetMovieDetails(id);
If(Request.IsAjaxRequest())
{
// return Json now
return Json(movieViewModel,JsonRequestBehavior.AllowGet);
}
// Not an ajax request, Let's return Normal View (HTML)
return View(movieViewModel);
}
UNIT TESTING ASPECT : Request.IsAjaxRequest() is not unit test friendly! So if you are worried about unit tests, You can write your IsAjaxRequest property and put in your basecontroller class and use it.
public class BaseController : Controller
{
protected bool IsAjaxRequest()
{
//Using this method instead of Request.IsAjaxRequest() because
//IsAjaxRequest is static and not mockable ! (not unit test friendly)
var isAjax = Request.Headers["X-Requested-With"];
if (isAjax != null && isAjax.ToUpper() == "XMLHTTPREQUEST")
return true;
return false;
}
}
Now inherit your controller from this BaseController.
public class HomeController : BaseController
{
public ActionResult details(string id)
{
var movieViewModel=movieService.GetMovieDetails(id);
If(IsAjaxRequest)
{
// return Json now
return Json(movieViewModel,JsonRequestBehavior.AllowGet);
}
// Not an ajax request, Let's return Normal View (HTML)
return View(movieViewModel);
}
}
You could also use:
[HttpGet]
public ActionResult() {
// Return HTML
}
[HttpPost]
public ActionResult() {
// Assuming Ajax is of the type post
}
Just another solution if all your Ajax is using post.
I prefer using a parameter explicit in the URL because that way building REST petitions is easy for developers, self explanatory and with no surprises (do you have to guess default format? or see "difficult" to see HTTP headers). You decide:
if you have many options for formats you can use format=json
you can go with json parameter, but it is not pretty because you have to pair it with a value json=true, json=1. Besides you can set json=1&xml=1&html=1, harder to handle.
the twitter way is to emulate an extension such as call.json or call.xml (e.g. https://dev.twitter.com/docs/api/1.1/get/statuses/user_timeline)
I recommend don't tie together a kind of petition and a format. Let your API clients decide, ajax-json is commonly used, but not all develepers use it that way. Maybe I am writing a terminal application with no ajax, maybe I want to do a wget to your API and still get json.

ASP.NET MVC - Proper way to handle ajax actions with no return object

I have a controller action that does some work in the database and then exits when it's finished. This action is being called via jQuery's ajax function with the dataType set to 'json'.
If I set the return type of the action to void, everything will function just fine except Firefox will show an error in the console that says: "no element found".
It makes sense that Firefox would throw this error if it was expecting XML to come back. However, even when I change the dataType property of the ajax call to "text", I still receive the error. In order to get rid of the error with the return type void, I would have to set the Response's ContentType to "text/html". Or I could set the return type to JsonResult and return a new [empty] JsonResult object.
I'm sure there are several ways I can make this error go away, but I wanted to know the proper way to handle actions with no return values being called via ajax.
If it matters, I'm also using the async controller action pattern.
public void DoSomethingAsync(SomeJsonObjectForModelBinding model)
{
// do some database things
}
public void DoSomethingCompleted()
{
// nothing to do...
// what should my return type be?
// do I need to set the content type here?
}
I know this doesn't exactly answer your question, but I would argue that you should always have a return value coming back from an AJAX or web service call. Even if only to tell you that the operation was successful, or otherwise return the error (message) back to you.
I often define a class like this:
public class JsonResultData
{
private bool _success = true;
public bool Success
{
get { return _success; }
set { _success = value; }
}
public object Value { get; set; }
public List<string> Errors { get; set; }
public JsonResultData()
{
this.Errors = new List<string>();
}
}
And then use it to return data or any other call meta data in the JsonResultData wrapper like so:
return new JsonResult {
Data = new JsonResultData { Value = returnValue, Success = true }
};
I can't comment because of my reputation but I still wanted to contribute to clear the confusion in Kon's answer.
In an application I caught all exceptions within an ActionMethod, set an HttpStatusCode and added an error message to the response. I extracted the message in the Ajax error function and showed it to the user.
Everything worked out fine until the application got put on the staging server, who had some kind of settings that did not allow a return message within an erroneous response. Instead some standard Html was transmitted resulting in a JS error processing the response.
In the end I had to rewrite all my exception handling returning my application errors as successful Ajax call (which it actually is) and then differ within the Ajax success function, just the way it should be.
You should not mix system-level and application-level feedback. You may not be able to control the system-level feedback the way your application needs.

How to get and set http headers in an Action, the testable way

I have an action that returns either a FileContentResult or a NotModifiedResult, which is a custom result type that returns HTTP 304 to indicate that the requested resource has not been modified, like this:
[ReplaceMissingPicture(Picture = "~/Content/Images/nothumbnail.png", MimeType = "image/png")]
public ActionResult Thumbnail(int id)
{
var item = Service.GetItem(id);
var requestTag = Request.Headers["If-None-Match"] ?? string.Empty;
var tag = Convert.ToBase64String(item.Version.ToArray());
if (tag == requestTag)
{
return new NotModifiedResult();
}
if (item.Thumbnail != null)
{
var thumbnail = item.Thumbnail.ToArray();
var mime = item.PictureMime;
Response.AppendHeader("ETag", tag);
return File(thumbnail, mime);
}
else
{
return null;
}
}
This action needs to access the Response object, which is of course not present during testing, so that makes this action untestable. I could add conditional statements around it, so that it runs during testing, but then I can't test for the headers being set correctly.
What would be a solution to this problem?
FYI, the ReplaceMissingPicture filter returns a specific resource in case null was returned from this action, to keep the MapPath() call out of the controller for the very same reason.
The first step would be to create an interface which simplifies the services you need:-
public interface IHeaders
{
public string GetRequestHeader(string headerName);
public void AppendResponseHeader(string headerName, string headerValue);
}
Now create a default implementation:-
public Headers : IHeaders
{
public string GetRequestHeader(string headerName)
{
return HttpContext.Current.Request[headerName];
}
public void AppendResponseHeader(string headerName, string headerValue)
{
HttpContext.Current.Response.AppendHeader(headerName, headerValue);
}
}
Now add a new field to your Controller:-
private IHeaders myHeadersService;
add new constructor to you controller:-
public MyController(IHeaders headersService)
{
myHeadersService = headersService;
}
modify or add the default constructor:-
public MyController()
{
myHeadersService = new Headers();
}
now in your Action code use myHeadersService instead of the Response and Request objects.
In your tests create your own implementation of the IHeaders interface to emulate/test the Action code and pass that implementation when constructing the Controller.
How about creating a subclass of FileResult--say ETagFileResult--that in its ExecuteResult() method sets the ETag header, and then defaults to the base class implementation? You can test that class with a mocked context (as you presumably are with your NotModifiedResult) to be sure that it's doing the right thing. And remove the entire complication from the testing of the controller.
Failing that, it's possible to set a mocked context on the controller in your test (after instantiating the class, before calling the action method). See this question, for instance. But that seems like more work.
(Also, by the way, it looks like you're quoting the tag value twice there: once when tag is set, and once more when you actually set the header....)

Resources