NancyFx per module or per route serialization - c#

I use NancyFx, I've tried to find the answer prior to question.
I have a single return type per module, but in different URL routes I need to serialize it differently, just specific properties.
If I wire serialization in Nancy pipeline, it touches all routes.
Is it possible to customize serialization per route or module without copy pasting the same type in different namespaces?

Not certain what to specifically recommend without better understanding the requirements.
Consider implementing a response processor.
In your implementation of CanProcess you can examine the NancyContext to evaluate your rule for which serialization scheme to use.
Example:
public ProcessorMatch CanProcess(MediaRange requestedMediaRange, dynamic model, NancyContext context)
{
return context.Request.Path != "/" &&
!context.Request.Path.StartsWith("/someroute/") &&
!context.Request.Path.StartsWith("/someotherroute") &&
!context.Request.Path.StartsWith("/login")
? new ProcessorMatch
{
ModelResult = MatchResult.DontCare,
RequestedContentTypeResult = MatchResult.ExactMatch
}
: new ProcessorMatch
{
ModelResult = MatchResult.DontCare,
RequestedContentTypeResult = MatchResult.NoMatch
};
}
See https://github.com/NancyFx/Nancy/wiki/Content-Negotiation
Consider: content negotiation using WithMediaRangeModel for the route.
Enables you to define media type specific models that should be used
during negotiation. The negotiation pipeline will first attempt to
find a media type specific model to use and will fallback to the
default model, which is specified using WithModel, if it fails.

I've come up with 2 solutions:
Derived type with JsonConvert attribute on corresponding overriden props;
Using serialization within module and return a string.

Related

How to Register Custom IRouter in .Net 7 MVC Application?

I have a custom IRouter implementation and I can't figure out how to register it in a .Net 7 MVC application.
What I am trying to accomplish is this: Incoming requests have the form of https://example.com/{id} and when such a request comes in I need to hit the database to retrieve the controller and action for that {id}, do some checks on it and if everything looks right pass the request on to the default router along with the entire RequestContext. The reason behind that is that such an url is valid only for a given time and a subset of visiting users. Also the underlying action and controller must not be guessable by looking at the url.
What I came up with is a cutom IRouter implementation (which also allows me to create those Urls) but I can't seem to figure out how to register on application startup.
Is using a custom IRouter still the right approach in .Net 7? How do I register one? Or am I totally on the wrong track?
One option is to switch back from endpoint routing:
builder.Services.AddMvc(options => options.EnableEndpointRouting = false);
app.UseMvc(routeBuilder => routeBuilder.Routes.Add(new CustomRouter(routeBuilder.DefaultHandler)));
UPD
Alternative approach is to use MapDynamicControllerRoute. Something along this lines:
builder.Services.AddScoped<MyDynamic>();
// ...
app.MapDynamicControllerRoute<MyDynamic>("/{*internalid}");
public class MyDynamic: DynamicRouteValueTransformer
{
public override ValueTask<RouteValueDictionary> TransformAsync(HttpContext httpContext, RouteValueDictionary values)
{
if (values.TryGetValue("internalid", out var value) && value is string s && s == "777") // simulate some search for id
{
values["controller"] = "Home";
values["action"] = "test";
}
return new ValueTask<RouteValueDictionary>(values);
}
}
Note that since you are still using the default routing pipeline this looks like security through obscurity which in general is not a good approach and probably you should better implement appropriate security restrictions (you should not rely on the "impossibility" to guess the actual routes).

Selectively disable XML Formatter in ASP.NET Web API

In ASP.NET Web API, I need to force XML output for a single method but leave the JSON formatter enabled for others. Everything that I have seen on the topic has suggested removing the JSON formatter from the GlobalConfiguration as follows:
// remove JSON formatter
var formatters = GlobalConfiguration.Configuration.Formatters;
formatters.Remove(formatters.JsonFormatter);
This works but disables JSON output application wide. I need to be able to specify a formatter for a specific method or controller without affecting the global configuration. Is this possible or can it only be done via the GlobalConfiguration?
Microsoft introduced per-controller configuration for this specific purpose. You will need to split up your functionality into different controllers, but hopefully that will not be too much of a hassle for your specific goal (it might even be an improvement).
Basically, here is what you do:
Setup basic JSON formatting for the general case
Introduce a specific Controller for the XML methods, and give it a specific configuration:
[XMLControllerConfig]
public class XMLController: ApiController
{
[HttpGet]
public string SomeMethod(string someArgument)
{
return "abc";
}
}
...
class XMLControllerConfigAttribute: Attribute, IControllerConfiguration
{
public void Initialize(HttpControllerSettings controllerSettings,
HttpControllerDescriptor controllerDescriptor)
{
controllerSettings.Formatters.Clear();
controllerSettings.Formatters.Add(new XMLFormatter());
}
}

How to access IHttpRequest from my custom serializer in ServiceStack

We have custom serializers for our Models that protect sensitive data depending on the request path. (For instance, if the request does not start with "/admin").
Up until now, we've tried registering the IHttpRequest with the Funq container on a RequestFilter with ReuseScope.None or ReuseScope.Request and on the serializers, we're getting the IHttpRequest from the container.
We found out that if there are multiple requests pending, the container will return that last registered IHttpRequest, which will not always be the correct request object.
I know that we could try to implement this protection of sensitive data on our models applying filter attributes, but that's very cumbersome, since our model object are often embedded in other objects or collections.
Having the serializer to do it is by far the best solution for us, if it werent for this issue of not being able to get the correct HttpRequest object from within the serializers.
So, what would be the correct way to do this? Or is this a bad practice?
Here a few code samples:
So this is a private method that my serializers use to define if they're being serialized within a "admin" route or not:
private bool IsAdminRoute() {
var path = container.Resolve<IHttpRequest> ().PathInfo;
var res = path.StartsWith ("/admin");
return res;
}
Here's the use of it:
public Question QuestionSerializer(Question question)
{
if (!IsAdminRoute())
{
// do stuff like nullyfying certain properties
}
return question;
}
On my AppHost initialization I have:
void ConfigureSerializers(Funq.Container container)
{
Serializers = new CustomSerializers ();
// ...
JsConfig<Question>.OnSerializingFn = Serializers.QuestionSerializer;
// ...
}
public void HttpRequestFilter(IHttpRequest httpReq, IHttpResponse httpRes, object dto) {
Container.Register <IHttpRequest>(c => httpReq).ReusedWithin (Funq.ReuseScope.Request);
}
Note: I'm using ServiceStack v3.
I managed to make it work by registering the IHttpRequest this way:
container.Register(c => HttpContext.Current.ToRequestContext ().Get<IHttpRequest>()).ReusedWithin(Funq.ReuseScope.None);
Now, I am always getting the IHttpRequest object I am supposed to when I try to resolve them.
Also, after more thourough tests in my application I was able to detect that everything that was relying on registering with ReuseScope.Request was getting mixed up if the concurrency was high enough.
The solution was quite simple, I am relying now on the HttpContext.Current.Items collection for storing these request-specific dependencies and registering them on a request filter like this:
HttpContext.Current.Items ["Token"] = token;
container.Register<Token> (c => (Token)HttpContext.Current.Items["Token"]).ReusedWithin(Funq.ReuseScope.None);
Now it works as it was supposed to every time.

Replacing legacy system & creating new server code using ServiceStack + custom serialization

We have a legacy server code that we want to abandon and develop new one using ServiceStack. Existing clients are not written in .Net. We don't plan to use .Net on the client side at all.
Data between client and server is being exchanged using XML and JSON - at the moment JSON is only used as a return format for the response (just for some of the services available). XML format was defined when the first version of the server solution was created couple of years ago. We don't want to change it.
How do we use ServiceStack to build new RESTful webservices, that will serialize and deserialize data to a format that was designed in the past (please note, that clients will not be written in C#/.Net). We need to contol both: serialization & deserialization. Is that possible to use DTOs and still have control on how are these objects serialized / deserialized?
Adding custom logic via Request / Response Filters
See Request and response filters to see how to add custom logic before and after your service is called. It's best to add these filters via the Request / Response FilterAttributes as it allows you mark only the services that need these filters applied.
The problem with the Request Filter is it happens after the deserialization into the request DTO which is too late to add custom de-serialization logic. To get around this you can register a custom Request binder in your AppHost with:
base.RegisterRequestBinder<MyRequest>(httpReq => ... requestDto);
This gives you access to the IHttpRequest object and lets you add the custom deserialization logic yourself. The other option is to tell ServiceStack to not attempt to deserialize the request itself and instead inject the HttpRequest InputStream so you can deserialize the request yourself:
public class Hello : IRequiresRequestStream {
Stream RequestStream { get; set; }
}
Both these examples are explained on ServiceStack's Serialization and De-Serialization wiki page.
Registering your own Custom Media Type
Another option to be able to return strong-typed DTOs but change the output for certain requests can be done by adding a new custom media type as explained in the Northwind VCard Custom media type example, e.g:
public static void Register(IAppHost appHost)
{
appHost.ContentTypeFilters.Register( "text/x-vcard", SerializeToStream, DeserializeFromStream);
}
...
public static void SerializeToStream(IRequestContext requestContext, object response, Stream stream)
{
var customerDetailsResponse = response as CustomerDetailsResponse;
using (var sw = new StreamWriter(stream))
{
if (customerDetailsResponse != null)
{
WriteCustomer(sw, customerDetailsResponse.Customer);
}
var customers = response as CustomersResponse;
if (customers != null)
{
customers.Customers.ForEach(x => WriteCustomer(sw, x));
}
}
}
This is a good option if you can mount the custom XML responses under a different Content Type, e.g. application/v-xml so it doesn't conflict with the existing XML format/endpoint. Using the ContentType above your HTTP Client can call this custom implementation with ?format=v-xml or using the HTTP Header: Accept: application/v-xml.
If you want to override the built-in XML ContentType you still can but I recommend falling back to the original XmlSerializer implementation for the SerializeStream and DeserializeStream methods if it's not one of the legacy formats you have to support.
By-pass ServiceStack and execute using your own Custom IHttpHandler
Another option is to by-pass ServiceStack completely and instead process the request in your own custom IHttpRequest handler by registering it in ServiceStack's config in your AppHost:
SetConfig(new EndpointHostConfig {
RawHttpHandlers = {
httpReq => return IsLegacyMatch(httpReq) ? new LegacyXmlHandler() : null
}
});
Returning non-null (i.e. any handler) by-passes ServiceStack.

How can you programatically add mappings to UnicastBusConfig?

I would like to be able to add subscriptions to additional message types living on potentially different servers to my application at runtime. What I'm trying to achieve is a Monitoring application where I can add/remove subscriptions at runtime. Is this possible? How do I get a reference to the current UnicastBus mappings?
Here is what I'm doing so far, but I believe this will overwrite any mappings currently in existence.
MessageEndpointMappingCollection mappings = new MessageEndpointMappingCollection();
mappings.Add(new MessageEndpointMapping()
{
Messages = m.MessageType.ToString(),
Endpoint = m.QueueName
});
IComponentConfig<UnicastBusConfig> busConfig = config.Configurer.ConfigureComponent<UnicastBusConfig>(ComponentCallModelEnum.None);
busConfig.ConfigureProperty(u => u.MessageEndpointMappings, mappings);
Yves used this code in his Azure samples (to be found in NSB samples collection)
using NServiceBus.Config;
using NServiceBus.Config.ConfigurationSource;
namespace OrderService
{
class ConfigOverride : IProvideConfiguration<UnicastBusConfig>
{
public UnicastBusConfig GetConfiguration()
{
return new UnicastBusConfig
{
MessageEndpointMappings = new MessageEndpointMappingCollection
{
new MessageEndpointMapping { Messages="MyMessages", Endpoint="orderserviceinputqueue" }
}
};
}
}
}
The best way to approach this would be to implement IConfigurationSource and provide your own configuration. Then you could cherry pick what you would like to load from the config file (if anything) and what you would like to specify yourself at runtime.
I would reflect the DefaultConfigurationSource class or refer to this gist for guidance.
In a project, I am currently involved with, we are doing some content-based routing to dynamically subscribed/unsubscribed agents by keeping track of them in our own routing table.
We have wrapped the IBus in a decorator (by using Windsor's support of decorators, as described <plug>here</plug>), that sees if the message implements a special IRoutableMessage interface, that allows the decorator to route the message by explicitly specifying the destination endpoint via bus.Send(destinationEndpoint).
This was a little bit complex to get right, and I would recommend going with NServiceBus' built-in routing as far as possible. But is is possible to explicitly route messages to any endpoint.
If you are looking into monitoring, check out the NSBManager repository. This takes the opposite approach and lets the endpoints register with the manager.

Categories

Resources