IHttpActionResult and helper methods in ASP.NET Core - asp.net-web-api

I'm trying to move my web api 2 project to ASP.NET 5.
But I have many elements that are not present anymore.
For example IHttpActionResult or Ok(), NotFound() methods.
Or RoutePrefix[]
Should I change every IHttpActionResult with IActionResult ?
Change Ok() with new ObjectResult ? (is it the same ?)
What about HttpConfiguration that seems no more present in startup.cs ?

IHttpActionResult is now effectively IActionResult, and to return an Ok with a return object, you'd use return new ObjectResult(...);
So effectively something like this:
public IActionResult Get(int id)
{
if (id == 1) return HttpNotFound("not found!");
return new ObjectResult("value: " + id);
}
Here's a good article with more detail:
http://www.asp.net/vnext/overview/aspnet-vnext/create-a-web-api-with-mvc-6

Updated reply-ish
I saw that someone referenced the WebApiCompatShim in a comment.
WebApiCompatShim is still maintained for this kind of portability scenarios and it is now released 1.1.0.
I saw that Microsoft.AspNetCore.OData 1.0.0-rtm-00011 has WebApiCompatShim as a dependency. I don't know exactly what they are trying to achieve in this area, these are just facts.
If you're not into getting another compatibility package and you're looking into more refactoring work, you can look at the following approach: WebApiCompatShim - how to configure for a REST api with MVC 6
You will still be able to use Ok() or you can try to use the OkObjectResult() method as Http word was removed in order not to be too verbose. HttpOkObjectResult -> OkObjectResult
[HttpPost]
public ObjectResult Post([FromBody]string value)
{
var item = new {Name= "test", id=1};
return new OkObjectResult(item);
}
[HttpPost]
public ObjectResult Post([FromBody]string value)
{
var item = new {Name= "test", id=1};
return Ok(item);
}

At 2.2, the ASP.NET Core migration guide states to replace IHttpActionResult with ActionResult. This works for me:
[Produces("application/json")]
[HttpPost]
public ActionResult GetSomeTable([FromBody] GridState state)
{
return Ok(new
{
data = query.ToList(),
paging = new
{
Total = total,
Limit = state.limit,
page = state.page,
Returned = query.Count()
}
});
}

Related

How to resolve Web API AmbiguousActionException in dotnet core web api?

I have two Get methods. I want to access this by using following urls
https://localhost:44396/api/values/1
https://localhost:44396/api/values/1?status=1
But I am trying to call this I am getting following exception
AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied:
// GET api/values/5
[HttpGet("{id}")]
public ActionResult<string> Get(SomeEnum id)
{
//somecode
return "value";
}
[HttpGet("{id}")]
public ActionResult<string> Get(SomeEnum id,int status)
{
//somecode
return "value";
}
Is there any way to use routs like this with mutltiple get methods
There is nothing out of the box provided by ASP.NET core to help your case. As suggested in one of the comments, you should make the status parameter as nullable and use it within the action method to decide what next to do. Something like this:
[HttpGet("{id}")]
public ActionResult<string> Get(SomeEnum id,int? status)
{
if(status == null)
{
//perform usual logic which requires only id
}
else
{
//perform logic or call a method which requires both id and status
}
return "value";
}

ToPagedListAsync() - The provider for the source IQueryable doesn't implement IDbAsyncQueryProvider

I have been trying to sort out this for hours and could not find a solution.
I am using a Web Api MVC project and trying to convert an IEnumerable list to a paged list async. I'm able to compile but I'm still getting a runtime error on the ToPagedListAsync() line.
The provider for the source IQueryable doesn't implement IDbAsyncQueryProvider. Only providers that implement IDbAsyncQueryProvider can be used for Entity Framework asynchronous operations. For more details see http://go.microsoft.com/fwlink/?LinkId=287068."
1st Layer:
public async Task<IEnumerable<MatchingCriteria>> GetMatchingCriterias(int id)
{
return await _ctx.MatchingCriterias.Include(mc => mc.RuleDefinition).Where(mc => mc.RuleDefinitionId == id).ToListAsync();
}
2nd Layer:
internal async Task<IEnumerable<MatchingCriteria>> GetMatchingCriterias(int id)
{
using (CrawlerDbContext db = new CrawlerDbContext())
{
var matchCriteriaMgr = new MatchingCriteriaManager(db);
return await matchCriteriaMgr.GetMatchingCriterias(id);
}
}
Controller:
public async Task<ActionResult> Index(int? id, string sortOrder, string currentFilter, string searchString, int? page){
var matchingCriterias = await _crawlerProvider.GetMatchingCriterias(id.Value);
//...
IQueryable<MatchingCriteria> matchCrit = matchingCriterias.AsQueryable();
//Error here -> return View(await matchCrit.ToPagedListAsync(pageNumber, pageSize));
}
if I do instead in the controller:
var matchingCriterias = _db.MatchingCriterias.Include(mc => mc.RuleDefinition);
then:
return View(await matchingCriterias.ToPagedListAsync(pageNumber, pageSize)
That works well!
I could return a DbSet on the nested methods but I could not use the ToListAsync() in the 1st layer;
Is there a way to make the list IQueryable?Any other ideas or suggestions?
Well, I fixed it myself by creating an AsyncQueryable extension as mentioned here
Then I could call the pagedListAsync() method like bellow:
return View(await matchingCriterias.AsAsyncQueryable().ToPagedListAsync(pageNumber, pageSize)

DataSourceRequest is not deserializing for a WebAPI Get method

I am trying to call a WebAPI method from Angular 5 like this:
selectClaims(state: DataSourceRequestState):Observable<DataResult>
{
return this.http.get<GridDataResult>(`${this.apiUrl}/SelectClaims?${toDataSourceRequestString(state)}`);
}
Which calls the API method as expected. The API method is:
[Route("SelectClaims")]
[HttpGet]
public IHttpActionResult SelectClaims([FromUri][DataSourceRequest]DataSourceRequest ClaimsRequest)
{
if(ClaimsRequest == null)
ClaimsRequest=new DataSourceRequest { Page=1, PageSize=20 };
var result = _db.Claims.ToDataSourceResult(ClaimsRequest, c => { c.SortHistory(); return c; });
return Ok(result);
}
The trouble is that ClaimsRequest only de-serializes Page and PageSize correctly. Filters and Sorts don't come through:
Fiddler tells me that the URL from Angular is:
GET /api/v1/Claims/SelectClaims?filter=id~eq~2&page=1&sort=firstName-asc&pageSize=20 HTTP/1.1, but in the controller both filter and sort are null.
If I create a URL through Swagger like: 'http://localhost:50223/api/v1/Claims/SelectClaims?ClaimsRequest.page=1&ClaimsRequest.pageSize=11&ClaimsRequest.sorts=firstName-desc' I do see a sort array in the API method, but the "Member" field is null.
Any attempt to add a filter through Swagger like 'http://localhost:50223/api/v1/Claims/SelectClaims?ClaimsRequest.page=1&ClaimsRequest.pageSize=11&ClaimsRequest.filters=id~eq~2' results in a "Cannot create an instance of an interface." error.
The state is a DataSourceRequestState in the angular component from a Kendo Grid for Angular.
I have simulated this in a simple test program and everything works fine there. The only difference in my test program is that the API controller targets .Net Core and the real system targets .Net 4.6.1.
Do I have to de-serialize manually in .Net 4.6.1 for some reason, or is something else going on here?
It should be a POST not a GET. Something like this:
return this.http.post<GridDataResult>(`${this.apiUrl}/SelectClaims`, toDataSourceRequestString(state)});
I needed it to be a GET (URL) so i created a new object
public class GridParamaterBinder
{
public int Page { get; set; }
public int PageSize { get; set; }
public string Filter { get; set; }
public string Sort { get; set; }
public DataSourceRequest ToDataSourceRequest(IConfigurationProvider mapper, Func<string, string> OverDefaultParamaterMapping)
{
DataSourceRequest result = new DataSourceRequest();
result.Page = Page;
result.PageSize = PageSize;
result.Sorts = GridDescriptorSerializer.Deserialize<SortDescriptor>(Sort);
result.Filters = FilterDescriptorFactory.Create(Filter);
return result;
}
}
and used it instead of the Telerik effort.
in API I Bind it like so
public virtual DataSourceResult Get([FromUri]GridParamaterBinder request)
And then used it like
DataSourceResult results = query.ToDataSourceResult(request.ToDataSourceRequest(), r => (r)));
Thanks #KevDevMan for your solution. I found this example,
then I changed my API controller like this and it worked like a charm :
[HttpGet, Route("for-kendo-grid")]
public DataSourceResult GetProducts([System.Web.Http.ModelBinding.ModelBinder(typeof(WebApiDataSourceRequestModelBinder))] DataSourceRequest request)
explanation here

How do you read POST data in an ASP.Net MVC 3 Web API 2.1 controller?

This does not seem to be as easy as I thought. I found some solutions on the web, but they are not working for me. I have an ASP.Net MVC 3 project with the Microsoft ASP.Net Web API 2.1 nuget package installed. Now, I want to be able to read data posted to a web api controller. The data sent will vary, so I cannot used a strongly typed ViewModel.
Here are the solutions I tried:
public void Post([FromBody]string value)
{
...
}
public void Post([FromBody]List<string> values)
{
...
}
public void Post([FromBody]NameValueCollection values)
{
...
}
But my value or values variables are always empty. I know the controller is receiving data however because I can check it by accessing (System.Web.HttpContextWrapper)Request.Properties["MS_HttpContext"].Request.Form. It does not look like the proper way to retrieve the data though. There ought to be a cleaner way.
UPDATE:
Here is how I am posting the information:
I am posting the data from another controller in the same web application:
public ActionResult SendEmailUsingService()
{
dynamic email = new ExpandoObject();
email.ViewName = "EmailTest";
email.From = "fromaddress#yahoo.com";
email.To = "toaddress#gmail.com";
email.Fullname = "John Smith";
email.Url = "www.mysite.com";
IDictionary<string, object> data = email;
using (var wb = new WebClient())
{
string url = BaseUrlNoTrailingSlash + Url.RouteUrl("DefaultApi", new { httproute = "", controller = "Emailer" });
var response = wb.UploadValues(url, "POST", data.ToNameValueCollection());
}
return View();
}
And here is what I am getting in my Post web api controller if I declare an httpContext variable like this:
var httpContext = (System.Web.HttpContextWrapper)Request.Properties["MS_HttpContext"];
httpContext.Request.Form =
{ViewName=EmailTest&From=fromaddress%40yahoo.com&To=toaddress%40gmail.com&Fullname=John+Smith&Url=www.mysite.com}
httpContext.Request.Form is a System.Collections.Specialized.NameValueCollection {System.Web.HttpValueCollection}
I finally found the answer to my question here:
Web API Form Data Collection
The solution is to use FormDataCollection:
public void Post([FromBody]FormDataCollection formData)
{
...
}

MVC Delete record but how to code this in Controller

I'm a beginner of MVC3 with ASP.Net (C#) but I don't get the next situation to delete a record.
I have a View that ask the user to confirm delete a item (record). As code I have this to initialize the view:
public ActionResult KeywordsDelete(Guid id)
{
_db = new BlaContext();
return _db.SearchTerms.Where(x => x.id.Equals(id)).First();
}
But when confirmed, then I have the next code.
[HttpPost]
public ActionResult KeywordsDelete(Guid id)
{
_db = new BlaContext();
var term = _db.SearchTerms.Where(x => x.id == id).First();
_db.SearchTerms.Remove(term);
_db.SaveChanges();
return View("Keywords", _db.SearchTerms.ToList());
}
Building is not possible because the signature of this method is already exists (same parameters and method name).
So I don't get how to delete a record in this situation. The view is created with a default Scaffold template (delete).
I found an alternative solution to this problem while reading up on MVC. Check out: Improving the Details and Delete Methods
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id = 0)
{
// Delete stuff...
}
This will route the action Delete to the method DeleteConfirmed.
You can give your post function another additional parameter
[HttpPost]
public ActionResult KeywordsDelete(Guid id, FormCollection collection)
{
_db = new BlaContext();
var term = _db.SearchTerms.Where(x => x.id == id).First();
_db.SearchTerms.Remove(term);
_db.SaveChanges();
return View("Keywords", _db.SearchTerms.ToList());
}
But your GET Action should also return a View not a data object, I think.
public ActionResult KeywordsDelete(Guid id)
{
_db = new BlaContext();
return View(_db.SearchTerms.Where(x => x.id.Equals(id)).First());
}

Resources