How to force CreatedAtRoute generate location started from https? - asp.net-core-webapi

I do POST to my api
curl -X POST --header 'Content-Type: application/json'
--header 'Accept: application/json'
--header 'Authorization: Bearer some_token' -d '{ some_data }'
'https://here_is_url'
In response I get
{ ... "location": "http://here_is_right_url_except_http", "status": "201", ... }
But url in location header should be with https.
Incoming request hits balancer which is https, then requested goes as http.

Found two ways to solve this issue.
1) Override UrlHelper
public class HttpsUrlHelper : UrlHelper {
public HttpsUrlHelper(ActionContext actionContext)
: base(actionContext) {
}
protected override string GenerateUrl(string protocol, string host, VirtualPathData pathData, string fragment) {
return base.GenerateUrl("https", host, pathData, fragment);
}
}
public class ForcedHttpsUrlHelperFactory : IUrlHelperFactory {
public IUrlHelper GetUrlHelper(ActionContext context) {
return new HttpsUrlHelper(context);
}
}
In Startup.cs need to register it
services.AddSingleton<IUrlHelperFactory, ForcedHttpsUrlHelperFactory>();
2) Create new class for action Result. Also you will need to implement own CreatedAtRoute function which will return instance of HttpsCreatedAtRouteResult.
public class HttpsCreatedAtRouteResult : CreatedAtRouteResult {
public HttpsCreatedAtRouteResult(object routeValues, object value)
: base(routeValues, value) {
}
public HttpsCreatedAtRouteResult(string routeName, object routeValues, object value)
: base(routeName, routeValues, value) {
}
public override void OnFormatting(ActionContext context) {
base.OnFormatting(context);
var url = context.HttpContext.Response.Headers[HeaderNames.Location];
// do with url whatever you need
context.HttpContext.Response.Headers[HeaderNames.Location] = url;
}
}

Related

Static headers and dynamic headers issue in retrofit

I need help regarding the Java Retrofit request:
Scenario 1: I have added an interceptor having few static headers.
Scenario 2: While requesting API, sending few dynamic headers also.
When the request completes, I check request headers like below.
response.raw().request().headers()
where I can see the static headers but not the dynamic headers.
below is the code for Interceptor to set static headers:
public class AuthInterceptor implements Interceptor {
public AuthInterceptor() {
}
protected String authtoken;
public AuthInterceptor(String authtoken) {
defaultHeader();
this.authtoken = authtoken;
}
public void setAuthtoken(String authtoken) {
this.authtoken = authtoken;
}
private Headers.Builder defaultHeader() {
final String xUserAgent = Util.SDK_NAME + "/" + Util.SDK_VERSION;
return new Headers.Builder()
.add("X-User-Agent", xUserAgent)
.add("User-Agent", Util.defaultUserAgent())
.add("Content-Type", "application/json");
}
public Headers.Builder addHeader(#NotNull String key, #NotNull String value) {
defaultHeader().add(key, value);
return defaultHeader();
}
#NotNull
#Override
public Response intercept(Chain chain) throws IOException {
Request.Builder request = chain.request().newBuilder()
.headers(defaultHeader().build());
if (this.authtoken != null) {
request.addHeader("authtoken", this.authtoken);
}
return chain.proceed(request.build());
}
}
And Sending dynamic headers like below.
#POST("stacks")
Call<ResponseBody> create(
#Header("organization_uid") String orgUid,
#Body RequestBody body);
It looks to me like the problem is in your use of:
Request.Builder request = chain.request().newBuilder()
.headers(defaultHeader().build());
If you look at the documentation of the 'headers' method it states: Removes all headers on this builder and adds {#code headers}.
Just add each header with addHeader and you should be fine.

Error 405 not allowed on HttpPost in Asp.net core 3.1 web api

When i try to post the raw text with postman the server answer 405 not allowed. I try to add
app.UseCors(options => options.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
services.AddCors();
without any solution.
Whis is the code of the application:
[Route("api/[controller]")]
[ApiController]
public class VideoWallController : ControllerBase
{
// GET: api/<ValuesController>
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<ValuesController>/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
// POST api/<ValuesController>
[HttpPost]
public string prova([FromBody] VideoCallBack test)
{
return "true;";
}
[HttpPost]
public void teststring(string test)
{
}
// PUT api/<ValuesController>/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/<ValuesController>/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
If you want to reach that endpoint using the /teststring suffix, you need to put that in the endpoint template:
[HttpPost("teststring")]
public void teststring(string test)
{
}
Or, as #maghazade said, you can just reach the endpoint using the controller URL, without a suffix: https://localhost:44336/api/videowall
Also, the CORS settings are not needed to access the API using Postman. CORS is used only to configure the API access through a web application running on a web browser.
Firstly,as maghazade said,there are two endpoints for the post method.You can try to use [HttpPost("teststring")] as ferhrosa said.You can also add [action] to [Route("api/[controller]")],so that routes of actions will include their action names.If you add other actions with post method,the routes will not conflict anymore.
[Route("api/[controller]/[action]")]
[ApiController]
public class VideoWallController : ControllerBase
{
// GET: api/<ValuesController>/Get
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<ValuesController>/Get/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
// POST api/<ValuesController>/prova
[HttpPost]
public string prova()
{
return "true;";
}
// POST api/<ValuesController>/teststring
[HttpPost]
public void teststring([FromBody]string test)
{
}
// PUT api/<ValuesController>/Put/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/<ValuesController>/Delete/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
And if you want to pass string test from Body,you need to add [FromBody] and select JSON in postman.
result:

In ASP.Net Core MVC, what's "value" used for in ControllerBase.StatusCode()?

It's the second param. I don't see any documentation describing what happens when I return a StatusCode ObjectResult that has that param set.
//
// Summary:
// Creates a Microsoft.AspNetCore.Mvc.ObjectResult object by specifying a statusCode
// and value
//
// Parameters:
// statusCode:
// The status code to set on the response.
//
// value:
// The value to set on the Microsoft.AspNetCore.Mvc.ObjectResult.
//
// Returns:
// The created Microsoft.AspNetCore.Mvc.ObjectResult object for the response.
[NonAction]
public virtual ObjectResult StatusCode(int statusCode, object value);
The value will be the payload/body included in the response, formatted by the applicable media formatter.
For the code below and when using the application/json content-type, this will be
{ "a" : "foo", "b" : 1 }
public class Dto
{
public string A { get; set; }
public int B { get; set; }
}
public class MyController : Controller
{
[HttpGet]
public IActionResult MyAction()
{
var dto = new Dto { A = "foo", B = 1};
return StatusCode(200, dto);
}
}

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 deal with the result that i wanted in asp.net web api

I'm trying to write web api for app developers,and i want the api result like the sample below
When Exception:
{
"StatusCode": "0",
"Message": "There's exception when calling web api"
}
Normal: the Result in the json string was the return type in the web api action.
{
"StatusCode": "1",
"Message": "Action completed successful",
"Result": {}
}
If the action is:
public DemoController : ApiController
{
public class DemoModel
{
public string X {get;set;}
public int Y {get;set;}
}
[HttpGet]
public DemoModel GetModel(int id)
{
return new DemoModel() { X = "Demo return string" , Y = 1234};
}
}
The Json string should be the sample below when calling the action successfully.
{
"StatusCode": "1",
"Message": "Action completed successful",
"Result": {
"X": "Demo return string",
"Y": 1234
}
}
and when exception, should be :
{
"StatusCode": "0",
"Message": "There's exception when calling web api"
}
So,the app developers could see the return type details in the web api help page.
Is that easy to implement?and how to do (no detail,just logic,also detail is better.)
thanks for everyone !
You should create DelegatingHandler to wrapper your all response from server:
public class WrappingResponseHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
return BuildApiResponse(request, response);
}
private static HttpResponseMessage BuildApiResponse(HttpRequestMessage request, HttpResponseMessage response)
{
object result;
string message = null;
int status;
if (response.TryGetContentValue(out result) == false || response.IsSuccessStatusCode == false)
{
var error = result as HttpError;
if (error != null)
{
result = null;
}
message = "There's exception when calling web api";
status = 0;
}
else
{
message = "Action completed successful";
status = 1;
}
HttpResponseMessage newResponse = request.CreateResponse(response.StatusCode,
new ApiResponse() { Message = message, Result = result, StatusCode = status });
foreach (KeyValuePair<string, IEnumerable<string>> header in response.Headers)
{
newResponse.Headers.Add(header.Key, header.Value);
}
return newResponse;
}
public class ApiResponse
{
public int StatusCode { get; set; }
public string Message { get; set; }
public object Result { get; set; }
}
}
And add this handler in WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MessageHandlers.Add(new WrappingResponseHandler()); //here
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
And nothing would have to change and add controllers.
Using IHttpActionResult would really helpful esp if your thinking of app developers. It works very with Http response code like 200(Ok), 500(Internal Server Error), 404(Not Found) etc
Here is simple code example, where your getting product and returning appropriate response based on returns
public IHttpActionResult Get (int id)
{
Product product = _repository.Get (id);
if (product == null)
{
return NotFound(); // Returns a NotFoundResult
}
return Ok(product); // Returns an OkNegotiatedContentResult
}
More on this Action Results on Web Api 2, you can even write custom action result.
When app client consumes, it gets proper HTTP response code, any response object or message along.

Resources