How to send and retrieve custom header information for REST WCF Service - rest

I am struggling to set-up infrastructure in my solution to send and retrieve the custom header for REST WCF Service. Basically, we need this to send UserID, password, token value from client to service and if provided values are valid then operation will continue to execute otherwise throw exception.
We already have few classes inherited from interfaces like IDispatchMessageInspector, IClientMessageInspector, IEndPointBehaviour, MessageHeader, etc., This is working fine for WCF with soap request. I tried to use these classes for my new REST WCF Service, but was not working as MessageHeader derived class supports only Soap.
I also tried using WebOperationContext, but no luck :(
Please provide a solution along with sample project to solve this problem.
Thank you so much!

Seems in your case it might be easier to interogate the ASPNET pipeline
if you add the following to your WCF service to allow it to hookup into the ASPNET pipeline
[AspNetCompatibilityRequirements(RequirementsMode =
AspNetCompatibilityRequirementsMode.Allowed)]
Then you can simply now use the HttpContext object and just get the headers as you would from a normal aspnet application, e.g
System.Web.HttpContext.Current.Request.Headers["CustomHeader"]

If you want to add http header in wcf rest service , you should use HttpRequestMessageProperty, it has a Headers property , you could set http Header through its Headers property
using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))
{
HttpRequestMessageProperty property;
// if OutgoingMessageProperties already has HttpRequestMessageProperty, use the existing one , or initialize a new one and
// set OutgoingMessageProperties's HttpRequestMessageProperty.Name key's value to the initialized HttpRequestMessageProperty so that the HttpRequestMessageProperty will work
if (OperationContext.Current.OutgoingMessageProperties.ContainsKey(HttpRequestMessageProperty.Name)){
property = OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
}
else
{
property = new HttpRequestMessageProperty();
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = property;
}
// add headers to HttpRequestMessageProperty, it will become the http header of the reuqest
property.Headers.Add(System.Net.HttpRequestHeader.Authorization, "myAuthorization");
string re = client.HelloWorld();
}
About getting the Header , just use WebOperationContext.Current.Headers.
WebOperationContext.Current.IncomingRequest.Headers["MyCustomHttpHeader"]
Please refer to http://kenneththorman.blogspot.com/2011/02/wcf-rest-client-using-custom-http.html

Related

ServiceStack JsonServiceClient: SendAsync uses wrong path, ignores Route attribute?

I am using JsonServiceClient in a Xamarin app, like this:
JsonServiceClient client = new JsonServiceClient("https://my.domain.com/");
SetHeaders(client);
var request = ...; // this is IRequest<T>
var result = await client.SendAsync(request); // <-- FAILS, can't find service
My backend returns an answer, saying that there is no service at that endpoint, which is true, the path that was actually sent over the wire is incorrect.
The request is defined in a lib, like so:
[Route("/mybasepath/endpoint", "POST")]
public class Login : IReturn<LoginResponse>
{
}
The problem is the path that is used in the call, which is wrong and does not follow the Route attribute:
https://my.domain.com/json/reply/Login
Here, ServiceStack client uses the default /json/reply path, even though I have the Route attribute defined in the DTO.
If I change the method used on the client instance, and instead use PostAsync, the path is ten correct and the call work as expected:
JsonServiceClient client = new JsonServiceClient("https://my.domain.com/");
SetHeaders(client);
var request = ...; // this is IRequest<T>
var result = await client.PostAsync(request); // <-- WORKS!
I don't have a minimal project right now that can be immediately tested, maybe it is something easy I have missed?
(Using ServiceStack.Client v 5.10.4 on VS 2019 16.9)
If you want to use ServiceStack's generic Send* APIs the Service Clients needs to explicitly infer the Verb to use by annotating the Request DTO with an HTTP Verb Interface Marker, not necessary for AutoQuery or AutoQuery CRUD APIs which is inferred from their base classes.
Otherwise Send* APIs are designed to fallback to use ServiceStack's pre-defined Routes.

Controller return type and httpStatus best practice and production/consumption on method in REST WS

I commence in REST and I have some questions:
What type must the controller return? Typically, I'm asking if my Rest #Controller must return Item object as it is or encapsulate it in ResponseEntity in order to specify http-status-code.
What http status code to use in a GET method on a particular item ("/items/2") if the given item does not exists: HttpMediaStatus.OK(200) and null return or HttpStatus.NO_CONTENT(204) and null return ?
Second part: I saw it was possible to specify #Produces and #Consumes on WS method but what the use of that? My application and my methods work so, why specify MediaType.APPLICATION_JSON_VALUE? Doesn't Spring/SpringBoot automatically convert Item or ResponseEntity into json?
Context: using Spring Boot, hibernate, REST webservice.
Thank you.
Many questions in one, I'll provide short answers with a bunch of link to relevant articles and the reference documentation.
What type must the controller return?
Depends on your annotation and the RESTful-ness of your service. There are three annotations you can use for controllers: #Controller, #RestController and #RepositoryRestController.
Controller is the base annotation to mark your class as a controller. The return type of the controller endpoint methods can be many things, I invite you to read this dedicated post to get a grasp of it.
When developing a pure-REST service, you will focus on using RestController and RepositoryRestController.
RestControlleris Controller + ResponseBody. It binds the return value of the endpoint method to the web response body:
#RestController
public ItemController {
#RequestMapping("/items/{id}")
public Item getItem(#PathVariable("id") String id) {
Item item = ...
return item;
}
}
With this, when you hit http:/.../api/items/foo, Spring does its magic, automatically converting the item to a ResponseEntity with a relevant 40X status code and some default HTTP headers.
At some point, you will need more control over the status code and headers, while still benefiting from Spring Data REST's settings. That's when you will use RepositoryRestController with a ResponseEntity<Item> as return type, see the example the Spring Data REST reference.
What http status code to use in a GET method on a particular item if the given item does not exists?
Bluntly said: use HttpStatus.NOT_FOUND. You're looking for a resource that does not exist, there's something wrong.
That being said, it is completely up to you to decide how to handle missing resources in your project. If your workflow justifies it, a missing resource could be something completely acceptable that indeed returns a 20X response, though you may expect users of your API to get confused if you haven't warned them or provided some documentation (we are creatures of habits and conventions). But I'd still start with a 404 status code.
(...) #Produces and #Consumes on WS method but what the use of that? My application and my methods work so, why specify MediaType.APPLICATION_JSON_VALUE? Doesn't Spring/SpringBoot automatically convert Item or ResponseEntity into json?
#Consumes and #Produces are respectively matched against content-type and accept headers from the request. It's a mean of restricting the input accepted and the output provided by your endpoint method.
Since we're talking about a REST service, communications between clients of the API and the service are expected to be JSON-formatted. Thanks to Spring HATEOAS, the answer are actually formatted with the application/hal+json content-type.
In that scenario, you can indeed not bother with those two annotations. You will need them if you develop a service that accepts different content-types (application/text, application/json, application/xml...) and provides, for instance, HTML views to users of your website and JSON or XML response to automated clients of your service.
For real life examples:
Facebook provides the Graph API for applications to read to/write from its graph, while users happily (?) surf on web pages
Google does the same with the Google Maps API

Spring WS remove flexible URL, Restricting WSDL URL and service URL

I'm trying to make a Spring Boot Soap WebService application, and was following the Get Started (https://spring.io/guides/gs/producing-web-service/) example to learn how to do this.
I've created what I want, but I have two URL problems with this setup and I could not find what configuration should I change to fix this :
WSDL URL basic is localhost:8080/ws/countries.wsdl but anything like localhost:8080/ws/whatever/countries.wsdl is correct
service URL for SoapUI request is localhost:8080/ws but anything like localhost:8080/ws/whatever is correct
I know that this is a feature for Spring WS, but I want a fixed URL (without 'whatever' in both cases) and could not find what to change for this
There is no straight forward way to restrict the way you want.
SOAP service is not URL based.
SOAP message body describe the endpoint.
The thing you wanted is possible following way.
Changing URL mapping in ServletRegistrationBean to restrict URL access
Existing /ws/* mapping is the reason why all the /ws/whatever url successfully responded.
Change as new ServletRegistrationBean(servlet, "/ws");
Effect will be you can not request other than /ws URL
Now the problem is, you can not get WSDL by this mapping.
Solution to get WSDL
The DefaultWsdl11Definition is actually generating WSDL from XSD on every request.
Save countries.wsdl to resource folder as static WSDL file.
Remove DefaultWsdl11Definition bean.
Create a new SimpleWsdl11Definition bean as like
#Bean(name = "countries")
public SimpleWsdl11Definition orders() {
SimpleWsdl11Definition wsdl11Definition = new SimpleWsdl11Definition();
wsdl11Definition.setWsdl(new ClassPathResource("countries.wsdl"));
return wsdl11Definition;
}
Now add another static URL mapping in ServletRegistrationBean. As it will be finally look like new ServletRegistrationBean(servlet, "/ws", "/ws/countries.wsdl");
This practice is good for development phase as you can publish easily the changed definition. But it is recommended to use static-wsdl for production environment. Details ** here
Just change
return new ServletRegistrationBean(servlet, "/ws/*");
for example to
return new ServletRegistrationBean(servlet, new String[]{
"/ws/v1/countries.wsdl",
"/ws/v2/countries.wsdl"
});

admin-on-rest Rest Client not mapping record with GET_ONE secondary API call

I have a GET_LIST that calls an API, displaying me an object of data. But when I create the Edit component which is under the same resource component, I am calling a second API call that shows additional data for that GET_ONE item.
My issue, is that although I can configure a GET_ONE call inside my rest client, I am unable to get the Edit component do use the GET_ONE API call response object. It is still using the GET_ALL API call response object.
Here's some sample code. Note, that my GET_ONE api call doesn't have {resource} because the URL link doesn't have that word.
const API_URL = 'https://api.link/biglist';
const 2API_URL2 = 'https://api.link/address=123456';
switch (type) {
case GET_LIST: {
url = `${API_URL}/${resource}`;
break;
}
case
GET_ONE:
url = `${2API_URL2}/${params.id}`;
break;
Inside the Edit component, I have an ImageField. By console logging the record inside the ImageField component, I can see that the record response object is using the GET_ALL API response, instead of GET_ONE API response.
I have made sure that when I access the Edit component of a specific item inside the GET_LIST, my Edit component does indeed make an API call to my secondary API request, and it does return me the response object of the secondary request. I just can't somehow map the secondary API response with record inside ImageField.
So my question is, how do I get the GET_ONE response inside my Edit component (which is under the same resource component) to use my second API response and map it with record?
I solved my problem by adding a fetch API call inside my resource components, and created custom field components to use my secondary API calls instead of using record. I feel there's a much better solution, but this'll do.

Performing Explicit Route Mapping based upon Web Api v2 Attributes

I'm upgrading a custom solution where I can dynamically register and unregister Web Api controllers to use the new attribute routing mechanism. However, it seems to recent update to RTM break my solution.
My solution exposes a couple of Web Api controllers for administration purposes. These are registered using the new HttpConfigurationExtensions.MapHttpAttributeRoutes method call.
The solution also allows Web Api controllers to be hosted in third-party assemblies and registered dynamically. At this stage, calling HttpConfigurationExtensions.MapHttAttributeRoutes a second time once the third-party controller is loaded would raise an exception. Therefore, my solution uses reflection to inspect the RoutePrefix and Route attributes and register corresponding routes on the HttpConfiguration object.
Unfortunately, calling the Web Api results in the following error:
"No HTTP resource was found that matches the request URI".
Here is a simple controller that I want to use:
[RoutePrefix("api/ze")]
public sealed class ZeController : ApiController
{
[HttpGet]
[Route("one")]
public string GetOne()
{
return "One";
}
[HttpGet]
[Route("two")]
public string GetTwo()
{
return "Two";
}
[HttpPost]
[Route("one")]
public string SetOne(string value)
{
return String.Empty;
}
}
Here is the first solution I tried:
configuration.Routes.MapHttpRoute("ZeApi", "api/ze/{action}");
Here is the second solution I tried:
var type = typeof(ZeController);
var routeMembers = type.GetMethods().Where(m => m.IsPublic);
foreach (MethodInfo method in routeMembers)
{
var routeAttribute = method.GetCustomAttributes(false).OfType<RouteAttribute>().FirstOrDefault();
if (routeAttribute != null)
{
string controllerName = type.Name.Substring(0, type.Name.LastIndexOf("Controller"));
string routeTemplate = string.Join("/", "api/Ze", routeAttribute.Template);
configuration.Routes.MapHttpRoute(method.Name, routeTemplate);
}
}
I also have tried a third solution, whereby I create custom classes that implement IHttpRoute and trying to register them with the configuration to no avail.
Is it possible to use legacy-style route mapping based upon the information contained in the new routing attributes ?
Update
I have installed my controller in a Web Application in order to troubleshoot the routing selection process with the Web Api Route Debugger. Here is the result of the screenshot:
As you can see, the correct action seems to be selected, but I still get a 404 error.
Update2
After further analysis, and per Kiran Challa's comment below, it seems that the design of Web Api prevents mixing attribute routing and conventional routing, and that what I want to do is not possible using this approach.
I have created a custom attribute [RouteEx] that serves the same purpose of the Web Api [Route] attribute, and now my code works perfectly.
I guess, since this is not possible using the conventional attribute routing, none of the answers on this question could legitimately be consisered valid. So I'm not nominating an answer just yet.
You shouldn't be required to use reflection and inspect the attribute-routing based attributes yourself. Attribute routing uses existing Web API features to get list of controllers to scan through.
Question: Before the switch to attribute routing, how were you loading these assemblies having the
controllers?
If you were doing this by IAssembliesResolver service, then this solution should work even with attribute routing and you should not be needing to do anything extra.
Regarding your Update: are you calling MapHttpAttributeRoutes?