AngularJS - Mapping REST search endpoint to $ resource
ng-resource returns an object with the following default actions
{ 'get': {method:'GET'},
'save': {method:'POST'},
'query': {method:'GET', isArray:true},
'remove': {method:'DELETE'},
'delete': {method:'DELETE'} };
I'm not sure exactly what is the best method to query data from REST WebApi endpoints from AngularJS, but I implemented a Predicate Builder server to query my db using Linq. I have an endpoint (POST) named "Search ()" @ / api / Product / Search that will accept a searchCriteria JSON object, which is deserialized, fed into the Linq predicate builder, and executed against dbContext. My WebApi2 controller is structured this way using the new route attribute function:
[RoutePrefix("Product")]
public class ProductController : ApiController
{
[HttpGet]
[Route("")]
public IEnumerable<Product> Get()
{
try
{
return _productBL.Get();
}
catch
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
}
[HttpGet]
[Route("{productId}")]
public Product Get(string productId)
{
try
{
var product= _externalWorkStepBL.GetById(productId);
if (product== null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return product;
}
catch (Exception)
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
}
[HttpPost]
public HttpResponseMessage Post([FromBody]Product product)
{
try
{
_productBL.Insert(product);
var response = Request.CreateResponse(HttpStatusCode.Created, product);
response.Headers.Location = new Uri(Request.RequestUri, string.Format("Product/{0}", product.workItemID));
return response;
}
catch
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
}
[HttpPost]
[Route("Search")]
public IEnumerable<Product> Where([FromBody] SearchCriteria searchCriteria)
{
if (searchCriteria == null || (searchCriteria.FieldContainsList == null || searchCriteria.FieldEqualsList == null || searchCriteria.FieldDateBetweenList == null))
{
throw new HttpRequestException("Error in, or null, JSON");
}
return _productBL.Where(searchCriteria);
}
[HttpPut]
[Route("")]
public HttpResponseMessage Put([FromBody]Productproduct)
{
try
{
_productBL.Update(product);
var response = Request.CreateResponse(HttpStatusCode.OK);
response.Headers.Location = new Uri(Request.RequestUri, string.Format("Product/{0}", product.Id));
return response;
}
catch ()
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
}
[HttpDelete]
[Route("{productId}")]
public void Delete(string productId)
{
HttpResponseMessage response;
try
{
_productBL.Delete(productId);
response = new HttpResponseMessage(HttpStatusCode.NoContent);
response.Headers.Location = new Uri(Request.RequestUri, string.Format("Product/"));
}
catch (Exception)
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
}
}
On the client side, I've wrapped the $ resource in a factory called $ myResource, adding a PUT method. Then I use $ myResource for my other factories like this:
var app = angular.module('App', ['ngResource'])
.factory('$myResource', ['$resource', function ($resource) {
return function (url, paramDefaults, actions) {
var MY_ACTIONS = {
'update': { method: 'PUT' }
};
actions = angular.extend({}, MY_ACTIONS, actions);
return $resource(url, paramDefaults, actions);
}
}])
.service('ProductFactory', ['$myResource', function ($myResource) {
return $myResource('/api/Product/:productId')
}]);
This works great, but now I want to add a search endpoint. Angular documentation for ng-Resource states that the url can be redefined in the action method, but it is not clear to me how to do this, I can add a "search" action to $ myResource, but how to change the url in the ProductFactory?
.factory('$myResource', ['$resource', function ($resource) {
return function (url, paramDefaults, actions) {
var MY_ACTIONS = {
'update': { method: 'PUT' },
'search': { method: 'POST','params': { searchCriteria: '@searchCriteria' }, isArray: true }
};
actions = angular.extend({}, MY_ACTIONS, actions);
return $resource(url, paramDefaults, actions);
}
}])
As at present, the call to ProductFactory.search (searchCriteria) sends a POST request with correct JSON, but with the wrong URL "/ api / Product". I need it to send the message "/ api / Product / Search". How can I change $ myResource to use "api / xxx / Search" where xxx is the lookupname?
source to share
Nevermind! Didn't expect it to work, but it will.
.factory('$myResource', ['$resource', function ($resource) {
return function (url, paramDefaults, actions) {
var searchUrl = url + "/Search/:searchCriteria"
var MY_ACTIONS = {
'update': { method: 'PUT' }, //add update (PUT) method for WebAPI endpoint
'search': { method: 'POST', url : searchUrl,'params': { searchCriteria: '@searchCriteria' }, isArray: true } //add Search (POST)
};
actions = angular.extend({}, MY_ACTIONS, actions);
return $resource(url, paramDefaults, actions);
}
}])
source to share