I am implementing a service for posting data to an external RestAPI.
What I did as below:
Service definition:
public class ExternalOutputService : IExternalOutputService
{
private readonly HttpClient _httpClient;
public ExternalOutputService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<object> Send(object data, string baseAddress, string uri)
{
try
{
HttpResponseMessage response = await _httpClient.PostAsJsonAsync(uri, data);
response.EnsureSuccessStatusCode();
}
catch (Exception ex) {
Console.Write(ex.Message);
}
return response.Content;
}
}
Add services.AddHttpClient<IExternalOutputService, ExternalOutputService>(); in Startup
Use the injected the service and call the Send method.
public class ConfigurableOutput
{
private readonly IExternalOutputService _externalOutputService;
public ConfigurableOutput(IExternalOutputService externalOutputService)
{
_externalOutputService = externalOutputService;
}
public override async Task<object> Run(object input)
{
await _externalOutputService.Send(input.data, "URI address");
}
}
But when I run it and hit the httpclient send line, it would throw an exception with 'Cannot access a disposed object'
Anyone has idea or advice?
Hi guys, I finally find the issue.
In another DI extension class, the class has already been registered.
context.Services.AddTransient<IExternalOutputService, ExternalOutputService>();
So removed this line and only keeps
services.AddHttpClient<IExternalOutputService, ExternalOutputService>();
It is all good now.
I need to handle an incoming request which is of the form:
//ohif/study/1.1/series
Note the exta slash at the front
My controller signature is:
[Route("ohif/study/{studyUid}/series")]
[HttpGet]
public IActionResult GetStudy(string studyUid)
If I modify the incoming request to /ohif/study/1.1/series it works fine
however when I use //ohif/study/1.1/series, the route is not hit
Additionally I also tried: [Route("/ohif/study/{studyUid}/series")]
and [Route("//ohif/study/{studyUid}/series")]
Both fail. I unfortunately cannot change the incoming request as it is from an external application. Is there some trick to handle this route? I am working in .NET Core 3.0.
Update NOTE:
I have logging activated and I see that asp.net core is analyzing the route, I have the message:
No candidates found for the request path '//ohif/study/1.1/series'
for the logger Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware
What about the middleware to handle double slash?
app.Use((context, next) =>
{
if (context.Request.Path.Value.StartsWith("//"))
{
context.Request.Path = new PathString(context.Request.Path.Value.Replace("//", "/"));
}
return next();
});
Rewrite the URL at the web server-level, e.g. for IIS, you can use the URL Rewrite Module to automatically redirect //ohif/study/1.1/series to /ohif/study/1.1/series. This isn't a job for your application.
I took Ravi's answer and fleshed out a middleware. The middleware is nice because it is encapsulated, easily testable, can inject a logger, more readable, etc.
app.UseDoubleSlashHandler();
The code and tests:
public class DoubleSlashMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<DoubleSlashMiddleware> _logger;
public DoubleSlashMiddleware(RequestDelegate next, ILogger<DoubleSlashMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
_logger.LogInformation($"Invoking {nameof(DoubleSlashMiddleware)} on {context.Request.Path}");
context.Request.Path = context.Request.Path.FixDoubleSlashes();
// Call the next delegate/middleware in the pipeline.
await _next(context);
}
}
public static class DoubleSlashMiddlewareExtensions
{
public static IApplicationBuilder UseDoubleSlashHandler(
this IApplicationBuilder builder)
{
return builder.UseMiddleware<DoubleSlashMiddleware>();
}
}
[TestClass()]
public class DoubleSlashMiddlewareTests
{
private DoubleSlashMiddleware _sut;
private ILogger<DoubleSlashMiddleware> _logger;
private bool _calledNextMiddlewareInPipeline;
[TestInitialize()]
public void TestInitialize()
{
_logger = Substitute.For<ILogger<DoubleSlashMiddleware>>();
Task Next(HttpContext _)
{
_calledNextMiddlewareInPipeline = true;
return Task.CompletedTask;
}
_sut = new DoubleSlashMiddleware(Next, _logger);
}
[TestMethod()]
public async Task InvokeAsync()
{
// Arrange
_calledNextMiddlewareInPipeline = false;
// Act
await _sut.InvokeAsync(new DefaultHttpContext());
// Assert
_logger.ReceivedWithAnyArgs(1).LogInformation(null);
Assert.IsTrue(_calledNextMiddlewareInPipeline);
}
}
String method to do the replacement:
public static class RoutingHelper
{
public static PathString FixDoubleSlashes(this PathString path)
{
if (string.IsNullOrWhiteSpace(path.Value))
{
return path;
}
if (path.Value.Contains("//"))
{
return new PathString(path.Value.Replace("//", "/"));
}
return path;
}
}
[TestClass()]
public class RoutingHelperTests
{
[TestMethod()]
[DataRow(null, null)]
[DataRow("", "")]
[DataRow("/connect/token", "/connect/token")]
[DataRow("//connect/token", "/connect/token")]
[DataRow("/connect//token", "/connect/token")]
[DataRow("//connect//token", "/connect/token")]
[DataRow("/connect///token", "/connect/token")]
public void FixDoubleSlashes(string input, string expected)
{
// Arrange
var path = new PathString(input);
// Act
var actual = path.FixDoubleSlashes();
// Assert
Assert.AreEqual(expected, actual.Value);
}
}
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"
}
I use the code below to throttle my ASP.NET Web Api:
public class Throttle : ActionFilterAttribute
{
public override async Task OnActionExecutingAsync(HttpActionContext context, CancellationToken cancellationToken)
{
// ...
if (throttle)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Conflict));
}
}
}
However, I cannot return error code 429, because it's not in HttpStatusCode enum. Is there a way to return a custom error code?
I found this over here.
var response = new HttpResponseMessage
{
StatusCode = (HttpStatusCode)429,
ReasonPhrase = "Too Many Requests",
Content = new StringContent(string.Format(CultureInfo.InvariantCulture, "Rate limit reached. Reset in {0} seconds.", data.ResetSeconds))
};
response.Headers.Add("Retry-After", data.ResetSeconds.ToString(CultureInfo.InvariantCulture));
actionContext.Response = response;
Hope this helps
This is what I did based on another response on StackOverflow.
Create Class (in controller file worked for me)
public class TooManyRequests : IHttpActionResult
{
public TooManyRequests()
{
}
public TooManyRequests(string message)
{
Message = message;
}
public string Message { get; private set; }
public HttpResponseMessage Execute()
{
HttpResponseMessage response = new HttpResponseMessage((HttpStatusCode)429);
if (!string.IsNullOrEmpty(Message))
{
response.Content = new StringContent(Message); // Put the message in the response body (text/plain content).
}
return response;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(Execute());
}
}
Use in controller
public IHttpActionResult Get()
{
// with message
return new TooManyRequests("Limited to 5 request per day. Come back tomorrow.");
// without message
// return new TooManyRequests();
}
I'm getting image data (as byte[]) from DB. How to return this image in #ResponseBody ?
EDIT
I did it without #ResponseBody using HttpServletResponse as method parameter:
#RequestMapping("/photo1")
public void photo(HttpServletResponse response) throws IOException {
response.setContentType("image/jpeg");
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
IOUtils.copy(in, response.getOutputStream());
}
Using #ResponseBody with registered org.springframework.http.converter.ByteArrayHttpMessageConverter converter as #Sid said doesn't work for me :(.
#ResponseBody
#RequestMapping("/photo2")
public byte[] testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
return IOUtils.toByteArray(in);
}
if you are using Spring version of 3.1 or newer you can specify "produces" in #RequestMapping annotation. Example below works for me out of box. No need of register converter or anything else if you have web mvc enabled (#EnableWebMvc).
#ResponseBody
#RequestMapping(value = "/photo2", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
return IOUtils.toByteArray(in);
}
With Spring 4.1 and above, you can return pretty much anything (such as pictures, pdfs, documents, jars, zips, etc) quite simply without any extra dependencies. For example, the following could be a method to return a user's profile picture from MongoDB GridFS:
#RequestMapping(value = "user/avatar/{userId}", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<InputStreamResource> downloadUserAvatarImage(#PathVariable Long userId) {
GridFSDBFile gridFsFile = fileService.findUserAccountAvatarById(userId);
return ResponseEntity.ok()
.contentLength(gridFsFile.getLength())
.contentType(MediaType.parseMediaType(gridFsFile.getContentType()))
.body(new InputStreamResource(gridFsFile.getInputStream()));
}
The things to note:
ResponseEntity with InputStreamResource as a return type
ResponseEntity builder style creation
With this method you dont have to worry about autowiring in the HttpServletResponse, throwing an IOException or copying stream data around.
In addition to registering a ByteArrayHttpMessageConverter, you may want to use a ResponseEntity instead of #ResponseBody. The following code works for me :
#RequestMapping("/photo2")
public ResponseEntity<byte[]> testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_PNG);
return new ResponseEntity<byte[]>(IOUtils.toByteArray(in), headers, HttpStatus.CREATED);
}
By using Spring 3.1.x and 3.2.x, this is how you should do it:
The controller method:
#RequestMapping("/photo2")
public #ResponseBody byte[] testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
return IOUtils.toByteArray(in);
}
And the mvc annotation in servlet-context.xml file:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>image/jpeg</value>
<value>image/png</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
In addition to a couple of answers here a few pointers (Spring 4.1).
Incase you don't have any messageconverters configured in your WebMvcConfig, having ResponseEntity inside your #ResponseBody works well.
If you do, i.e. you have a MappingJackson2HttpMessageConverter configured (like me) using the ResponseEntity returns a org.springframework.http.converter.HttpMessageNotWritableException.
The only working solution in this case is to wrap a byte[] in the #ResponseBody as follows:
#RequestMapping(value = "/get/image/{id}", method=RequestMethod.GET, produces = MediaType.IMAGE_PNG_VALUE)
public #ResponseBody byte[] showImageOnId(#PathVariable("id") String id) {
byte[] b = whatEverMethodUsedToObtainBytes(id);
return b;
}
In this case do rememeber to configure the messageconverters properly (and add a ByteArrayHttpMessageConverer) in your WebMvcConfig, like so:
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(mappingJackson2HttpMessageConverter());
converters.add(byteArrayHttpMessageConverter());
}
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper);
return converter;
}
#Bean
public ByteArrayHttpMessageConverter byteArrayHttpMessageConverter() {
ByteArrayHttpMessageConverter arrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
arrayHttpMessageConverter.setSupportedMediaTypes(getSupportedMediaTypes());
return arrayHttpMessageConverter;
}
private List<MediaType> getSupportedMediaTypes() {
List<MediaType> list = new ArrayList<MediaType>();
list.add(MediaType.IMAGE_JPEG);
list.add(MediaType.IMAGE_PNG);
list.add(MediaType.APPLICATION_OCTET_STREAM);
return list;
}
I prefere this one:
private ResourceLoader resourceLoader = new DefaultResourceLoader();
#ResponseBody
#RequestMapping(value = "/{id}", produces = "image/bmp")
public Resource texture(#PathVariable("id") String id) {
return resourceLoader.getResource("classpath:images/" + id + ".bmp");
}
Change the media type to what ever image format you have.
In your application context declare a AnnotationMethodHandlerAdapter and registerByteArrayHttpMessageConverter:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<util:list>
<bean id="byteArrayMessageConverter" class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
</util:list>
</property>
</bean>
also in the handler method set appropriate content type for your response.
#RequestMapping(value = "/get-image",method = RequestMethod.GET)
public ResponseEntity<byte[]> getImage() throws IOException {
RandomAccessFile f = new RandomAccessFile("/home/vivex/apache-tomcat-7.0.59/tmpFiles/1.jpg", "r");
byte[] b = new byte[(int)f.length()];
f.readFully(b);
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_PNG);
return new ResponseEntity<byte[]>(b, headers, HttpStatus.CREATED);
}
Worked For Me.
You should specify the media type in the response. I'm using a #GetMapping annotation with produces = MediaType.IMAGE_JPEG_VALUE. #RequestMapping will work the same.
#GetMapping(value="/current/chart",produces = MediaType.IMAGE_JPEG_VALUE)
#ResponseBody
public byte[] getChart() {
return ...;
}
Without a media type, it is hard to guess what is actually returned (includes anybody who reads the code, browser and of course Spring itself). A byte[] is just not specific. The only way to determine the media type from a byte[] is sniffing and guessing around.
Providing a media type is just best practice
It's work for me in Spring 4.
#RequestMapping(value = "/image/{id}", method = RequestMethod.GET)
public void findImage(#PathVariable("id") String id, HttpServletResponse resp){
final Foto anafoto = <find object>
resp.reset();
resp.setContentType(MediaType.IMAGE_JPEG_VALUE);
resp.setContentLength(anafoto.getImage().length);
final BufferedInputStream in = new BufferedInputStream(new ByteArrayInputStream(anafoto.getImageInBytes()));
try {
FileCopyUtils.copy(in, resp.getOutputStream());
resp.flushBuffer();
} catch (final IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Non of the answers worked for me, so I've managed to do it like that:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("your content type here"));
headers.set("Content-Disposition", "attachment; filename=fileName.jpg");
headers.setContentLength(fileContent.length);
return new ResponseEntity<>(fileContent, headers, HttpStatus.OK);
Setting Content-Disposition header I was able to download the file with the #ResponseBody annotation on my method.
This is how I do it with Spring Boot and Guava:
#RequestMapping(value = "/getimage", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public void getImage( HttpServletResponse response ) throws IOException
{
ByteStreams.copy( getClass().getResourceAsStream( "/preview-image.jpg" ), response.getOutputStream() );
}
In spring 4 it's very easy you don't need to make any changes in beans. Only mark your return type to #ResponseBody.
Example:-
#RequestMapping(value = "/image/{id}")
public #ResponseBody
byte[] showImage(#PathVariable Integer id) {
byte[] b;
/* Do your logic and return
*/
return b;
}
I think you maybe need a service to store file upload and get that file.
Check more detail from here
1) Create a Storage Sevice
#Service
public class StorageService {
Logger log = LoggerFactory.getLogger(this.getClass().getName());
private final Path rootLocation = Paths.get("upload-dir");
public void store(MultipartFile file) {
try {
Files.copy(file.getInputStream(), this.rootLocation.resolve(file.getOriginalFilename()));
} catch (Exception e) {
throw new RuntimeException("FAIL!");
}
}
public Resource loadFile(String filename) {
try {
Path file = rootLocation.resolve(filename);
Resource resource = new UrlResource(file.toUri());
if (resource.exists() || resource.isReadable()) {
return resource;
} else {
throw new RuntimeException("FAIL!");
}
} catch (MalformedURLException e) {
throw new RuntimeException("FAIL!");
}
}
public void deleteAll() {
FileSystemUtils.deleteRecursively(rootLocation.toFile());
}
public void init() {
try {
Files.createDirectory(rootLocation);
} catch (IOException e) {
throw new RuntimeException("Could not initialize storage!");
}
}
}
2) Create Rest Controller to upload and get file
#Controller
public class UploadController {
#Autowired
StorageService storageService;
List<String> files = new ArrayList<String>();
#PostMapping("/post")
public ResponseEntity<String> handleFileUpload(#RequestParam("file") MultipartFile file) {
String message = "";
try {
storageService.store(file);
files.add(file.getOriginalFilename());
message = "You successfully uploaded " + file.getOriginalFilename() + "!";
return ResponseEntity.status(HttpStatus.OK).body(message);
} catch (Exception e) {
message = "FAIL to upload " + file.getOriginalFilename() + "!";
return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(message);
}
}
#GetMapping("/getallfiles")
public ResponseEntity<List<String>> getListFiles(Model model) {
List<String> fileNames = files
.stream().map(fileName -> MvcUriComponentsBuilder
.fromMethodName(UploadController.class, "getFile", fileName).build().toString())
.collect(Collectors.toList());
return ResponseEntity.ok().body(fileNames);
}
#GetMapping("/files/{filename:.+}")
#ResponseBody
public ResponseEntity<Resource> getFile(#PathVariable String filename) {
Resource file = storageService.loadFile(filename);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"")
.body(file);
}
}
When using produces with MediaType.IMAGE_JPEG_VALUE, make sure that you are returning byte[], but not Byte[]. Very strange, but spring cannot convert it and raises an exception: no converter found.