Searching With RESTful Web Services

As well as the usual CRUD (create, read, update, delete) actions, you will probably want to provide some search facilities in your web service.

You can use a SearchCriteria object for this, where the request parameters in the URL map to attributes of the SearchCriteria. For example in C# your API controller has a method like this:

using log4net;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace WebServices.Controllers
{
    public class BooksController : ApiController
    {
        public static readonly ILog LOGGER = log4net.LogManager.GetLogger(typeof(BooksController));

        [System.Web.Mvc.Authorize]
        public HttpResponseMessage Get([FromUri]BookSearchCriteria criteria)
        {
            LOGGER.Debug("BooksController.Get(BookSearchCriteria) : Getting books for criteria[" + criteria + "]...");
            ...
            return (Request.CreateResponse(HttpStatusCode.OK, results));
        }
    }
}

The SearchCriteria object looks like this:

using System.Text;

namespace WebServices.DAO
{
    public class BookSearchCriteria
    {
        public string Author
        {
            get;
            set;
        }

        public string ISBN
        {
            get;
            set;
        }
    }

    public override string ToString()
    {
        bool first = true;
        StringBuilder sb = new StringBuilder(200);

        if (Author != null)
        {
            sb.Append("author[");
            sb.Append(Author);
            sb.Append("]");
            first = false;
        }
        if (ISBN != null)
        {
            if (!first)
            {
                sb.Append(" ");
            }
            sb.Append("ISBN[");
            sb.Append(ISBN);
            sb.Append("]");
            first = false;
        }
            return (sb.ToString());
    }
}

And your Data Access Object checks which attributes of the SearchCriteria are not null and adds them to the Where clause of the SQL:

using WebServices.Data;
using System.Collections.Generic;
using System.Configuration;
using System.Data.Linq;
using System.Linq;
using log4net;

namespace WebServices.DAO
{
    public class BooksDAOImpl : BooksDAO
    {
        public static readonly ILog LOGGER = log4net.LogManager.GetLogger(typeof(BooksDAOImpl));
        public IList Search(BookSearchCriteria criteria)
        {
            ConnectionStringSettings connectionString = DAOHelper.GetConnectionStringSettings(...);
            DataContext context = new DataContext(connectionString.ConnectionString);

            var query = from B in context.GetTable()
                        select new Book
                        {
                            ISBN = B.ISBN,
                            Author = B.Author,
                            Title = B.Title
                        };
            if (criteria.ISBN != null)
            {
                query = query.Where(B => B.ISBN.Equals(criteria.ISBN));
            }
            if (criteria.Author != null)
            {
                query = query.Where(B => B,Author == criteria.Author);
            }

            IList books = query.ToList();
            LOGGER.Debug("BooksDAOImpl.Search() : Found[" + books.Count + "] books for criteria[" + criteria + "].");
            if (books.Count > 0)
            {
                return (query.ToList());
            }
            return (null);
        }
    }
}

Slightly more advanced search

You can add a Contains search (in SQL this would be Like '%value%') by adding more attributes to the SearchCriteria:

using System.Text;

namespace WebServices.DAO
{
    public class BookSearchCriteria
    {
        public string Author
        {
            get;
            set;
        }

        public string ISBN
        {
            get;
            set;
        }

        public string TitleContains
        {
            get;
            set;
        }
    }

    public override string ToString()
    {
        bool first = true;
        StringBuilder sb = new StringBuilder(200);

        if (Author != null)
        {
            sb.Append("author[");
            sb.Append(Author);
            sb.Append("]");
            first = false;
        }
        if (ISBN != null)
        {
            if (!first)
            {
                sb.Append(" ");
            }
            sb.Append("ISBN[");
            sb.Append(ISBN);
            sb.Append("]");
            first = false;
        }
        if (TitleContains != null)
        {
            if (!first)
            {
                sb.Append(" ");
            }
            sb.Append("titleContains[");
            sb.Append(TitleContains);
            sb.Append("]");
            first = false;
        }
        return (sb.ToString());
    }
}

Add this to your DAO:

if (criteria.TitleContains != null)
{
    query = query.Where(B => B.Title.ToLower().Contains(criteria.TitleContains.ToLower()));
}

To get more complex searches you may need to parse search strings from the user, but remember not to concatenate URL parameters onto your SQL without validating them thoroughly.