I am replacing a Project built using ASP.NET WebForms and I need to replace the .ashx Generic Handlers - but I need to keep the page names so an app that has these URIs hardcoded does not require updating.
I know how to deal with the logic, that is not the problem. The problem is that these pages are referenced by an app that I do not want to update, so I need to be able to use URIs that point to pages ending in .ashx.
I have tried everything I can think of. I had hoped to just use the #page directive as shown below:
#page "/mygenerichandler.ashx"
Unfortunately, that does not work. If it did, I would be all set.
I have seen pages telling me to handle the .ashx as a sort of parameter:
#page "/mygenerichandler/{ashx}" (and variations of this, none work)
This does not work.
I have tried just naming the pages with the .ashx extension. This does not work.
I do not want to have to update the apps that have the URLs embedded in them, but it is looking more and more like that is my only option.
Is there any way to accept a page request in Blazor to a page that is named something like "mygenerichandler.ashx"?
I figured it out. I am using the Middleware Pattern, and it turns out that this will execute early in the pipeline and allow me to inspect the URL for the .ashx extension and then route accordingly. I should be able to return a response from this point - I still have to implement that code, but it is not directly germane to this question so I will not cover it here.
public class HandlerTrapper
{
private readonly RequestDelegate _next;
public string? AccountID { get; set; }
public HandlerTrapper(RequestDelegate next)
{
_next = next;
}
public Task Invoke(HttpContext httpContext) //, [FromQuery(Name = "AccountID")] string accountId
{
string? page = httpContext.Request.Path.Value?.Replace(#"/", "");
Console.WriteLine("Page Name is {0}, AccountID = {1}", page, AccountID);
if(page==null || !page.Contains(".ashx"))
return _next(httpContext);
AccountID = httpContext.Request.Query["AccountID"];
switch (page)
{
case "GetAmzRefreshToken":
break;
}
return _next(httpContext);
}
private
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class HandlerTrapperExtensions
{
public static IApplicationBuilder UseHandlerTrapper(this IApplicationBuilder builder)
{
return builder.UseMiddleware<HandlerTrapper>();
}
}
This is called as shown here in Program.cs:
app.UseHandlerTrapper();
I am pretty sure I can just return a Response from here and after implementing the code that does the work based on the incoming legacy page name, I should have a replacement for my .ashx Generic Handlers.
There is an even better solution which I implemented in my code. The WebApplication class has a UrlReWriter method that solves this problem quite elegantly when used in conjunction with the Controller Routing.
I added these lines to my Program.cs file - I placed them before the UseHttpRedirection and the UseRouting calls.:
RewriteOptions urlOptions = new RewriteOptions().AddRewrite(#"^(.*).ashx$", "api/$1", false);
urlOptions.AddRewrite(#"^(.*).inf$", "api/ComputerInfo", false);
app.UseRewriter(urlOptions);
That resolved the issue for both of the file type patterns I needed to handle, and I can add more if need be.
I was reading the link: http://www.gwtproject.org/doc/latest/tutorial/JSON.html and found that the JSNI and overlay types are used to decode the JSON data from the server side.
// Send request to server and catch any errors.
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url);
try {
Request request = builder.sendRequest(null, new RequestCallback() {
public void onError(Request request, Throwable exception) {
displayError("Couldn't retrieve JSON");
}
public void onResponseReceived(Request request, Response response) {
if (200 == response.getStatusCode()) {
updateTable(JsonUtils.<JsArray<StockData>>safeEval(response.getText()));
} else {
displayError("Couldn't retrieve JSON (" + response.getStatusText()
+ ")");
}
}
});
} catch (RequestException e) {
displayError("Couldn't retrieve JSON");
}
Why does it use the JsonUtils.>safeEval(response.getText()) to decode the JSON data? Does it have to do so? Is this the only way to receive the JSON data when using GWT?
No this is not the only way. However, JsonUtils.safeEval will either make use of the browser's built-in (i.e. native, so assumed to be the fastest way to do it) JSON.parse() (see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse for more details), which will turn a JSON string into a JavaScript Object.
In turn, overlay types let you write Java code which directly maps to a JavaScript object, making it very simple and easy to talk about the underlying JS data, from your normal Java code.
Of course there are other options as well, off the top of my head, you can use JSONObject class in the com.google.gwt.json.client package - this still does the JSON.parse() call, but then exposes data through a map- and list-like structure for your Java code. Additionally, AutoBeans make it possible to declare interfaces that describe the structure of the data, and read from the JSON string (or a JavaScript object) into those interfaces - it lets your code behave more like Java with built-in collections and such, at the cost of some performance to translate back and forth.
This question's answers are a community effort. Edit existing answers to improve this post. It is not currently accepting new answers or interactions.
Using the newer ASP.NET Web API, in Chrome I am seeing XML - how can I change it to request JSON so I can view it in the browser? I do believe it is just part of the request headers, am I correct in that?
Note: Read the comments of this answer, it can produce a XSS Vulnerability if you are using the default error handing of WebAPI
I just add the following in App_Start / WebApiConfig.cs class in my MVC Web API project.
config.Formatters.JsonFormatter.SupportedMediaTypes
.Add(new MediaTypeHeaderValue("text/html") );
That makes sure you get JSON on most queries, but you can get XML when you send text/xml.
If you need to have the response Content-Type as application/json please check Todd's answer below.
NameSpace is using System.Net.Http.Headers.
If you do this in the WebApiConfig you will get JSON by default, but it will still allow you to return XML if you pass text/xml as the request Accept header.
Note: This removes the support for application/xml
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
}
}
If you are not using the MVC project type and therefore did not have this class to begin with, see this answer for details on how to incorporate it.
Using RequestHeaderMapping works even better, because it also sets the Content-Type = application/json in the response header, which allows Firefox (with JSONView add-on) to format the response as JSON.
GlobalConfiguration.Configuration.Formatters.JsonFormatter.MediaTypeMappings
.Add(new System.Net.Http.Formatting.RequestHeaderMapping("Accept",
"text/html",
StringComparison.InvariantCultureIgnoreCase,
true,
"application/json"));
I like Felipe Leusin's approach best - make sure browsers get JSON without compromising content negotiation from clients that actually want XML. The only missing piece for me was that the response headers still contained content-type: text/html. Why was that a problem? Because I use the JSON Formatter Chrome extension, which inspects content-type, and I don't get the pretty formatting I'm used to. I fixed that with a simple custom formatter that accepts text/html requests and returns application/json responses:
public class BrowserJsonFormatter : JsonMediaTypeFormatter
{
public BrowserJsonFormatter() {
this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
this.SerializerSettings.Formatting = Formatting.Indented;
}
public override void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType) {
base.SetDefaultContentHeaders(type, headers, mediaType);
headers.ContentType = new MediaTypeHeaderValue("application/json");
}
}
Register like so:
config.Formatters.Add(new BrowserJsonFormatter());
MVC4 Quick Tip #3–Removing the XML Formatter from ASP.Net Web API
In Global.asax add the line:
GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
like so:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
BundleTable.Bundles.RegisterTemplateBundles();
GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
}
In the WebApiConfig.cs, add to the end of the Register function:
// Remove the XML formatter
config.Formatters.Remove(config.Formatters.XmlFormatter);
Source.
In the Global.asax I am using the code below. My URI to get JSON is http://www.digantakumar.com/api/values?json=true
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
GlobalConfiguration.Configuration.Formatters.JsonFormatter.MediaTypeMappings.Add(new QueryStringMapping("json", "true", "application/json"));
}
Have a look at content negotiation in the WebAPI. These (Part 1 & Part 2) wonderfully detailed and thorough blog posts explain how it works.
In short, you are right, and just need to set the Accept or Content-Type request headers. Given your Action isn't coded to return a specific format, you can set Accept: application/json.
As the question is Chrome-specific, you can get the Postman extension which allows you to set the request content type.
This code makes json my default and allows me to use the XML format as well. I'll just append the xml=true.
GlobalConfiguration.Configuration.Formatters.XmlFormatter.MediaTypeMappings.Add(new QueryStringMapping("xml", "true", "application/xml"));
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
Thanks everyone!
One quick option is to use the MediaTypeMapping specialization. Here is an example of using QueryStringMapping in the Application_Start event:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.MediaTypeMappings.Add(new QueryStringMapping("a", "b", "application/json"));
Now whenever the url contains the querystring ?a=b in this case, Json response will be shown in the browser.
Don't use your browser to test your API.
Instead, try to use an HTTP client that allows you to specify your request, such as CURL, or even Fiddler.
The problem with this issue is in the client, not in the API. The web API behaves correctly, according to the browser's request.
Most of the above answers makes perfect sense.
Since you are seeing data being formatted in XML format ,that means XML formatter is applied,SO you can see JSON format just by removing the XMLFormatter from the HttpConfiguration parameter like
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.Remove(config.Formatters.XmlFormatter);
config.EnableSystemDiagnosticsTracing();
}
since JSON is the default format
Returning the correct format is done by the media-type formatter.
As others mentioned, you can do this in the WebApiConfig class:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
...
// Configure Web API to return JSON
config.Formatters.JsonFormatter
.SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("text/html"));
...
}
}
For more, check:
Media Formatters in ASP.NET Web API 2.
Content Negotiation in ASP.NET Web API.
In case your actions are returning XML (which is the case by default) and you need just a specific method to return JSON, you can then use an ActionFilterAttribute and apply it to that specific action.
Filter attribute:
public class JsonOutputAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
ObjectContent content = actionExecutedContext.Response.Content as ObjectContent;
var value = content.Value;
Type targetType = actionExecutedContext.Response.Content.GetType().GetGenericArguments()[0];
var httpResponseMsg = new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
RequestMessage = actionExecutedContext.Request,
Content = new ObjectContent(targetType, value, new JsonMediaTypeFormatter(), (string)null)
};
actionExecutedContext.Response = httpResponseMsg;
base.OnActionExecuted(actionExecutedContext);
}
}
Applying to action:
[JsonOutput]
public IEnumerable<Person> GetPersons()
{
return _repository.AllPersons(); // the returned output will be in JSON
}
Note that you can omit the word Attribute on the action decoration and use just [JsonOutput] instead of [JsonOutputAttribute].
I used a global action filter to remove Accept: application/xml when the User-Agent header contains "Chrome":
internal class RemoveXmlForGoogleChromeFilter : IActionFilter
{
public bool AllowMultiple
{
get { return false; }
}
public async Task<HttpResponseMessage> ExecuteActionFilterAsync(
HttpActionContext actionContext,
CancellationToken cancellationToken,
Func<Task<HttpResponseMessage>> continuation)
{
var userAgent = actionContext.Request.Headers.UserAgent.ToString();
if (userAgent.Contains("Chrome"))
{
var acceptHeaders = actionContext.Request.Headers.Accept;
var header =
acceptHeaders.SingleOrDefault(
x => x.MediaType.Contains("application/xml"));
acceptHeaders.Remove(header);
}
return await continuation();
}
}
Seems to work.
In the latest version of ASP.net WebApi 2, under WebApiConfig.cs, this will work:
config.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
config.Formatters.Add(GlobalConfiguration.Configuration.Formatters.JsonFormatter);
I found the Chrome app "Advanced REST Client" excellent to work with REST services. You can set the Content-Type to application/json among other things:
Advanced REST client
config.Formatters.Remove(config.Formatters.XmlFormatter);
It's unclear to me why there is all of this complexity in the answer. Sure there are lots of ways you can do this, with QueryStrings, headers and options... but what I believe to be the best practice is simple. You request a plain URL (ex: http://yourstartup.com/api/cars) and in return you get JSON. You get JSON with the proper response header:
Content-Type: application/json
In looking for an answer to this very same question, I found this thread, and had to keep going because this accepted answer doesn't work exactly. I did find an answer which I feel is just too simple not to be the best one:
Set the default WebAPI formatter
I'll add my tip here as well.
WebApiConfig.cs
namespace com.yourstartup
{
using ...;
using System.Net.Http.Formatting;
...
config.Formatters.Clear(); //because there are defaults of XML..
config.Formatters.Add(new JsonMediaTypeFormatter());
}
I do have a question of where the defaults (at least the ones I am seeing) come from. Are they .NET defaults, or perhaps created somewhere else (by someone else on my project). Anways, hope this helps.
You can use as below:
GlobalConfiguration.Configuration.Formatters.Clear();
GlobalConfiguration.Configuration.Formatters.Add(new JsonMediaTypeFormatter());
Here is a solution similar to jayson.centeno's and other answers, but using the built-in extension from System.Net.Http.Formatting.
public static void Register(HttpConfiguration config)
{
// add support for the 'format' query param
// cref: http://blogs.msdn.com/b/hongyes/archive/2012/09/02/support-format-in-asp-net-web-api.aspx
config.Formatters.JsonFormatter.AddQueryStringMapping("$format", "json", "application/json");
config.Formatters.XmlFormatter.AddQueryStringMapping("$format", "xml", "application/xml");
// ... additional configuration
}
The solution was primarily geared toward supporting $format for OData in the early releases of WebApi, but it also applies to the non-OData implementation, and returns the
Content-Type: application/json; charset=utf-8 header in the response.
It allows you to tack &$format=json or &$format=xml to the end of your uri when testing with a browser. It does not interfere with other expected behavior when using a non-browser client where you can set your own headers.
Just add those two line of code on your WebApiConfig class
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
//add this two line
config.Formatters.Clear();
config.Formatters.Add(new JsonMediaTypeFormatter());
............................
}
}
You just change the App_Start/WebApiConfig.cs like this:
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
//Below formatter is used for returning the Json result.
var appXmlType = config.Formatters.XmlFormatter.SupportedMediaTypes.FirstOrDefault(t => t.MediaType == "application/xml");
config.Formatters.XmlFormatter.SupportedMediaTypes.Remove(appXmlType);
//Default route
config.Routes.MapHttpRoute(
name: "ApiControllerOnly",
routeTemplate: "api/{controller}"
);
}
Some time has passed since this question was asked (and answered) but another option is to override the Accept header on the server during request processing using a MessageHandler as below:
public class ForceableContentTypeDelegationHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var someOtherCondition = false;
var accHeader = request.Headers.GetValues("Accept").FirstOrDefault();
if (someOtherCondition && accHeader.Contains("application/xml"))
{
request.Headers.Remove("Accept");
request.Headers.Add("Accept", "application/json");
}
return await base.SendAsync(request, cancellationToken);
}
}
Where someOtherCondition can be anything including browser type, etc. This would be for conditional cases where only sometimes do we want to override the default content negotiation. Otherwise as per other answers, you would simply remove an unnecessary formatter from the configuration.
You'll need to register it of course. You can either do this globally:
public static void Register(HttpConfiguration config) {
config.MessageHandlers.Add(new ForceableContentTypeDelegationHandler());
}
or on a route by route basis:
config.Routes.MapHttpRoute(
name: "SpecialContentRoute",
routeTemplate: "api/someUrlThatNeedsSpecialTreatment/{id}",
defaults: new { controller = "SpecialTreatment" id = RouteParameter.Optional },
constraints: null,
handler: new ForceableContentTypeDelegationHandler()
);
And since this is a message handler it will run on both the request and response ends of the pipeline much like an HttpModule. So you could easily acknowledge the override with a custom header:
public class ForceableContentTypeDelegationHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var wasForced = false;
var someOtherCondition = false;
var accHeader = request.Headers.GetValues("Accept").FirstOrDefault();
if (someOtherCondition && accHeader.Contains("application/xml"))
{
request.Headers.Remove("Accept");
request.Headers.Add("Accept", "application/json");
wasForced = true;
}
var response = await base.SendAsync(request, cancellationToken);
if (wasForced){
response.Headers.Add("X-ForcedContent", "We overrode your content prefs, sorry");
}
return response;
}
}
Here is the easiest way that I have used in my applications. Add given below 3 lines of code in App_Start\WebApiConfig.cs in the Register function:
var formatters = GlobalConfiguration.Configuration.Formatters;
formatters.Remove(formatters.XmlFormatter);
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
Asp.net web API will automatically serialize your returning object to JSON and as the application/json is added in the header so the browser or the receiver will understand that you are returning JSON result.
From MSDN Building a Single Page Application with ASP.NET and AngularJS (about 41 mins in).
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// ... possible routing etc.
// Setup to return json and camelcase it!
var formatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
formatter.SerializerSettings.ContractResolver =
new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
}
It should be current, I tried it and it worked.
Using Felipe Leusin's answer for years, after a recent update of core libraries and of Json.Net, I ran into a System.MissingMethodException:SupportedMediaTypes.
The solution in my case, hopefully helpful to others experiencing the same unexpected exception, is to install System.Net.Http. NuGet apparently removes it in some circumstances. After a manual installation, the issue was resolved.
WebApiConfig is the place where you can configure whether you want to output in json or xml. By default, it is xml. In the register function, we can use HttpConfiguration Formatters to format the output.
System.Net.Http.Headers => MediaTypeHeaderValue("text/html") is required to get the output in the json format.
I'm astonished to see so many replies requiring coding to change a single use case (GET) in one API instead of using a proper tool what has to be installed once and can be used for any API (own or 3rd party) and all use cases.
So the good answer is:
If you only want to request json or other content type install Requestly or a similar tool and modify the Accept header.
If you want to use POST too and have nicely formatted json, xml, etc. use a proper API testing extension like Postman or ARC.