Accessing Route Data in DelegatingHandler using MVC 4 - asp.net

I just Register these routes now how can I get Apikey value in WebApiKeyHandler.cs since it is not part of query string so I am getting null. Also guide me how I am validating keys is best practice for Web Api Project?
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi2",
routeTemplate: "api/v1/users/{apikey}/{controller}/{action}/",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/v1/users/{apikey}/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.MessageHandlers.Add(new WebApiKeyHandler()); // global message handler
}
WebApiKeyHandler.cs
public class WebApiKeyHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
string apikey = HttpUtility.ParseQueryString(request.RequestUri.Query).Get(Constants.API_KEY_QUERY_STRING);
if (string.IsNullOrWhiteSpace(apikey))
{
HttpResponseMessage response = request.CreateErrorResponse(HttpStatusCode.Forbidden, ErrorCodes.API_KEY_EMPTY_ERROR);
var tsc = new TaskCompletionSource<HttpResponseMessage>();
tsc.SetResult(response);
return tsc.Task;
}
else if (!ValidateKey(apikey))
{
HttpResponseMessage response = request.CreateErrorResponse(HttpStatusCode.Forbidden, ErrorCodes.API_KEY_INVALID_ERROR);
var tsc = new TaskCompletionSource<HttpResponseMessage>();
tsc.SetResult(response);
return tsc.Task;
}
else
{
return base.SendAsync(request, cancellationToken);
}
}
private static bool ValidateKey(string apiKey)
{
Guid key;
Guid.TryParse(apiKey, out key);
return new StAccountsDomainContext().ApiSubscriptions.Any(x => x.ApiKey == key && x.IsActive == true && !x.ApiIBlacklists.Any());
}
private static bool ValidateSecretKey(string apiKey, string secretKey)
{
Guid key1;
Guid.TryParse(apiKey, out key1);
Guid key2;
Guid.TryParse(secretKey, out key2);
return new StAccountsDomainContext().ApiSubscriptions.Any(x => x.ApiKey == key1
&& x.SecretKey == key2
&& x.IsActive == true
&& !x.ApiIBlacklists.Any());
}
}

You can use the GetRouteData() extension on HttpRequestMessage to get the route data. Example: request.GetRouteData().Values

Related

ApiController HttpPost parameters

The httpPost transfer with parameters fails in the apiconroller.
It is trying to communicate from Android to Web server.
I succeeded in communicating with Get and Post, which had no parameters.
However, if parameter is added in Post transmission, it fails. I certainly think there is a problem with the Web server code.
The tutorial only contains information about the Model. I want to exchange strings.
Global.asax.cs
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
//....
}
WebApiConfig.cs
public class WebApiConfig
{
public const string UrlPrefix = "api";
public const string UrlPrefixRelative = "~/" + UrlPrefix;
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var httpControllerRouteHandler = typeof(HttpControllerRouteHandler).GetField("_instance",
System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
if (httpControllerRouteHandler != null)
{
httpControllerRouteHandler.SetValue(null,
new Lazy<HttpControllerRouteHandler>(() => new SessionHttpControllerRouteHandler(), true));
}
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: UrlPrefix + "/{controller}/{action}/{sn}",
defaults: new { action = "Index", sn = RouteParameter.Optional }
);
}
public class SessionControllerHandler : HttpControllerHandler, IRequiresSessionState
{
public SessionControllerHandler(RouteData routeData) : base(routeData) { }
}
public class SessionHttpControllerRouteHandler : HttpControllerRouteHandler
{
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
=> new SessionControllerHandler(requestContext.RouteData);
}
}
ApiController.cs
public class LicenseController : ApiController
{
[HttpPost]
public HttpResponseMessage GetLicense([FromBody]string data)
{
return Request.CreateResponse(HttpStatusCode.OK, data);
}
[HttpGet]
public HttpResponseMessage GetLicense2(string data)
{
string udid = data;
string license = AES.Encrypt(udid);
return Request.CreateResponse(HttpStatusCode.OK, license);
}
[HttpPost]
public HttpResponseMessage GetLicense3()
{
return Request.CreateResponse(HttpStatusCode.OK, "ABC");
}
}
android code
new Thread(new Runnable() {
#Override
public void run() {
try{
// Defined URL where to send data
URL url = new URL("http://192.1.1.1:80/api/License/GetLicense/");
// Send POST data request
URLConnection conn = url.openConnection();
conn.setDoOutput(true);
OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
//wr.write(URLEncoder.encode("data=3434", "UTF-8") );
wr.write("data=3434");
wr.flush();
// Get the server response
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
StringBuilder sb = new StringBuilder();
String line = null;
// Read Server Response
while((line = reader.readLine()) != null)
{
// Append server response in string
sb.append(line + "\n");
}
}
catch(Exception ex)
{
}
}
}).start();
For a web api POST method accepting a single string parameter you can do:
[HttpPost]
public HttpResponseMessage GetLicense([FromBody]string data)
And then post the data from client like:
wr.write("=3434");
For multiple post parameters, create a model class in Web API:
public class DataModel {
public string data1 {get;set;}
public string data2 {get;set;}
}
Update api endpoint parameter type:
[HttpPost]
public HttpResponseMessage GetLicense([FromBody]DataModel dataModel)
Then post json string from client with content-type: "application/json"
{
"data1": "Data1 contents",
"data2": "Data2 contents"
}

How to configure route in Asp.Net WebApi to use action method name as a query parameter?

I want to invoke diffrent action methods based on the query string parameter, for example, webapi/mycontroller?action=getuser&id=10 should invoke mycontroller.getuser(10) action method and webapi/mycontroller?action=getallusers should invoke mycontroller.getallusers() action method. I tried to write the routing in the following way:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "webapi/{controller}?action={action}"
);
But this is not allowed, visual studio gives me the error The route URL cannot start with a '/' or '~' character and it cannot contain a '?' character.
So I've knocked something together that might help you get started
First create a route with custom handler
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}",
defaults: null,
constraints: null,
handler: new CustomHttpControllerDispatcher(config)
);
public class CustomHttpControllerDispatcher : HttpMessageHandler
{
private IHttpControllerSelector _controllerSelector;
private readonly HttpConfiguration _configuration;
public CustomHttpControllerDispatcher(HttpConfiguration configuration)
{
_configuration = configuration;
}
public HttpConfiguration Configuration
{
get { return _configuration; }
}
private IHttpControllerSelector ControllerSelector
{
get
{
if (_controllerSelector == null)
{
_controllerSelector = _configuration.Services.GetHttpControllerSelector();
}
return _controllerSelector;
}
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return SendAsyncInternal(request, cancellationToken);
}
private Task<HttpResponseMessage> SendAsyncInternal(HttpRequestMessage request, CancellationToken cancellationToken)
{
IHttpRouteData routeData = request.GetRouteData();
Contract.Assert(routeData != null);
HttpControllerDescriptor httpControllerDescriptor = ControllerSelector.SelectController(request);
IHttpController httpController = httpControllerDescriptor.CreateController(request);
foreach (var queryParam in request.GetQueryNameValuePairs())
{
routeData.Values.Add(queryParam.Key, queryParam.Value);
}
// Create context
HttpControllerContext controllerContext = new HttpControllerContext(_configuration, routeData, request);
controllerContext.Controller = httpController;
controllerContext.ControllerDescriptor = httpControllerDescriptor;
return httpController.ExecuteAsync(controllerContext, cancellationToken);
}
}
Then set your methods to get in the controller
public class MyController : ApiController
{
[HttpGet]
public IHttpActionResult GetUser([FromUri]int userId)
{
return Ok();
}
[HttpGet]
public IHttpActionResult DoSomething([FromUri]string test)
{
return Ok();
}
}
I've only tried with GET methods, POSTs may just work, but I haven't tested.

ASP.NET The requested resource does not support http method 'GET'

This should be very simple, but im notsure what is wrong.
Every time i try and access "http://localhost:50949/api/projects", i get the following error
The requested resource does not support http method 'GET'
WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
ApiController
private const string m_BaseUrl = "http://example.com/rest/api/2/";
private string m_Username = Properties.Settings.Default.username;
private string m_Password = Properties.Settings.Default.password;
// GET: api/Projects
[HttpGet]
public IEnumerable<string> Get(JiraResource resource, string argument = null, string data = null, string method = "GET")
{
string url = string.Format("{0}{1}/", m_BaseUrl, resource.ToString());
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.ContentType = "application/json";
request.Method = method;
string base64Credentials = GetEncodedCredentials();
request.Headers.Add("Authorization", "Basic " + base64Credentials);
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
string result = string.Empty;
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
result = reader.ReadToEnd();
}
return null;
}
private string GetEncodedCredentials()
{
string mergedCredentials = string.Format("{0}:{1}", m_Username, m_Password);
byte[] byteCredentials = UTF8Encoding.UTF8.GetBytes(mergedCredentials);
return Convert.ToBase64String(byteCredentials);
}
HTML/JS
<h2>Projects</h2>
<button id="projects" class="btn btn-info">List Projects</button>
#section scripts
{
<script>
$(document)
.ready(function() {
$("#projects")
.click(function(e) {
var button = $(e.target);
$.get("/api/projects/")
.done(function() {
alert("Got Projects");
})
.fail(function() {
alert("Something failed!");
});
});
});
</script>
}
If i do it through the UI i get "Something Failed"
In your method int the controller you still have to put the route attribute even if it is empty string:
[HttpGet]
[Route("")]
public IEnumerable<string> Get(JiraResource resource, string argument = null, string data = null, string method = "GET")
{
...
}

Web api interface works locally but not on Azure

My case is very similar to this question, but since he did not get an answer I thought I'd throw some more input.
Everything works fine locally (on the VS embedded server). When I deploy to Azure, I get a 404 error accompanied by "No type was found that matches the controller named...".
However, when I load the routedebugger module the mapping seems ok even on Azure.
What can I do to debug that problem?
Thanks,
Alex
Edit: my routes are created this way:
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
};
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Edit 2: Here my controller class
public class EmployeeController : ApiController
{
// GET api/<controller>
public IEnumerable<Employee> Get()
{
using (var context = new nws())
{
return context.Employees;
}
}
// GET api/<controller>/5
public Employee Get(int id)
{
using (var context = new nws())
{
return context.Employees.FirstOrDefault(e => e.ID == id);
}
}
// GET api/<controller>/getbyatid/5
public Employee GetByAtId(string id)
{
using (var context = new nws())
{
return context.Employees.FirstOrDefault(e => e.AtUserID == id);
}
}
// POST api/<controller>
public void Post([FromBody]string value)
{
}
// PUT api/<controller>/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/<controller>/5
public void Delete(int id)
{
}
// GET api/<controller>/timebank/5
public int? GetTimeBank(string id)
{
using (var context = new nws())
{
var employee = context.Employees.FirstOrDefault(e => e.AtUserID == id);
if (employee != null)
return employee.GetTimeBank();
return null;
}
}
}
Switch the order of routes and try again.
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
};

Posting T with RestSharp to MVC4 api server gives null object

I have this common CUser class
public class CUser
{
public String Username { get; set; }
public String Password { get; set; }
}
Then I have this code on my client that uses RestSharp
public void CreateUser()
{
RestRequest request = new RestRequest(Method.POST);
request.RequestFormat = DataFormat.Json;
request.Resource = "user/{cUser}";
CUser user = new CUser
{
Username = "Foo",
Password = "BarBaz"
};
request.AddParameter("cUser", user, ParameterType.UrlSegment);
client.PostAsync<int>(request, (response, handler) =>
{
System.Diagnostics.Debug.WriteLine(response.StatusDescription);
System.Diagnostics.Debug.WriteLine("id: " + response.Data);
});
}
And this http route in my global.asax
routes.MapHttpRoute(
name: "CreateUser",
routeTemplate: "api/{controller}/{cUser}",
defaults: new
{
controller = "User",
action = "CreateUser",
cUser = RouteParameter.Optional
});
And this servercode to handle it
public int CreateUser(CUser cUser)
{
User user = new User(cUser);
LoginManager manager = new LoginManager();
return manager.CreateUser(user);
}
But everytime I run it cUser's values are null (Username and Password) so do I need to do something to restsharp to make it serialize it properly?
I think this is what you want:
request.AddParameter("username", user.Username);
request.AddParameter("password", user.Password);
What you're doing would result in cUser.ToString() being substituted for the {cUser} placeholder.

Resources