Examples:
Eg: validate API key, generate ID, create record and return created resource:
Create api key auth class in a folder called 'Filters' (can be any name really).
// api key auth attribute filter:
// ApiKeyAuthAttribute.cs
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Net;
using System.Net.Http;
namespace WebApi_ApiKey1.Filters
{
public class ApiKeyAuthAttribute : AuthorizeAttribute
{
private const string API_KEY_HEADER = "X-API-KEY";
private const string VALID_API_KEY = "my-secret-key-123"; // move to config in real apps
protected override bool IsAuthorized(HttpActionContext actionContext)
{
// check header exists
if (!actionContext.Request.Headers.Contains(API_KEY_HEADER))
{
return false;
}
// retrieve api key value from header
var apiKey = actionContext.Request.Headers
.GetValues(API_KEY_HEADER)
.FirstOrDefault();
// validate api
return apiKey == VALID_API_KEY;
}
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
actionContext.Response = actionContext.Request
.CreateResponse(HttpStatusCode.Unauthorized, "Invalid or missing API key");
}
}
}
// webapi controller and model:
// PostController.cs
using WebApi_ApiKey1.Filters; // folder
namespace WebApi_ApiKey1.Controllers
{
[ApiKeyAuth]
[RoutePrefix("api/records")]
public class PostController : ApiController
{
private static List<Record> Records = new List<Record>
{
new Record { Id = 1, Value = "First" },
new Record { Id = 2, Value = "Second" }
};
// POST api/records
[HttpPost]
[Route("")]
public IHttpActionResult Create([FromBody] RecordCreateDto dto)
{
if (dto == null || string.IsNullOrWhiteSpace(dto.Value))
{
return BadRequest("Value is required.");
}
// example for small list (in-memory list)
var newId = Records.Any() ? Records.Max(r => r.Id) + 1 : 1; // Any() returns true if list has one or more items, else false.
var record = new Record
{
Id = newId,
Value = dto.Value
};
Records.Add(record);
// ef:
//var record = new Record { Value = dto.Value };
//dbContext.Records.Add(record);
//dbContext.SaveChanges(); // ID auto-generated
// in ASP.NET Web API, the Created() method is a helper for HTTP 201 Created responses.
// syntax: Created(string location, object content)
// location : URL of the newly created resource
// content : the resource itself (returned in the body)
// outputs:
// HTTP / 1.1 201 Created
// Location: api/records/3
// Content-Type: application/json
return Created($"api/records/{record.Id}", record);
}
// GET api/records
[HttpGet]
[Route("")]
public IHttpActionResult GetAll()
{
return Ok(Records);
}
}
public class RecordCreateDto
{
public string Value { get; set; } // No Id: client cannot control identity
}
}
Test with curl (CREATE):
curl -X POST http://localhost:53923/api/records ^
-H "Content-Type: application/json" ^
-H "X-API-KEY: my-secret-key-123" ^
-d "{\"Value\":\"Created from POST\"}"
^ : caret at the end of a line is a line-continuation character in windows shells (use '\' for linux)
-X : specify http request method 'POST'
-H : add custom headers
-d : send data in request body
Verify the insert with curl get request:
curl http://localhost:53923/api/records -H "X-API-KEY: my-secret-key-123"
Note: if you do not specify an HTTP method in curl, it defaults to GET.
Outputs:

Eg2: test with a client app:
// controller:
// ClientController.cs
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Text;
namespace WebapiClient1.Controllers
{
public class ClientController : Controller
{
private const string ApiBaseUrl = "http://localhost:53923/";
private const string ApiKey = "my-secret-key-123";
// GET: /RecordsClient/Create
public ActionResult Create()
{
return View();
}
// POST: /RecordsClient/Create
[HttpPost]
public async Task<ActionResult> Create(string value)
{
var dto = new RecordCreateDto
{
Value = value
};
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(ApiBaseUrl);
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("X-API-KEY", ApiKey);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var json = JsonConvert.SerializeObject(dto);
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync("api/records", content);
if (!response.IsSuccessStatusCode)
{
return Content($"API error: {response.StatusCode}");
}
var resultJson = await response.Content.ReadAsStringAsync();
return Content(resultJson, "application/json");
}
}
}
public class RecordCreateDto
{
public string Value { get; set; }
}
}
// view:
@{
Layout = null;
ViewBag.Title = "Create Record";
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Create</title>
</head>
<body>
<h2>Create Record</h2>
<form method="post">
<label>Value:</label><br />
<input type="text" name="value" required /><br /><br />
<button type="submit">Create</button>
</form>
</body>
</html>
Outputs:

Eg3: full example:
Post 2 types of values: leadid and lead status.
// api key auth attribute:
// ApiKeyAuthorizeAttribute.cs
namespace WebApi.LeadQService.Filters
{
public class ApiKeyAuthorizeAttribute : AuthorizeAttribute
{
private const string HeaderName = "BUYER-API-KEY";
protected override bool IsAuthorized(HttpActionContext actionContext)
{
if (!actionContext.Request.Headers.Contains(HeaderName))
{
return false;
}
var apiKey = actionContext.Request.Headers.GetValues(HeaderName).FirstOrDefault();
MerchantDal merchantDal = new MerchantDal();
List<Merchant> merchants = merchantDal.GetMerchants(false);
Merchant merchant = merchants.FirstOrDefault(m => m.LeadQApiKey == apiKey
&& m.LeadQIsEnabled == true);
// save merchant id
actionContext.Request.Properties["MerchantId"] = merchant.Id;
// validate api key:
if (merchant != null)
{
return true;
}
return false;
}
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
actionContext.Response = actionContext.Request
.CreateResponse(HttpStatusCode.Unauthorized, "Invalid or missing API key");
}
}
}
// models:
public class LeadSubmitDto
{
public int LeadId { get; set; }
public EnumLeadStatus LeadStatus { get; set; }
}
public class ApiFeedback
{
public int Id { get; set; }
public int LeadId { get; set; } // this can either be convero lead id, or buyer distribution id
public EnumLeadStatus EnumLeadStatus { get; set; }
public DateTime CreatedOn { get; set; }
public int PostedByMerchantId { get; set; } // Keep track of which merchant actually posted becuase a lead can be shared by multiple merchants
}
// business layer:
public class FeedbackBusinessLayer
{
private ConveroReportingContext dbContext;
public FeedbackBusinessLayer(ConveroReportingContext context)
{
this.dbContext = context;
}
public List<ApiFeedback> ApiFeedbacks()
{
return dbContext.ApiFeedbacks.ToList();
}
public void AddApiFeedback(ApiFeedback apiFeedback)
{
dbContext.ApiFeedbacks.Add(apiFeedback);
dbContext.SaveChanges();
}
}
// dbcontext:
public class ConveroReportingContext : DbContext
{
public ConveroReportingContext() : base("name=ConveroReportingConnection")
{
this.Configuration.LazyLoadingEnabled = true;
}
public virtual DbSet<ApiFeedback> ApiFeedbacks { get; set; }
}
// web api controllers:
// cors is installed and enabled for all
namespace WebApi.LeadQService.Controllers
{
[EnableCors(origins: "*", headers: "*", methods: "*")]
[ApiKeyAuthorize]
[RoutePrefix("api/feedback")] // single route for all in controller
public class FeedbackController : ApiController
{
private ConveroReportingContext reportContext;
public FeedbackController()
{
reportContext = new ConveroReportingContext();
}
[HttpPost]
[Route("")]
public IHttpActionResult Submit([FromBody] LeadSubmitDto dto)
{
if (dto == null || dto.LeadId == 0 || dto.LeadStatus == 0 )
{
return BadRequest("Bad request");
}
FeedbackBusinessLayer businessLayer = new FeedbackBusinessLayer(reportContext);
ApiFeedback feedback = new ApiFeedback();
feedback.LeadId = dto.LeadId;
feedback.EnumLeadStatus = (EnumLeadStatus) dto.LeadStatus;
feedback.CreatedOn = DateTime.Now;
feedback.PostedByMerchantId = (int)Request.Properties["MerchantId"];
businessLayer.AddApiFeedback(feedback);
return Created($"api/feedback/{feedback.Id}", feedback);
}
// GET api/records
[HttpGet]
[Route("")]
public IHttpActionResult GetLastWeek()
{
int merchantId = (int)Request.Properties["MerchantId"];
FeedbackBusinessLayer businessLayer = new FeedbackBusinessLayer(reportContext);
List<ApiFeedback> feedbacks = businessLayer.ApiFeedbacks()
.Where(a => a.PostedByMerchantId == merchantId && a.CreatedOn > DateTime.Today.AddDays(-1)).ToList();
return Ok(feedbacks);
}
}
}
// used to generate a random string for api key:
public class SecurityController : ApiController
{
[EnableCors(origins: "*", headers: "*", methods: "*")]
[HttpPost]
[Route("api/security/generatekey")]
public IHttpActionResult GenerateKey()
{
using (var rng = new RNGCryptoServiceProvider())
{
var bytes = new byte[32];
rng.GetBytes(bytes);
// convert to URL-safe Base64
var key = Convert.ToBase64String(bytes)
.Replace("+", "")
.Replace("/", "")
.Replace("=", "");
return Ok(key);
}
}
}