How to return xml or json with ASP.NET web mvc 6 depending on Accept Header - asp.net-web-api

I have implemented a web api controller using ASP.NET mvc 6 and I would like to return the result of the controller as json or xml, depending on the client's Accept header. For example, if the client sends a GET request with "Accept: application/xml", then the returned response should be xml. If the header is "Accept: application/json", then it should be json. At the moment the controller always returns json. Is there a way of configuring this? Note: this question is indeed a duplicate of How to return XML from an ASP.NET 5 MVC 6 controller action. However the solution provided there did not solve my problem. The accepted answer below worked for me.
The controller is given below and is the one provided by the ASP.NET 5 web api template:
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET: api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id:int}")]
public string Get(int id)
{
return "value";
}
// POST api/values
[HttpPost]
public void Post([FromBody]string value)
{
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
Thanks for your help!

I did the research for you, you may continue alone:
needed:
"Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
"Microsoft.AspNet.Mvc.Core": "6.0.0-rc1-final",
"Microsoft.AspNet.Mvc.Formatters.Xml": "6.0.0-rc1-final"
startup:
services.Configure<MvcOptions>(options =>
{
options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
});
go on from here
another
and antoher

Related

Can't retrieve BadRequest errors when calling .NET CORE 5 Web API microservice from ASP.NET MVC project

I am trying to retrieve ModelState validation errors from a micro service built with .NET Core 5 Web API from a ASP.NET Core MVC frontend.
Say I have a model that looks like this:
public class Comment
{
public string Title { get; set; }
[Required]
public string Remarks { get; set; }
}
When I call the rest endpoint in the micro service through Swagger to update the Comment model without a remark, I get a response body like this:
{
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"Remarks": [
"The Remarks field is required."
]
}
}
Awesome! This is what I expect... However, when I call this endpoint through my MVC project, I can't seem to get the actual "errors".
This is how I am calling the rest endpoint:
var client = _httpClientFactory.CreateClient("test");
HttpContent httpContent = new StringContent(JsonConvert.SerializeObject(comment), Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PutAsync($"api/comments", httpContent);
The response object just has a statusCode of BadRequest. I want to pull the info regarding the errors (the section that says "The Remarks field is required.") but I don't see it in the Content or Headers property or anything like that.
I am new to micro services and .NET Core - am I doing something wrong? Do I need to add something to the startup.cs? Seems weird that I can get the BadRequest status but no supporting problem details.
Thanks in advance!
Be sure your web api controller is not declared with [ApiController].
Web Api Project:
//[ApiController]
[Route("api/[controller]")]
public class CommentsController : ControllerBase
{
[HttpPut]
public IActionResult Put([FromBody] Comment model)
{
if(ModelState.IsValid)
{
//do your stuff...
return Ok();
}
return BadRequest(ModelState);
}
}
Mvc Project:
HttpResponseMessage response = await client.PutAsync($"api/comments", httpContent);
var result = response.Content.ReadAsStringAsync().Result;

Model Binder Not Finding Post Data

I have a simple jquery post
function saveImage(base64) {
$.post("http://localhost:50575/api/images", {base64Data: base64});
}
That is going against a .net core controller
[HttpPost]
public async Task<ActionResult> Post(string base64Data)
{
var base64 = Request.Form["base64Data"];
return Ok();
}
When the data is posted, base64Data is null. However, base64, which is populated from the form variables has a value.
Is there any reason why this shouldn't work?
What sent by $.post("http://localhost:50575/api/images", {base64Data: base64}) is:
POST /api/images HTTP/1.1
Content-Type: application/x-www-form-urlencoded
base64Data=xxxxxxxx
Since you send the request with a content-type of application/x-www-form-urlencoded and have the request processed by a ApiController , you should decorate the parameter with a [FromForm]
public async Task<ActionResult> Post([FromForm] string base64Data)
{
// ...
}
Or if you would like to send the request encoded with application/json , you should firstly create a DTO to hold the whole playload :
public class Base64Dto{
public string Base64Data{get;set;}
}
and decorate the parameter with a [FromBody] at the same time :
public async Task<ActionResult> Post([FromBody] Base64Dto base64Data)
{
// var base64 = Request.Form["base64Data"];
return Ok();
}
Another way to hold the whole payload with Base64Dto , is to send the request with a header of Content-Type: application/x-www-form-urlencoded , and use a [FromForm] attribute at the same time :
public async Task<ActionResult> Post([FromForm] Base64Dto base64Data)
{
// var base64 = Request.Form["base64Data"];
return Ok();
}

Custom AuthorizeAttribute on WebApi OData EntitySetController

I created custom authorize attribute to handle my custom permissions on WebAPI odata controller inherited from EntitySetController, here is the code for my attribute
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public class RequirePermissionsAttribute : System.Web.Http.AuthorizeAttribute
{
public Permissions[] Permissions { get; set; }
public RequirePermissionsAttribute()
{ }
public RequirePermissionsAttribute(params Permissions[] permissions)
{
this.Permissions = permissions;
}
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
// Custom authorization logic
}
Now I try to add this attribute on Get() method, it get invoked
public class ItemsController : EntitySetController<Item, Guid>
{
[EnableQuery(MaxExpansionDepth = 5)]
[RequirePermissionsAttribute(Permissions.ViewAll)]
public override IQueryable<Item> Get()
{
//Code go here
}
}
But when I add the same attribute on CreateEntity() it never get invoked
[RequirePermissionsAttribute(Permissions.Add)]
protected override Item CreateEntity(Item item)
{
// Create item
}
Any help appreciated
You should use your attribute as RequirePermissions without the tailed "Attribute" word, So change your code to be like this
[RequirePermissions(Permissions.Add)]
protected override Item CreateEntity(Item item)
{
// Create item
}
Islam
From the WebAPI source codes, the internal virtual function CreateEntity() is called in POST request. Here's the source codes in EntitySetController:
public virtual HttpResponseMessage Post([FromBody] TEntity entity)
{
TEntity createdEntity = CreateEntity(entity);
TKey entityKey = GetKey(entity);
return EntitySetControllerHelpers.PostResponse(this, createdEntity, entityKey);
}
I use your sample codes and send a POST request, the CreateEntity() can be invoked as:
POST ~/odata/Items
Content-type: application/json
{"Id":"9daf653f-212c-42e3-80a4-4778e445c092"}
However, if you want to get the correct response, you should override GetKey() because GetKey() is called after CreateEntity() in the Post() method. The same information is also mentioned in the remarks of CreateEntity() as below:
Sample Test
I create the following two functions in ItemsController:
protected override Guid GetKey(Item entity)
{
return entity.Id;
}
[RequirePermissionsAttribute(Permissions.Add)]
protected override Item CreateEntity(Item item)
{
// Create item
return item;
}
And send the same POST request mentioned above, I can get the following response:
HTTP/1.1 201 Created
Cache-Control: no-cache
.....
Content-Length: 124
{
"odata.metadata":"http://localhost:47794/odata/$metadata#Items/#Element","Id":"9daf653f-212c-42e3-80a4-4778e445c092"
}
Hope it can help you. Thanks.

Breeze webapi controller 'cannot support querying' over DbSet error

I have setup very similar to breeze sample application which comes in sample nuget. This is the code of my api controller:
[JsonFormatter, ODataActionFilter]
public class WorkOrdersController : ApiController
{
readonly EFContextProvider<WorkOrdersContext> _contextProvider =
new EFContextProvider<WorkOrdersContext>();
public WorkOrdersController()
{
// I was thinking this may be the cause of the issue
this._contextProvider.Context.Configuration.ProxyCreationEnabled = false;
}
[HttpGet]
public string Metadata()
{
return _contextProvider.Metadata();
}
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
return _contextProvider.SaveChanges(saveBundle);
}
[HttpGet]
public IQueryable<WorkOrder> WorkOrders()
{
return _contextProvider.Context.WorkOrders;
}
}
The problem I'm having is when I try to perform query over WorkOrders action, I get 500 - Internal server error, and this is the payload of response:
{"$id":"1","$type":"System.Web.Http.HttpError, System.Web.Http","Message":"An error has occurred.","ExceptionMessage":"The action 'WorkOrders' on controller 'WorkOrders' with return type 'System.Collections.Generic.List`1[[WorkOrders.Domain.Models.WorkOrder, WorkOrders.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' cannot support querying. Ensure the type of the returned content is IEnumerable, IQueryable, or a generic form of either interface.","ExceptionType":"System.InvalidOperationException","StackTrace":" at System.Web.Http.QueryableAttribute.ValidateReturnType(Type responseContentType, HttpActionDescriptor actionDescriptor)\r\n at System.Web.Http.QueryableAttribute.OnActionExecuted(HttpActionExecutedContext actionExecutedContext)\r\n at System.Web.Http.Tracing.ITraceWriterExtensions.TraceBeginEnd(ITraceWriter traceWriter, HttpRequestMessage request, String category, TraceLevel level, String operatorName, String operationName, Action`1 beginTrace, Action execute, Action`1 endTrace, Action`1 errorTrace)\r\n at System.Web.Http.Tracing.Tracers.ActionFilterAttributeTracer.OnActionExecuted(HttpActionExecutedContext actionExecutedContext)\r\n at System.Web.Http.Filters.ActionFilterAttribute.<>c__DisplayClass2.<System.Web.Http.Filters.IActionFilter.ExecuteActionFilterAsync>b__0(HttpResponseMessage response)\r\n at System.Threading.Tasks.TaskHelpersExtensions.<>c__DisplayClass41`2.<Then>b__40(Task`1 t)\r\n at System.Threading.Tasks.TaskHelpersExtensions.ThenImpl[TTask,TOuterResult](TTask task, Func`2 continuation, CancellationToken cancellationToken, Boolean runSynchronously)"}
WorkOrders is Dbset on context:
public DbSet<WorkOrder> WorkOrders { get; set; }
I have also tried explicitly casting it to IQueryable, with no change:
[HttpGet]
public IQueryable<WorkOrder> WorkOrders()
{
return (IQueryable<WorkOrder>)_contextProvider.Context.WorkOrders;
}
The only thing that works for me is:
[HttpGet]
public IEnumerable WorkOrders()
{
return _contextProvider.Context.WorkOrders.AsEnumerable();
}
This, however causes another problem for me on the client side, described in this question.
Just a guess here, but can you try using the latest breeze (v 0.82.1) with the following attribute.
[BreezeController]
instead of these
[JsonFormatter, ODataActionFilter]
The new [BreezeController] attribute is a complete replacement for the other two and avoids some other issues.

Appropriate pattern for setting request object properties from POST request with MVC3?

With incoming POST requests to my MVC3 application, I want to validate the incoming request parameters. If an invalid parameter exists, an exception is thrown.
Given the following object:
public class ActionRequest
{
public string ActionRequestPassword { get; set; }
public bool EnableNewsfeedAppPool { get; set; }
}
With incoming post requests, I want to initialize the object with the appropriate properties via:
public class NewsfeedAppPoolController : Controller
{
[ActionName("EnableAppPool"), AcceptVerbs(HttpVerbs.Post)]
[ValidateInput(false)]
[NoCache]
public ActionResult EnableAppPool(FormCollection formCollection)
{
Models.ActionRequest actionRequest = ValidatePOSTRequest(formCollection);
// do things with actionRequest
return null;
}
private Models.ActionRequest ValidatePOSTRequest(FormCollection formCollection)
{
try
{
Type actionRequestType = typeof(Models.ActionRequest);
System.Reflection.PropertyInfo propertyInfo = null;
object systemActivatorObject = Activator.CreateInstance(actionRequestType);
foreach (var key in formCollection.AllKeys)
{
propertyInfo = typeof(Models.ActionRequest).GetProperty(key);
Type t = propertyInfo.PropertyType; // t will be System.String
if (t.Name == "Int32")
{
actionRequestType.GetProperty(key).SetValue(systemActivatorObject, Convert.ToInt32(formCollection[key]), null);
}
else
{
actionRequestType.GetProperty(key).SetValue(systemActivatorObject, formCollection[key], null);
}
}
return (Models.ActionRequest)systemActivatorObject;
}
catch (Exception ex)
{
throw ex;
}
}
}
I would like to know if there can be any improvements made to this, or recommendations of how else to accomplish this in an efficient manner.
Thanks.
ASP.Net MVC already does all of this for you.
Just add a Models.ActionRequest actionRequest parameter to your action.
If you want to add additional validation logic, use System.ComponentModel.DataAnnotations.
Simply use the default model binder which will take care of instantiating and binding the ActionRequest from the request parameters:
public class NewsfeedAppPoolController : Controller
{
[ActionName("EnableAppPool"), AcceptVerbs(HttpVerbs.Post)]
[ValidateInput(false)]
[NoCache]
public ActionResult EnableAppPool(ActionRequest actionRequest)
{
// do things with actionRequest
return null;
}
}
The appropriate pattern is,
[HttpPost]
public ActionResult Save(Employee employee)
{
if(ModelState.IsValid)
{
db.Save(employee);
RedirectToAction("Index");
}
return View();
}
Notes:
The employee instance is automatically created and populated by the default model binder from the values available in the request(form, querystrings, routedata and more)
When the default model binder binds the values to the model it also does the validation and store all the errors in the ModelState dictionary, so by checking the ModelState.IsValid you can know that whether the validation is succeeded or not.
To know more about model binding refer this.
To know more about model validation refer this.

Resources