GetRouteUrl returns null and RedirectToRoute throws "No matching route found for RedirectToRoute." - asp.net-routing

Page.GetRouteUrl("routename", new { blah = "blah" })
Returns null
Response.RedirectToRoute("routename", new { blah = "blah" })
throws InvalidOperationException("No matching route found for RedirectToRoute.")
I put a breakpoint and checked System.Web.Routing.RouteTable.Routes and my routes are there. I can use them without a hitch in my page code. What's the deal?

Check your route restrictions. I ran into the same problem before and it turned out that my route restrictions were incorrect.
Hope that helsps

Try:
Response.Redirect(string.Format("~/Blah.aspx/{0}", blah.ToString()));
Instead of:
Response.RedirectToRoute("routename", new { blah = "blah" })

Related

Access TempData within custom middleware

I have custom middleware that provides global error handling. If an exception is caught it should log the details with a reference number. I then want to redirect the user to an error page and only show the reference number. My research shows that TempData should be ideal for this but it only seems to be accessible from within a controller context. I tried adding the reference number to HttpContext.Items["ReferenceNumber"] = Guid.NewGuid();
But this value is lost through the redirect.
How can middleware pass information through a redirect? Do I just have to put the number in a querystring?
Inside the middleware class you need to add a reference to get access to the required interfaces. I have this middleware in a separate project and needed to add this NuGet package.
using Microsoft.AspNetCore.Mvc.ViewFeatures;
This then allows you to request the correct services within the middleware.
//get TempData handle
ITempDataDictionaryFactory factory = httpContext.RequestServices.GetService(typeof(ITempDataDictionaryFactory)) as ITempDataDictionaryFactory;
ITempDataDictionary tempData = factory.GetTempData(httpContext);
After you have ITempDataDictionary you can use it like you would use TempData within a controller.
//pass reference number to error controller
Guid ReferenceNum = Guid.NewGuid();
tempData["ReferenceNumber"] = ReferenceNum.ToString();
//log error details
logger.LogError(eventID, exception, ReferenceNum.ToString() + " - " + exception.Message);
Now when I get the the controller after a redirect I have no issues pulling out the reference number and using it in my view.
//read value in controller
string refNum = TempData["ReferenceNumber"] as string;
if (!string.IsNullOrEmpty(refNum))
ViewBag.ReferenceNumber = refNum;
#*display reference number if one was provided*#
#if (ViewBag.ReferenceNumber != null){<p>Reference Number: #ViewBag.ReferenceNumber</p>}
Once you put this all together, you give users a reference number that they can give you to help troubleshoot the problem. But, you are not passing back potentially sensitive error information which could be misused.
You can register an ITempDataProvider yourself and use it in your middleware. Here is a small sample I got working between two simple paths. If you are already using MVC the ITempDataProvider is probably already registered. The issue I faced was the path of the cookie that was written. It was /page1 so /page2 did not have access to the cookie. So I had to override the options as you can see in code below.
I hope this will help you :)
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IDataProtectionProvider>(s => DataProtectionProvider.Create("WebApplication2"));
services.Configure<CookieTempDataProviderOptions>(options =>
{
options.Path = "/";
});
services.AddSingleton<ITempDataProvider, CookieTempDataProvider>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ITempDataProvider tempDataProvider)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.Map("/page1", (app1) =>
{
app1.Run(async context =>
{
tempDataProvider.SaveTempData(context, new Dictionary<string, object> { ["Message"] = "Hello from page1 middleware" });
await context.Response.WriteAsync("Hello World! I'm page1");
});
});
app.Map("/page2", (app1) =>
{
app1.Run(async context =>
{
var loadTempData = tempDataProvider.LoadTempData(context);
await context.Response.WriteAsync("Hello World! I'm page2: Message from page1: " + loadTempData["Message"]);
});
});
}
This led me in the right direction: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state#cookie-based-tempdata-provider
Happy coding! :)

HTTP 400 bad request when using POST

I am getting HTTP 400 when I POST some JSON using RestSharp PCL.
When I send a string, it seems that the \" is included. Which it should not. This might be the reason why the POST does not work.
I am probably missing something that I need to fill in but please do help me to understand what I am missing.
Here is the code I am using
public async Task<bool> DoPost<T>(string endPoint, T content) where T : class
{
var body = JsonConvert.SerializeObject(content);
var request = new RestRequest(endPoint, Method.POST);
request.AddParameter("application/json", body, ParameterType.RequestBody);
try
{
var response = await _client.Execute(request, _cancellationToken.Token);
if (response.IsSuccess)
{
return true;
}
}
catch (Exception e)
{
throw new GTSWebServiceException(e.Message, e);
}
return false;
}
Have you checked this: How to POST request using RestSharp I know you are including the content type in the first argument but maybe you can play with RequestFormat? I doubt that's needed though. Also, have you checked whether your string does actually contain an escaped character like a double quote on it? If you are also seeing that slash on strings could it also be because you are debugging it? What do you receive in the payload coming through in the server that returns you the bad request?

servicestack - caching a service response using redis

I have a servicestack service which when called via the browser (restful) Url ex:http://localhost:1616/myproducts, it works fine.
The service method has RedisCaching enabled. So first time it hits the data repository and caches it for subsequent use.
My problem is when I try calling it from a c# client via Soap12ServiceClient. It returns the below error:
Error in line 1 position 183. Expecting element '<target response>'
from namespace 'http://schemas.datacontract.org/2004/07/<target namespace>'..
Encountered 'Element' with name 'base64Binary',
namespace 'http://schemas.microsoft.com/2003/10/Serialization/'.
Below is my Client code:
var endpointURI = "http://mydevelopmentapi.serverhostingservices.com:1616/";
using (IServiceClient client = new Soap12ServiceClient(endpointURI))
{
var request = new ProductRequest { Param1 = "xy23432"};
client.Send<ProductResponse>(request);
}
It seems that the soapwsdl used is giving the problem, but I appear to have used the defaults as generated by servicestack..
Any help will be much appreciated.
Update
I was able over come this error by changing the cache code at the service end:
Code that returned error at client end:
return RequestContext.ToOptimizedResultUsingCache(this.CacheClient, cacheKey,
() =>
new ProductResponse(){CreateDate = DateTime.UtcNow,
products = new productRepository().Getproducts(request)
});
Code that works now:
var result = this.CacheClient.Get<ProductResponse>(cacheKey);
if (result == null)
{
this.CacheClient.Set<ProductResponse>(cacheKey, productResult);
result = productResult;
}
return result;
But I am still curious to know why the first method (RequestContext.ToOptimizedResultUsingCache) returned error at c# client?
But I am still curious to know why the first method (RequestContext.ToOptimizedResultUsingCache) returned error at c# client?
From what I can tell, the ToOptimizedResultUsingCache is trying to pull a specific format (xml, html, json, etc) out of the cache based on the RequestContext's ResponseContentType (see code here and here). When using the Soap12ServiceClient the ResponseContentType is text/html (not sure if this is correct/intentional within ServiceStack). So what ToOptimizedResultUsingCache is pulling out of the cache is a string of html. The html string is being returned to the Soap12ServiceClient and causing an exception.
By pulling directly out of the cache you are bypassing ToOptimizedResultUsingCache's 'format check' and returning something the Soap12ServiceClient can handle.
** If you are using Redis and creating your key with UrnId.Create method you should see a key like urn:ProductResponse:{yourkey}.html
Thanks for your response paaschpa.
I revisited the code and I was able to fix it. Since your response gave me the direction, I have accepted your answer. Below is my fix.
I moved the return statement from RequestContext to the response DTO.
Code which throws error when used via c# client (code was returning entire requestcontext):
return RequestContext.ToOptimizedResultUsingCache(this.CacheClient, cacheKey,
() =>
new ProductResponse(){CreateDate = DateTime.UtcNow,
products = new productRepository().Getproducts(request)
});
Fixed Code (return moved to response DTO):
RequestContext.ToOptimizedResultUsingCache(this.CacheClient, cacheKey,
() => {
return new ProductResponse(){CreateDate = DateTime.UtcNow,
products = new productRepository().Getproducts(request)
}
});

How to retrieve message from WEB API?

I created some web apis and when an error happens the api returns HttpResponseMessage that is created with CreateErrorResponse message. Something like this:
return Request.CreateErrorResponse(
HttpStatusCode.NotFound, "Failed to find customer.");
My problem is that I cannot figure out how to retrieve the message (in this case "Failed to find customer.") in consumer application.
Here's a sample of the consumer:
private static void GetCustomer()
{
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
string data =
"{\"LastName\": \"Test\", \"FirstName\": \"Test\"";
var content = new StringContent(data, Encoding.UTF8, "application/json");
var httpResponseMessage =
client.PostAsync(
new Uri("http://localhost:55202/api/Customer/Find"),
content).Result;
if (httpResponseMessage.IsSuccessStatusCode)
{
var cust = httpResponseMessage.Content.
ReadAsAsync<IEnumerable<CustomerMobil>>().Result;
}
}
Any help is greatly appreciated.
Make sure you set the accept and or content type appropriately (possible source of 500 errors on parsing the request content):
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
content.Headers.ContentType = new MediaTypeWithQualityHeaderValue("application/json");
Then you could just do:
var errorMessage = response.Content.ReadAsStringAsync().Result;
That's all on the client of course. WebApi should handle the formatting of the content appropriately based on the accept and/or content type. Curious, you might also be able to throw new HttpResponseException("Failed to find customer.", HttpStatusCode.NotFound);
One way to get the message is to do:
((ObjectContent)httpResponseMessage.Content).Value
This will give you a dictionary that contains also the Message.
UPDATE
Refer to the official page:
http://msdn.microsoft.com/en-us/library/jj127065(v=vs.108).aspx
You have to vary the way you're reading the successful response and the error response as one is obviously in your case StreamContent, and the other should be ObjectContent.
UPDATE 2
Have you tried doing it this way ?
if (httpResponseMessage.IsSuccessStatusCode)
{
var cust = httpResponseMessage.Content.
ReadAsAsync<IEnumerable<CustomerMobil>>().Result;
}
else
{
var content = httpResponseMessage.Content as ObjectContent;
if (content != null)
{
// do something with the content
var error = content.Value;
}
else
{
Console.WriteLine("content was of type ", (httpResponseMessage.Content).GetType());
}
}
FINAL UPDATE (hopefully...)
OK, now I understand it - just try doing this instead:
httpResponseMessage.Content.ReadAsAsync<HttpError>().Result;
This is an option to get the message from the error response that avoids making an ...Async().Result() type of call.
((HttpError)((ObjectContent<HttpError>)response.Content).Value).Message
You should make sure that response.Content is of type ObjectContent<HttpError> first though.
It should be in HttpResponseMessage.ReasonPhrase. If that sounds like a bit of a strange name, it's just because that is the way it is named in the HTTP specification http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html
OK this is hilarious, but using QuickWatch I came up with this elegant solution:
(new System.Collections.Generic.Mscorlib_DictionaryDebugView(((System.Web.Http.HttpError)(((System.Net.Http.ObjectContent)(httpResponseMessage.Content)).Value)))).Items[0].Value
That is super readable!

Method not allowed in WCF Rest service

[OperationContract]
[WebInvoke(UriTemplate = "createinvoice", Method = "POST", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
public Invoice CreateInvoice(string instance)
{
// TODO: Add the new instance of SampleItem to the collection
try
{
string icode = instance;
//decimal paid = instance.AmountPaid;
return new Invoice() {InvoiceCode = icode };
}
catch( Exception )
{
throw new NotImplementedException();
}
}
Everytime i run it on the browser it says:
Method not allowed. Please see the service help page for constructing valid requests to the service.
Any ideas? Also when i go and do this on the browser. it says Endpoint not found. (Mobile) is a virtual directory while (POS) is a registered route for service1.cs
Posting to the URL from browser will not work. You need your custom code or use fiddler(use Composer and select POST) Another link with solution.
The answer is under "Everytime i run it on the browser it says:"
Your web browser request is a GET request .You can change WebInvoke to WebGet and remove POST Method attribute or build a POST request using a tool.

Resources