ApiController HttpPost parameters - asp.net

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"
}

Related

Call a rest web api in asp.net and receive error

Hi I want to call a rest Web Api and I use asp.net MVC+Web Api.
I write a get Token Method like below :
public TokenViewModel GetToken()
{
//string Result = string.Empty;
TokenViewModel token = null;
string baseAddress = "http://$$$$$$$$$$/api/security/login";
using (HttpClient client = new HttpClient())
{
try
{
var url = new Uri(baseAddress);
MultipartFormDataContent form = new MultipartFormDataContent();
Dictionary<string, string> parameters = new Dictionary<string, string>();
parameters.Add("UserName", "###");
parameters.Add("Password", "$$$");
HttpContent DictionaryItems = new FormUrlEncodedContent(parameters);
form.Add(DictionaryItems, "model");
var response = client.PostAsync(url.ToString(), form, System.Threading.CancellationToken.None);
if (response.Result.StatusCode == System.Net.HttpStatusCode.OK)
{
//Get body
var bodyRes = response.Result.Content.ReadAsStringAsync().Result;
token = JsonConvert.DeserializeObject<TokenViewModel>(bodyRes);
//Get Header
// var headers = response.Result.Headers.GetValues("appToken");
}
else
{
var a = response.Result.Content.ReadAsStringAsync().Result;
}
}
catch (Exception ex)
{
}
return token;
}
}
And also webController:
namespace WebAPI.Controllers
{
public class WebApiController : ApiController
{
private readonly GetToken_BLL _tokenService;
public WebApiController(GetToken_BLL tokenService)
{
_tokenService = tokenService;
}
public object Verfiybll { get; private set; }
public class stcAPIMessage
{
public string Message { get; set; }
public HttpStatusCode StatusCode { get; set; }
}
[HttpPost]
[Route("api/Token")]
public IHttpActionResult Token()
{
stcAPIMessage message = new stcAPIMessage();
GetToken_BLL tokenbll = new GetToken_BLL();
var result = tokenbll.GetToken();
if (result == null)
{
message.Message = "error in recieveing token";
message.StatusCode = HttpStatusCode.BadRequest;
return Content(message.StatusCode, message.Message);
}
else if (string.IsNullOrEmpty(result.Token))
{
message.Message = "Error";
message.StatusCode = HttpStatusCode.BadRequest;
return Content(message.StatusCode, message.Message);
}
return Ok(result);
}
}
}
When I run the program it throw out error:
An error occurred when trying to create a controller of type 'Web ApiController'.
Make sure that the controller has a parameter less public constructor.
System. Invalid Operation Exception Type 'WebAPI.Controllers.
Web ApiController' does not have a default constructor
System.
The parameter less constructor error is common in ASP.NET web applications that use dependency injection.
I have noticed there is a constructor parameter being used:
GetToken_BLL _tokenService
Use a dependency injection resolver for the type GetToken_BLL so that the parameter _tokenService can be instantiated.

Web API Post giving me error

I have a web API application. I'm supposed to do a post to an endpoint. When l tried my API controller in postman, l get the error message "
Requested resource does not support HTTP 'POST'
I'm new to Web API so any help and suggestions are welcomed.
This is my model class:
namespace Products.Models
{
public class Prouct
{
public string ProductID { get; set; }
public string ProductName { get; set; }
public string ProductPrice { get; set; }
public string VoucherID { get; set; }
}
}
Here is my controller class
[RoutePrefix("api/products")]
public class ProductsController : ApiController
{
static HttpClient client = new HttpClient();
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
public string Get(int id)
{
return "value";
}
[Route("products")]
public async Task PostAsync(string ProductID, string ProductName, string ProductPrice,
string VoucherID)
{
Products p = new Products();
p.ProductID = ProductID;
p.ProductName = ProductName;
p.ProductPrice = ProductPrice;
p.VoucherID = VoucherID;
var client = new HttpClient { BaseAddress = new
Uri("http://localhost:51613/") };
var response = await
client.PostAsJsonAsync("api/products",
p);
if (response.IsSuccessStatusCode)
{
}
public void Put(int id, [FromBody]string value)
{
}
public void Delete(int id)
{
}
You need to specify HttpPost on PostAsync method. by default, it is [HttpGet].
[HttpPost]
[Route("products")]
public async Task PostAsync(string ProductID, string ProductName, string ProductPrice, string VoucherID)
{
// implementation
}
Looks like you're stuck in a loop. Why does the PostAsync method call itself after having been invoked? This will result in an endless request loop.
var client = new HttpClient { BaseAddress = new Uri("http://localhost:51613/") };
This is not related to the fact that the [HttpPost] attribute is required however.
Please observe that you are supposed to use [FromBody] . Also inside Postman (image attached) you have to choose "Raw" data with the product json with type as JSON(application.json).
[HttpPost]
[Route("products")]
public async Task PostAsync([FromBody] Products p)
{
var client = new HttpClient
{
BaseAddress = new
Uri("http://localhost:51613/")
};
var response = await
client.PostAsJsonAsync("api/products",
p);
if (response.IsSuccessStatusCode)
{
}
}

.NET Core API + MVC Core Client supported by RestSharp

I have created .NET Core Web Api with JWT authentication. Now, I am in the middle of creating web app using MVC Core. In MVC project I have API client wrapper:
Interface:
public interface IWebApiService
{
Task<T> AuthenticateAsync<T>(string userName);
Task<T> GetAsync<T>(string action, string authToken);
Task PutAsync<T>(string action, T data, string authToken);
Task PostAsync<T>(string action, T data, string authToken);
}
Implementation:
public class WebApiService : IWebApiService
{
private readonly WebApiSettings _webApiSettings;
public WebApiService(WebApiSettings webApiSettings)
{
_webApiSettings = webApiSettings;
}
public async Task<T> AuthenticateAsync<T>(string userName)
{
var client = new RestClient(_webApiSettings.BaseUri);
var request = new RestRequest("/Login", Method.POST)
{
RequestFormat = DataFormat.Json
};
request.AddBody(new { UserName = userName });
var response = await client.ExecuteTaskAsync(request);
if (response.IsSuccessful)
{
return JsonConvert.DeserializeObject<T>(response.Content);
}
throw new ApiException(response.StatusCode.ToString(), response.ErrorMessage);
}
public async Task<T> GetAsync<T>(string action, string authToken)
{
var client = new RestClient(_webApiSettings.BaseUri);
var request = new RestRequest(action, Method.GET)
{
RequestFormat = DataFormat.Json
};
request.AddHeader("Authorization", $"Bearer {authToken}");
var response = await client.ExecuteTaskAsync(request);
if (response.IsSuccessful)
{
return JsonConvert.DeserializeObject<T>(response.Content);
}
throw new ApiException(response.StatusCode.ToString(), response.ErrorMessage);
}
public Task PutAsync<T>(string action, T data, string authToken)
{
// TODO
throw new NotImplementedException();
}
public Task PostAsync<T>(string action, T data, string authToken)
{
// TODO
throw new NotImplementedException();
}
}
MVC Login Controller:
public class LoginController : Controller
{
private readonly IWebApiService _webApiService;
public LoginController(IWebApiService webApiService)
{
_webApiService = webApiService;
}
public async Task<IActionResult> Get(string redirectUrl)
{
var user = User.Identity.Name;
if(user == null)
throw new WebInterfaceException("Invalid username.");
var response = await _webApiService.AuthenticateAsync<JwtToken>(user);
HttpContext.Session.SetObjectAsJson("Token", response);
return Redirect(redirectUrl ?? "/Home/Index");
}
}
I keep JWT object in session as I didn't find better solution for storing tokens in MVC Core.
Below example controller:
public class ExampleController : Controller
{
private readonly IWebApiService _webApiService;
public ExampleController(IWebApiService webApiService)
{
_webApiService = webApiService;
}
[HttpGet]
public async Task<IActionResult> Browse()
{
var jwtToken = HttpContext.Session.GetObjectFromJson<JwtToken>("Token");
if (jwtToken == null)
{
return RedirectToAction("Get", "Login", new { redirectUrl = Request.Path});
}
var response = await _webApiService.GetAsync<IEnumerable<ExampleBrowseViewModel>>("/Examples", jwtToken.Token);
return Json(response);
}
}
My problem is that in every controller action I will have to check if token is not null. If it's null, I am redirecting to Login page where I am retrieving token from API and redirecting to originally requested page. I would like to have some token handler where so I will not repeat the same code over and over. Additionally in my JWT object I have token expiration time and I would like to refresh it once it will expire so user could continue sending requests to API.
Can you give me few advises so I could accomplish this?

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.

Return custom HTTP code from ActionFilterAttribute

I use the code below to throttle my ASP.NET Web Api:
public class Throttle : ActionFilterAttribute
{
public override async Task OnActionExecutingAsync(HttpActionContext context, CancellationToken cancellationToken)
{
// ...
if (throttle)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Conflict));
}
}
}
However, I cannot return error code 429, because it's not in HttpStatusCode enum. Is there a way to return a custom error code?
I found this over here.
var response = new HttpResponseMessage
{
StatusCode = (HttpStatusCode)429,
ReasonPhrase = "Too Many Requests",
Content = new StringContent(string.Format(CultureInfo.InvariantCulture, "Rate limit reached. Reset in {0} seconds.", data.ResetSeconds))
};
response.Headers.Add("Retry-After", data.ResetSeconds.ToString(CultureInfo.InvariantCulture));
actionContext.Response = response;
Hope this helps
This is what I did based on another response on StackOverflow.
Create Class (in controller file worked for me)
public class TooManyRequests : IHttpActionResult
{
public TooManyRequests()
{
}
public TooManyRequests(string message)
{
Message = message;
}
public string Message { get; private set; }
public HttpResponseMessage Execute()
{
HttpResponseMessage response = new HttpResponseMessage((HttpStatusCode)429);
if (!string.IsNullOrEmpty(Message))
{
response.Content = new StringContent(Message); // Put the message in the response body (text/plain content).
}
return response;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(Execute());
}
}
Use in controller
public IHttpActionResult Get()
{
// with message
return new TooManyRequests("Limited to 5 request per day. Come back tomorrow.");
// without message
// return new TooManyRequests();
}

Resources