MVC4 add overload to a method - asp.net-mvc-4

As many of you know MVC4 has some great new features, I am struggling with the ContextDependentView trying to add an overload to it. I get an error saying no overload method for ContextDependentView takes 1 argument . My original code that was working was this
// This worked fine
return View(new ModelSample { getInfo= info, Retrieving= Retrieve })
// This is now what I have tried to do that doesn't work
return ContextDependentView(new ModelSample { getInfo= info, Retrieving= Retrieve })
//This is the method for ContextDependentView()
private ActionResult ContextDependentView()
{
string actionName = ControllerContext.RouteData.GetRequiredString("action");
if (Request.QueryString["content"] != null)
{
ViewBag.FormAction = "Json" + actionName;
return PartialView();
}
else
{
ViewBag.FormAction = actionName;
return View();
}
}
I obviously see that there are no overloads but how can I add an overload to the ContextDependentView method to accept my model like return View()..thanks

Add this overload to your controller:
private ActionResult ContextDependentView(SampleModel model)
{
string actionName = ControllerContext.RouteData.GetRequiredString("action");
if (Request.QueryString["content"] != null)
{
ViewBag.FormAction = "Json" + actionName;
return PartialView();
}
else
{
ViewBag.FormAction = actionName;
return View();
}
}
That should work...

Related

What is the best possible way to send custom error responses in .net core web api

I'm making a .net Core WebApi using .Net Core 2.2. The API is ready but the failure message and response is where I'm stuck at.
Right now, I'm getting respose like below
json
{
"empId":1999,
"empName":"Conroy, Deborah",
"enrollmentStatus":true,
"primaryFingerprintScore":65,
"secondaryFingerprintScore":60,
"primaryFingerprint":null,
"secondaryFingerprint":null,
"primaryFingerprintType":null,
"secondaryFingerprintType":null}
}
I created a json formatter class and wrote the below code
public class SuperJsonOutputFormatter : JsonOutputFormatter
{
public SuperJsonOutputFormatter(
JsonSerializerSettings serializerSettings,
ArrayPool<char> charPool) : base(serializerSettings, charPool)
{
}
public override async Task WriteResponseBodyAsync(
OutputFormatterWriteContext context,
Encoding selectedEncoding)
{
if (context == null)
throw new ArgumentNullException(nameof(context));
if (selectedEncoding == null)
throw new ArgumentNullException(nameof(selectedEncoding));
using (TextWriter writer =
context.WriterFactory(
context.HttpContext.Response.Body,
selectedEncoding))
{
var rewrittenValue = new
{
resultCode = context.HttpContext.Response.StatusCode,
resultMessage =
((HttpStatusCode)context.HttpContext.Response.StatusCode)
.ToString(),
result = context.Object
};
this.WriteObject(writer, rewrittenValue);
await writer.FlushAsync();
}
}
I expect all the error codes to be sent as generic error messages like the JSON below.
FOR STATUS OKAY:
{
"status" : True,
"error" : null,
"data" : {
{
"empId":1999,
"empName":"Conroy, Deborah",
"enrollmentStatus":true,
"primaryFingerprintScore":65,
"secondaryFingerprintScore":60,
"primaryFingerprint":null,
"secondaryFingerprint":null,
"primaryFingerprintType":null,
"secondaryFingerprintType":null}
}
}
}
FOR OTHER STATUS LIKE 404, 500, 400, 204
{
"status" : False,
"error" : {
"error code" : 404,
"error description" : Not Found
},
"data" : null
}
I expect all the error codes to be sent as generic error messages like the JSON below
You're almost there. What you need to do is enabling your SuperJsonOutputFormatter.
A Little Change to Your Formatter
Firstly, your formatter didn't return a json with the same schema as you want. So I create a dummy class to hold the information for error code and error description:
public class ErrorDescription{
public ErrorDescription(HttpStatusCode statusCode)
{
this.Code = (int)statusCode;
this.Description = statusCode.ToString();
}
[JsonProperty("error code")]
public int Code {get;set;}
[JsonProperty("error description")]
public string Description {get;set;}
}
And change your WriteResponseBodyAsync() method as below:
...
using (TextWriter writer = context.WriterFactory(context.HttpContext.Response.Body, selectedEncoding)) {
var statusCode = context.HttpContext.Response.StatusCode;
var rewrittenValue = new {
status = IsSucceeded(statusCode),
error = IsSucceeded(statusCode) ? null : new ErrorDescription((HttpStatusCode)statusCode),
data = context.Object,
};
this.WriteObject(writer, rewrittenValue);
await writer.FlushAsync();
}
Here the IsSucceeded(statusCode) is a simple helper method that you can custom as you need:
private bool IsSucceeded(int statusCode){
// I don't think 204 indicates that's an error.
// However, you could comment out it if you like
if(statusCode >= 400 /* || statusCode==204 */ ) { return false; }
return true;
}
Enable your Formatter
Secondly, to enable your custom Formatter, you have two approaches: One way is to register it as an global Formatter, the other way is to enable it for particular Controller or Action. Personally, I believe the 2nd way is better. So I create a Action Filter to enable your formatter.
Here's an implementation of the Filter that enables your custom formatter dynamically:
public class SuperJsonOutputFormatterFilter : IAsyncActionFilter{
private readonly SuperJsonOutputFormatter _formatter;
// inject your SuperJsonOutputFormatter service
public SuperJsonOutputFormatterFilter(SuperJsonOutputFormatter formatter){
this._formatter = formatter;
}
// a helper method that provides an ObjectResult wrapper over the raw object
private ObjectResult WrapObjectResult(ActionExecutedContext context, object obj){
var wrapper = new ObjectResult(obj);
wrapper.Formatters.Add(this._formatter);
context.Result= wrapper;
return wrapper;
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
ActionExecutedContext resultContext = await next();
// in case we get a 500
if(resultContext.Exception != null && ! resultContext.ExceptionHandled){
var ewrapper = this.WrapObjectResult(resultContext, new {});
ewrapper.StatusCode = (int) HttpStatusCode.InternalServerError;
resultContext.ExceptionHandled = true;
return;
}
else {
switch(resultContext.Result){
case BadRequestObjectResult b : // 400 with an object
var bwrapper=this.WrapObjectResult(resultContext,b.Value);
bwrapper.StatusCode = b.StatusCode;
break;
case NotFoundObjectResult n : // 404 with an object
var nwrapper=this.WrapObjectResult(resultContext,n.Value);
nwrapper.StatusCode = n.StatusCode;
break;
case ObjectResult o : // plain object
this.WrapObjectResult(resultContext,o.Value);
break;
case JsonResult j : // plain json
this.WrapObjectResult(resultContext,j.Value);
break;
case StatusCodeResult s: // other statusCodeResult(including NotFound,NoContent,...), you might want to custom this case
var swrapper = this.WrapObjectResult(resultContext, new {});
swrapper.StatusCode = s.StatusCode;
break;
}
}
}
}
And don't forget to register your formatter as a service :
services.AddScoped<SuperJsonOutputFormatter>();
Finally, when you want to enable your formatter, just add a [TypeFilter(typeof(SuperJsonOutputFormatterFilter))] annotation for the controller or action.
Demo
Let's create an action method for Test:
[TypeFilter(typeof(SuperJsonOutputFormatterFilter))]
public IActionResult Test(int status)
{
// test json result(200)
if(status == 200){ return Json(new { Id = 1, }); }
// test 400 object result
else if(status == 400){ return BadRequest( new {}); }
// test 404 object result
else if(status == 404){ return NotFound(new { Id = 1, }); }
// test exception
else if(status == 500){ throw new Exception("unexpected exception"); }
// test status code result
else if(status == 204){ return new StatusCodeResult(204); }
// test normal object result(200)
var raw = new ObjectResult(new XModel{
empId=1999,
empName = "Conroy, Deborah",
enrollmentStatus=true,
primaryFingerprintScore=65,
secondaryFingerprintScore=60,
primaryFingerprint = null,
secondaryFingerprint= null,
primaryFingerprintType=null,
secondaryFingerprintType=null
});
return raw;
}
Screenshot:

Custom serializer not working in Web API 2 for oData 4 when the URL contains $select

I implemented a custom serializer by inheriting ODataEntityTypeSerializer. The serializer sets the value of "MessageStateName" by getting the name of BayStateEnum from the value of "MessageState".
It works well only except when the URL contains "$select". I debugged the code and found it was executed and entityInstanceContext.EntityInstance had the correct value, but entityInstanceContext.EdmModel, which was of type System.Web.OData.Query.Expressions.SelectExpandBinder.SelectSome, still had an empty "MessageStateName".
public class CustomEntitySerializer : ODataEntityTypeSerializer
{
public CustomEntitySerializer(ODataSerializerProvider serializerProvider)
: base(serializerProvider)
{
}
public override ODataEntry CreateEntry(SelectExpandNode selectExpandNode, EntityInstanceContext entityInstanceContext)
{
if (entityInstanceContext.EntityInstance is SmartLinkInfoModel)
{
var smartLinkInfo = entityInstanceContext.EntityInstance as SmartLinkInfoModel;
if (smartLinkInfo.ModemIMEI != null)
{
smartLinkInfo.ModemIMEIString = "0x" + string.Join(string.Empty, smartLinkInfo.ModemIMEI.Select(b => (b - 48).ToString()));
}
if (smartLinkInfo.SmartLinkHardwareId != null)
{
smartLinkInfo.SmartLinkHardwareIdString = "0x" + string.Join(string.Empty, smartLinkInfo.SmartLinkHardwareId.Select(b => b.ToString()));
}
if (smartLinkInfo.XbeeSourceId != null)
{
smartLinkInfo.XbeeSourceIdString = "0x" + string.Join(string.Empty, smartLinkInfo.XbeeSourceId.Select(b => b.ToString()));
}
}
else if (entityInstanceContext.EntityInstance is BayMessageModel)
{
var bayMessage = entityInstanceContext.EntityInstance as BayMessageModel;
bayMessage.MessageStateName = Enum.GetName(typeof(BayStateEnum), bayMessage.MessageState);
}
return base.CreateEntry(selectExpandNode, entityInstanceContext);
}
}
Your code to change the entityInstanceContext.EntityInstance is right, but it won't change the result of select, you can see
object propertyValue = entityInstanceContext.GetPropertyValue(structuralProperty.Name);
in ODataEntityTypeSerializer 's CreateStructuralProperty method, you should override this method, if the structuralProperty.Name is MessageStateName, then use (entityInstanceContext.EntityInstance as BayMessageModel).MessageStateName

How to request same parameter twice in query string?

I am trying to request the following query string url: api/item?name=storm&name=prest
I am using the following code below and I cannot get the code to work.
public class ItemController : ApiController
{
private cdwEntities db = new cdwEntities();
public HttpResponseMessage Get([FromUri] Query query)
{
var data = db.database_ICs.AsQueryable();
if (query.name != null)
{
**data = data.Where(c => c.Name.Split("&").Contains(query.name));**
}
if (query.id!= null)
{
data = data.Where(c => c.ID== query.id);
}
if (!data.Any())
{
var message = string.Format("No data was found");
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
}
return Request.CreateResponse(HttpStatusCode.OK, data);
}
}
Any help would be very much appreciated.
You can use post Api and send array of [name].
name = [item1,item2....]
public void Post([FromBody] List<string> name) {
}
You can not pass same name key in Querystring. Browser/Code did not identified which is correct value, if you want multiple value then pass as a object.

MVC4 - during post controller action, the WebSecurity.CurrentUserId is losing it's value, and becomes -1

Somehow, in this controller, after the SaveChanges, the CurrentUserId becomes -1.
The data post works, and the CurrentUserId has it's logged in value (example 8888), but after the SQL insert, the WebSecurity.CurrentUserId becomes -1. Any clue? During debug I can't find where and why.
// POST: /Account/Edit
[HttpPost]
[ValidateInput(false)]
public ActionResult Edit(UserProfile model)
{
if (ModelState.IsValid)
{
using (var context = new dbContext())
{
var id = WebSecurity.CurrentUserId;
var account = context.UserProfiles.Find(id);
UpdateModel(account);
context.SaveChanges();
return RedirectToAction("Index", "Account");
}
}
else
{
return View(model);
}
}
that will always return -1, what you need is the below code
int currentuserid = WebSecurity.GetUserId(username);
You can then validate that the userid above, matches the userid in the model, in order to prevent users, changing other users code
as Additional. I use this in my Base Controller:
public int GetUserId()
{
var userid = "0";
if (Request.IsAuthenticated && User.Identity.Name != null)
{
var membershipUser = Membership.GetUser(User.Identity.Name);
if (membershipUser != null)
{
if (membershipUser.ProviderUserKey != null)
{
userid = membershipUser.ProviderUserKey.ToString();
}
}
}
return Convert.ToInt32(userid);
}

Return View() or PartialView()? How to decide?

I hava an Action:
public ActionResult GetOrders(int id)
{
...
}
When I access it through hyperlink(~/Order/GetOrders/1), I want GetOrder return View(), the whole page.
When through #Html.Action("GetOrders"), I want it return PartialView() to be a part of a page.
Now i settled the problem using Erik Philips's method.
public ActionResult GetOrders(int id)
{
var orders = db.Order.Where(a => a.AdCompanyID == id).ToList();
ViewBag.AdCompanyName = db.AdCompany.Where(a => a.ID == id).Select(a => a.Name).First().ToString();
if (ControllerContext.IsChildAction)
{
ViewBag.isPartial = true;
return PartialView(orders);
}
ViewBag.isPartial = false;
return View(orders);
}
#{Html.RenderAction("GetOrders", new { id = Model.ID });}
#Html.ActionLink("Related orders", "GetOrders", new { id = item.ID })
in GetOrders.cshtml:
#if (ViewBag.isPartial == false)
{
...
}
to generate different view.
Queti M. Porta thanks all the same!
You can use the ControllerContext.IsChildAction.
public ActionResult Foo()
{
if (ControllerContext.IsChildAction)
{
return PartialView("GetOrdersPartial", model);
}
return View("GetOrders", model);
}
Also, I would recommend using Html.RenderAction.
Updated per Comment
I'd also mention that I've never had the need to do this, in my own experience. Either you really have a completely different view, or you are unaware that PartialView will return a view without a Layout.
An easy way would be to pass in a parameter into the action method to let it know how you want the view rendered.
public ActionResult GetOrders(int id, bool? isPartial)
{
return (isPartial.HasValue() && isPartial.Value)
? PartialView()
: View();
}
In the above example, we are passing in the isPartial, however, you can also check to see if the request was done via ajax using Request.IsAjaxRequest
Other than that, there aren't many other ways to determine the method of the request.