.Net Core API Reject Incomplete JSON Request - asp.net-mvc

I need to reject the Request if the body has incomplete JSON.
I have a .NetCore API, which has a lot of properties. That API does a lot of operations and will be getting a lot of requests, so want to reject if the JSON is incomplete beforehand.
If, I have below AssignmentDetail class,
public class AssignmentDetail
{
public string Name { get; set; }
public string Address { get; set; }
}
Complete JSON Example
{
"Name":"dfsdfsdf",
"Address":"essdfsdfsd",
}
Incomplete JSON Example
{
"Name":"dfsdfsdf"
}
I have some approaches but need something which can be done through in startup, but just for that action.
Using custom Serializable for that AssignmentDetail model (Don't want this approach)
Creating a function to validate the Incomplete JSON like validateIncompleteJSON() (Don't want this approach)
Something in ConfigureServices(IServiceCollection services) but just for that controller action
[HttpPost]
[Route("ProcessAssignment")]
public async Task<AssignmentResponseModel> ProcessAssignment(AssignmentDetail model)
{
var response = new AssignmentResponseModel();
try
{
//can call function here to check the incomlpete JSON
//validateIncompleteJSON();
//var result = await _mediator.Send(queryDetails);
//response = result.Response;
}
catch (Exception ex)
{
throw exception;
}
return response;
}
I don't want to use the Serializable way, as the class is too big and will have to handle all the properties.

Data Annotations add Required Attribute to property
Codes of Model
public class AssignmentDetail
{
[Required]
public string Name { get; set; }
[Required]
public string Address { get; set; }
}
Codes of Controller
[HttpPost]
[Route("/ProcessAssignment")]
public IActionResult ProcessAssignment(AssignmentDetail model)
{
//var response = new AssignmentResponseModel();
//try
//{
// //can call function here to check the incomlpete JSON
// //validateIncompleteJSON();
// //var result = await _mediator.Send(queryDetails);
// //response = result.Response;
//}
//catch (Exception ex)
//{
// throw exception;
//}
//return response;
if (ModelState.IsValid)
{
return Ok("Success");
}
return BadRequest();
}

Related

Wrapping IHttpActionResult - Generic solution

I'd like to wrap IHttpActionResult because I need some extra data to be consumed by the client app.
My first approach was to create and return simple DTO, wrapping result object if succeeded:
Response DTO:
public class Response<T>
{
public string ErrorMessage { get; set; }
public bool Success { get; set; }
public string CodeStatus { get; set; }
public T Result { get; set; }
public Response(bool isSuccess, [Optional] T result, [Optional] string codeStatus, [Optional] string errorMessage)
{
Success = isSuccess;
Result = result;
CodeStatus = codeStatus;
ErrorMessage = errorMessage;
}
}
Controller:
public IHttpActionResult Get(int id)
{
return BadRequest(new Response<MyObjectClass>(false, null,"Invalid Id",400));
...
return Ok(new Response<MyObjectClass>(true, result);
}
I've found it very ineffective way to deal with wrapping. I dont find it very elegant way. I've tried to figured out some generic solution and ended up with the following:
Example Controller Action:
public IHttpActionResult GetById(int id)
{
var result = _someService.Get(id);
if (result == null)
return NotFound().WithError("Invalid Id");
return Ok().WithSuccess(result);
}
This still returns Response DTO.
I've wrapped IHttpActionResult to deal with creating Response DTO:
public class HttpActionResult : IHttpActionResult
{
private readonly string _errorMessage;
private readonly IHttpActionResult _innerResult;
private readonly object _result;
private readonly bool _isSuccess;
public HttpActionResult(IHttpActionResult inner, bool isSuccess, object result,string errorMessage)
{
_errorMessage = errorMessage;
_innerResult = inner;
_result = result;
_isSuccess = isSuccess;
}
public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
HttpResponseMessage response = await _innerResult.ExecuteAsync(cancellationToken);
response.Content = new ObjectContent(typeof(Response), new Response(_isSuccess, _result, ((int)response.StatusCode).ToString(), _errorMessage), new JsonMediaTypeFormatter());
return response;
}
}
Finally I've added extension methods to IHttpActionResult to easier use in controller:
public static class IHttpActionResultExtensions
{
public static IHttpActionResult WithSuccess(this IHttpActionResult inner, object result = null, string message = null)
{
return new HttpActionResult(inner, true, result, message);
}
public static IHttpActionResult WithError(this IHttpActionResult inner, string message = null)
{
return new HttpActionResult(inner, false,null, message);
}
}
What are the alternatives to deal with wrapping http messages in API Controller?
What weak points do you see in my solution?
BTW, I see some weak points on your approach:
WebAPI is meant to be used to create RESTful Web services. Why are you trying to provide another layer of status and other details? HTTP is rich enough to cover these requirements. For example, you can use standard status codes and a subcode as follows: 500.1, 500.2.
Success or failure is easier to express with HTTP status codes. 2XX range for successful operations, and for an unsuccessful one you can use, for example, 400 (Bad Request). 401 for an unauthorized access... 500 for a server failure...
WebAPI already provides ModelState to let the framework build a response object. Use it and try to don't re-invent the wheel.
Again, keep it simple. Response entity goes on the response body. Success or failure is expressed by status codes. Details about a bad request are added to the ModelState dictionary. An error message should be set to the response's ReasonPhrase.
IHttpActionResult implementations are meant to transform your domain result into an HTTP response. That is, you're in the right track excepting when you try to return your response object as is. My advise is you should use your IHttpActionResult to set every detail on your own response object to standard HTTP semantics, and notify errors using ModelState out-of-the-box approach which works well.
Avoid IHttpActionResult and use HttpResponseException with Business Entity as result type. As in your solution, you cannot write statically typed test cases.
For example,
protected void ThrowHttpError(HttpStatusCode statusCode, string message)
{
throw new HttpResponseException(
new HttpResponseMessage(statusCode) {
ReasonPhrase = message,
// HTTP 2.0 ignores ReasonPhrase
// so we send ReasonPhrase again in the Content
Content = new StringContent(message)
});
}
// some generic option...
protected void ThrowHttpError<T>(HttpStatusCode statusCode, T content)
where T:class
{
throw new HttpResponseException(
new HttpResponseMessage(statusCode) {
ReasonPhrase = "Error",
Content = JsonConvert.Serialize(content)
});
}
Your methods,
public async Task<Product> Get(long id){
var product = await context.Products
.FirstOrDefaultAsync( x=> x.ProductID == id);
if(product==null){
ThrowHttpError(HttpStatusCode.NotFound,
$"Product not found for {id}");
}
if(product.RequiresValidation){
// generic version....
ThrowHttpError(HttpStatusCode.Conflict,
new Product{
ProductID = product.ProductID,
ValidationRequestCode = product.ValidationRequestCode
});
}
return product;
}
For further more, you can customise method ThrowHttpError to suite your needs. Best part is, it is still testable.

How to pass void return type into Json

Please help me. How can I pass message from void to return type?
WebApi.cs
public void DeleteById(int id)
{
string meassga = "";
try
{
objser.DeleteBYId(id);
}
catch (Exception ex)
{
meassga = "" + ex;
}
}
Mvc.cs
public JsonResult DeleteById(int id)
{
string meassga = "";
ss.DeleteBYId(id);
return Json ( meassga, JsonRequestBehavior.AllowGet );
}
Here I'm passing data from mvc to webApi & I want to show error details from api controller to mvc json control
You can't. You have a couple of options, change your webapi method to return a string, or throw the exception like this:
public void DeleteById(int id)
{
try
{
objser.DeleteBYId(id);
}
catch (Exception ex)
{
throw new Exception($"Exception in DeleteById({id}) - {ex.Message}", ex);
}
}
then in the controller:
public JsonResult DeleteById(int id)
{
try
{
ss.DeleteBYId(id);
return Json ("Deleted successfully", JsonRequestBehaviour.AllowGet);
}
catch(Exception ex)
{
return Json ( ex.Message, JsonRequestBehavior.AllowGet );
}
}
... similar. Without throwing exceptions another common strategy is to create a return class like this:
public class ApiResult<T>
{
public string Message { get; set; }
public T Result { get; set; }
public bool Success { get; set; }
}
And implement that as the thing all your api calls return.

How to return callback json from VC Controller?

I am using 3rd party Kendo UI in client side. it expects data from server in below format.
callback([{"TaskID":4,"OwnerID":2,"Title":"Bowling}])
I am having below code in server side
public JsonResult GetAllAppointments()
{
IEnumerable<AppointmentModel> appointmentCollection = app_repository.GetAll();
if (appointmentCollection == null)
{
return Json(appointmentCollection, JsonRequestBehavior.AllowGet);
}
return Json(appointmentCollection, JsonRequestBehavior.AllowGet);
}
But this returns just json, how to add "callback" to it?
Ideally you should get an interceptor at client side which should let you modify your response format..but since you have not provided any further details I won't be able to comment on that.
You can change your return type fron JsonResult to string and manually serialize your response.You can use Json.NET to serialize your response.
Here is a NuGet LINK for the same.
public class AppointmentModel
{
public string TaskID { get; set; }
public string OwnerID { get; set; }
public string Title { get; set; }
}
public string GetAllAppointments()
{
string responseFormat = #"callback({0})";
IEnumerable<AppointmentModel> appointmentCollection = getDummyAppoitments();
if (appointmentCollection != null)
{
string json_string = Newtonsoft.Json.JsonConvert.SerializeObject(appointmentCollection);
return string.Format(responseFormat, json_string);;
}
//no items were present so sending empty response;
return string.Format(responseFormat, "[]");;
}
private IEnumerable<AppointmentModel> getDummyAppoitments()
{
return new List<AppointmentModel>() {
new AppointmentModel()
{
TaskID = "4",
OwnerID = "2",
Title = "Bowling"
}
};
}
Check out response.
Cheers!!

ASP.NET MVC Web API : Posting a list of objects

I'm trying to post a list of objects from my winforms application to my asp.net mvc 4 website. I've tested posting one object, and it works, but does not work for the list. It returns a 500 (Internal Server Error). Here is my code:
ASP.NET MVC Web API
public class PostTraceController : ApiController
{
public HttpResponseMessage Post(List<WebTrace> list)
{
try
{
// Some code
return Request.CreateResponse(HttpStatusCode.Created);
}
catch (Exception ex)
{
HttpContext.Current.Trace.Write("exception", ex.Message);
return Request.CreateErrorResponse(HttpStatusCode.ServiceUnavailable, ex);
}
}
public HttpResponseMessage Post(WebTrace item)
{
try
{
// Some code
return Request.CreateResponse(HttpStatusCode.Created);
}
catch (Exception ex)
{
HttpContext.Current.Trace.Write("exception", ex.Message);
return Request.CreateErrorResponse(HttpStatusCode.ServiceUnavailable, ex);
}
}
}
Win forms application
public class BaseSender
{
public BaseSender()
{
Client = new HttpClient
{
BaseAddress = new Uri(#"http://localhost/mywebsite/")
};
Client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
public string UserCode { get; set; }
protected readonly HttpClient Client;
public HttpResponseMessage PostAsJsonAsync(string requestUri, object value)
{
var response = Client.PostAsJsonAsync(requestUri, value).Result;
response.EnsureSuccessStatusCode();
return response;
}
}
public class WebTraceSender : BaseSender
{
private const string requestUri = "api/posttrace";
public bool Post(List<ArchiveCptTrace> list)
{
try
{
var listWebTrace = new List<WebTrace>();
foreach (var item in list)
{
listWebTrace.Add(new WebTrace
{
DateStart = item.DatePreparation,
DateEnd = item.DateCloture,
UserStart = item.UserPreparation.UserName,
UserEnd = item.UserCloture.UserName,
AmountStart = item.MontantPreparation,
AmountEnd = item.MontantCloture,
TheoricAmountEnd = item.MontantTheorique,
Difference = item.Ecart,
UserCode = UserCode
});
}
var responce = PostAsJsonAsync(requestUri, listWebTrace);
return responce.IsSuccessStatusCode;
}
catch (Exception e)
{
// TODO : Trace the exception
return false;
}
}
}
EDIT :
I've found out the scenario of the error, which is having two methods in my api controller, even thought they have different signature. If I comment one method, the post work fine (item or a list). Any ideas ?
The methods may have different signatures, but Web API can't tell the difference between them without inspecting the body, which it won't do for performance reasons.
You could do two things - either create a new class which just holds a list of WebTrace objects, and put that in a different API controller, or you could map a custom route to one of your existing methods. You could do that with ActionName attribute, however, I would probably take the first approach.

How does NerdDinner's AddModelErrors work?

I'm going through the NerDinner free tutorial
http://nerddinnerbook.s3.amazonaws.com/Intro.htm
I got to somewhere in Step 5 where it says to make the code cleaner we can create an extension method. I look at the completed code and it has this to use the extension method:
catch
{
ModelState.AddModelErrors(dinner.GetRuleViolations());
return View(new DinnerFormViewModel(dinner));
}
And then this as the extension method's definition.
namespace NerdDinner.Helpers {
public static class ModelStateHelpers {
public static void AddModelErrors(this ModelStateDictionary modelState, IEnumerable<RuleViolation> errors) {
foreach (RuleViolation issue in errors) {
modelState.AddModelError(issue.PropertyName, issue.ErrorMessage);
}
}
}
}
I try to follow what the tutorial says combined with what the code contains but receive the expected error that there is no AddModelErrors method that accepts only 1 argument.
I'm obviously missing something very important here. What is it?
You need to include the helpers reference;
using NerdDinner.Helpers;
and
using NerdDinner.Models;
Then check for valid and add the errors;
if (!dinner.IsValid)
{
ModelState.AddModelErrors(dinner.GetRuleViolations());
return View(dinner);
}
You must also have a partial class for your dinner;
public partial class Dinner
{
public bool IsValid
{
get { return (GetRuleViolations().Count() == 0); }
}
public IEnumerable<RuleViolation> GetRuleViolations()
{
if (String.IsNullOrEmpty( SomeField ))
yield return new RuleViolation("Field value text is required", "SomeField");
}
partial void OnValidate(ChangeAction action)
{
if (!IsValid)
throw new ApplicationException("Rule violations prevent saving");
}
}
Don't forget the RuleViolation class;
public class RuleViolation
{
public string ErrorMessage { get; private set; }
public string PropertyName { get; private set; }
public RuleViolation(string errorMessage)
{
ErrorMessage = errorMessage;
}
public RuleViolation(string errorMessage, string propertyName)
{
ErrorMessage = errorMessage;
PropertyName = propertyName;
}
}
If you are receiving the same error message as this poster:
"'System.Web.Mvc.ModelStateDictionary' does not contain a definition for 'AddModelErrors' and no extension method 'AddModelErrors' accepting a first argument of type 'System.Web.Mvc.ModelStateDictionary' could be found (are you missing a using directive or an assembly reference?)"
You may be having this problem:
http://p2p.wrox.com/book-professional-asp-net-mvc-1-0-isbn-978-0-470-38461-9/74321-addmodalerrors-allcountries-page-87-view-data-dictionary.html#post248356

Resources